目錄
Yao框架
每次看spring的源碼都是難以看進去, 但是平時都會通過各種各樣的博客文章中大致理解了spring的基本原理。 同時我也萌生了自己實現一個類似框架的想法,於是便有了這個項目。雖然是重複造輪子,但是寫這個項目的過程中我對於spring的實現也有了更深的瞭解吧。
雖然我只是無人氣的小博主, 但是我還是在我的在這裏推下我的框架,因爲夢想還是要有的,萬一這個框架哪天火了呢!
**本項目使用的是java11版本的基礎上開發,不能保證在小於java11的版本上能正常運行 **
想學習spring的,又難以看進去源碼的, 可以看看我的這個項目,
歡迎star和留言。有疑問請留言, 我很樂意與你們一起探討技術問題。 這個項目我會堅持更新的,希望大家能給與我支持。
項目地址
https://github.com/blanexie/Yao
使用示例
可以參照我的項目源碼中的example模塊來使用
說明除了下面示例的方式, 還可以使用FactoryBean接口和@Bean註解方法的方式將bean放入容器中
導入maven包
<!-- 依賴注入的核心包, 提供基本的依賴注入功能 -->
<dependency>
<groupId>xyz.xiezc</groupId>
<artifactId>xioc</artifactId>
<version>2.2</version>
</dependency>
<!-- 整合mybatis的包 -->
<dependency>
<groupId>xyz.xiezc</groupId>
<artifactId>xorm</artifactId>
<version>2.2</version>
</dependency>
<!-- 數據源鏈接池 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<!-- 整合netty的包, 提供了基本的http能力和websocket支持 -->
<dependency>
<groupId>xyz.xiezc</groupId>
<artifactId>xweb</artifactId>
<version>2.2</version>
</dependency>
定義一個啓動類
package xyz.xiezc.example;
import xyz.xiezc.ioc.starter.Xioc;
import xyz.xiezc.ioc.starter.annotation.Configuration;
import xyz.xiezc.ioc.starter.annotation.Cron;
import xyz.xiezc.ioc.starter.annotation.EnableCron;
import xyz.xiezc.ioc.starter.orm.annotation.MapperScan;
import java.time.LocalTime;
@MapperScan({"xyz.xiezc.example.web.mapper"}) //掃描mapper包
@Configuration
@EnableCron //啓用定時任務
public class Application {
/**
* 每3秒執行一次。<br>
* 表達式每部分的意思如下:
* 秒 分 時 日 月 星期
*/
@Cron("*/3 * * * * *")
public void cronTest() {
System.out.println("定時任務執行: " + LocalTime.now());
}
public static void main(String[] args) {
Xioc.run(Application.class);
}
}
定義一個Controller放入容器中
package xyz.xiezc.example.web.controller;
import cn.hutool.json.JSONUtil;
import xyz.xiezc.example.web.common.TestAopspect;
import xyz.xiezc.example.web.entity.Album;
import xyz.xiezc.example.web.mapper.AlbumMapper;
import xyz.xiezc.example.web.service.TestService;
import xyz.xiezc.ioc.starter.annotation.Aop;
import xyz.xiezc.ioc.starter.annotation.Inject;
import xyz.xiezc.ioc.starter.orm.common.example.Example;
import xyz.xiezc.ioc.starter.starter.web.annotation.Controller;
import xyz.xiezc.ioc.starter.starter.web.annotation.GetMapping;
import xyz.xiezc.ioc.starter.starter.web.entity.WebContext;
import java.util.List;
import java.util.Map;
@Aop(TestAopspect.class)
@Controller("/")
public class TestController {
@Inject
AlbumMapper albumMapper;
/**
* 支持list注入, 會將符合TestService類型的bean都注入到這個list中
*/
@Inject
TestService[] testServices;
@GetMapping("/get.json")
public String get() {
return testServices.length + "";
}
@GetMapping("/test.json")
public String get(String param) {
WebContext webContext = WebContext.get();
//
Example build = Example.of(Album.class)
.andEqualTo(Album::getId, 3537) //支持類似mybatis-plus的lambda的使用方式
.build();
List<Album> albums = albumMapper.selectByExample(build);
//獲取session信息
Map<String, Object> session = webContext.getSession();
session.put("param", param);
return JSONUtil.toJsonStr(albums);
}
}
定義一個實體類
import lombok.Data;
import Column;
import Id;
import Table;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@Table("t_album")
public class Album implements Serializable {
@Id
Integer id;
@Column
String title;
@Column
String publishTime;
@Column
String type;
@Column
LocalDateTime createTime;
@Column
Integer coverId;
@Column
Integer see;
}
定義一個Mapper接口
import BaseMapper;
/**
* BaseMapper 中內置了很多的方法
*/
public interface AlbumMapper extends BaseMapper<Album> {
}
定義一個websocket的controller
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import WebSockerController;
import WebSocketFrameHandler;
@WebSockerController("/websocket")
public class WebSocketHandler implements WebSocketFrameHandler {
@Override
public WebSocketFrame handleTextWebSocketFrame(TextWebSocketFrame textWebSocketFrame) {
String text = textWebSocketFrame.text();
TextWebSocketFrame textWebSocketFrame1 = new TextWebSocketFrame("resp:" + text);
return textWebSocketFrame1;
}
@Override
public WebSocketFrame handleBinaryWebSocketFrame(BinaryWebSocketFrame binaryWebSocketFrame) {
return null;
}
}
配置文件
### web服務的端口
xweb.server.port=8443
### 靜態文件的目錄
xweb.static.path=/static
## 是否啓用ssl
xweb.server.ssl.enable = false
## 證書鏈文件, .crt後綴的文件。啓用ssl後這個文件必須存在
xweb.server.ssl.certChainFile.Path=
## 私鑰文件, .pem後綴的文件。啓用ssl後這個文件必須存在
xweb.server.ssl.privatekeyFile.Path=
#----------------------------------------------------------------------------------------------------------------
## 基本配置信息
# JDBC URL,根據不同的數據庫,使用相應的JDBC連接字符串
url =jdbc:mysql://localhost:8306/daily
# 用戶名,此處也可以使用 user 代替
username = root
# 密碼,此處也可以使用 pass 代替
password = 123456
# JDBC驅動名,可選(Hutool會自動識別)
# driver = com.mysql.jdbc.Driver
# 是否在日誌中顯示執行的SQL
showSql = true
# 是否格式化顯示的SQL
formatSql = true
#----------------------------------------------------------------------------------------------------------------
## 連接池配置項
## ---------------------------------------------------- Druid
# 初始化時建立物理連接的個數。初始化發生在顯示調用init方法,或者第一次getConnection時
initialSize = 0
# 最大連接池數量
maxActive = 8
# 最小連接池數量
minIdle = 0
# 獲取連接時最大等待時間,單位毫秒。配置了maxWait之後, 缺省啓用公平鎖,併發效率會有所下降, 如果需要可以通過配置useUnfairLock屬性爲true使用非公平鎖。
maxWait = 0
# 是否緩存preparedStatement,也就是PSCache。 PSCache對支持遊標的數據庫性能提升巨大,比如說oracle。 在mysql5.5以下的版本中沒有PSCache功能,建議關閉掉。作者在5.5版本中使用PSCache,通過監控界面發現PSCache有緩存命中率記錄, 該應該是支持PSCache。
poolPreparedStatements = false
# 要啓用PSCache,必須配置大於0,當大於0時, poolPreparedStatements自動觸發修改爲true。 在Druid中,不會存在Oracle下PSCache佔用內存過多的問題, 可以把這個數值配置大一些,比如說100
maxOpenPreparedStatements = -1
# 用來檢測連接是否有效的sql,要求是一個查詢語句。 如果validationQuery爲null,testOnBorrow、testOnReturn、 testWhileIdle都不會其作用。
validationQuery = SELECT 1
# 申請連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能。
testOnBorrow = true
# 歸還連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能
testOnReturn = false
# 建議配置爲true,不影響性能,並且保證安全性。 申請連接的時候檢測,如果空閒時間大於 timeBetweenEvictionRunsMillis,執行validationQuery檢測連接是否有效。
testWhileIdle = false
# 有兩個含義: 1) Destroy線程會檢測連接的間隔時間 2) testWhileIdle的判斷依據,詳細看testWhileIdle屬性的說明
timeBetweenEvictionRunsMillis = 60000
# 物理連接初始化的時候執行的sql
connectionInitSqls = SELECT 1
# 屬性類型是字符串,通過別名的方式配置擴展插件, 常用的插件有: 監控統計用的filter:stat 日誌用的filter:log4j 防禦sql注入的filter:wall
filters = stat,wall
# 類型是List<com.alibaba.druid.filter.Filter>, 如果同時配置了filters和proxyFilters, 是組合關係,並非替換關係
proxyFilters =
Xioc 小型的依賴注入框架,
目前支持的註解詳解
- @Component 被註解的類放入容器成爲bean,可以註解類和方法。
- @Configuration 被註解的類放入容器成爲bean, 只能註解類。
- @Inject 註解字段,是的字段從容器中注入bean。
- @Bean 方法註解, 只能配合@Configuration註解通過方法來生產bean
- @BeanScan 類註解, 只能配合@Configuration一起使用,用來標註框架掃描的包路徑
- @Init 方法註解, 被註解的方法在所在bean初始化完成後調用, 方法必須無參
- @Value 字段註解。 將配置中的內容注入到字段中, 類型轉換參照Hutool的類型轉換工具。
- @EventListener 類註解, 被註解的方類必須有無參構造方法,且實現ApplicationListener接口, 註解中必須設置處理的時間名稱。
- @Cron 方法註解, 被註解的方法會作爲定時任務執行
- @EnableCron 在啓動類上的註解, 只有這個註解存在, @Cron 註解纔會起作用
目前支持功能列表如下
- 所有的bean都是單例模式
- 基本的掃描注入類,
- 支持方法注入bean
- 支持注入配置,配置文件使用Hutool的setting
- 支持類似springboot的starter一樣的導入包引入對應功能的方法
需要導入的starter包必須在xyz.xiezc.ioc.starter包下放上一個@Configuration註解的類。 並且可以配合@BeanScan一起使用
- 支持bean初始化後調用的init方法
- 支持基本的事件處理器
- 支持配置注入
缺陷待完善
- AOP功能需要在被切的類上面增加註解, 但是AOP的意義就在於不用修改被切類從而達到切面的目的
xweb 整合netty支持web的框架
支持的註解
- @Controller 參考SpringMvc
- @GetMapping 參考SpringMvc
- @PostMapping 參考SpringMvc
- @RequestBody 參考SpringMvc
- @WebSockerController 被註解的類必須實現WebSocketFrameHandler接口, 從而來實現websocket. 這個註解的value值就是websocket的連接的url
說明
- 默認靜態文件目錄是
classPath:static/
,在這個目錄下面的文件會作爲靜態文件。可以在配置文件中指定xweb.static.path
作爲靜態文件。 - 請求路徑只有在controller中找不到後纔會進入靜態文件目錄下查找。
- GET和POST請求只返回application/json格式的內容,不返回其他類型。這樣已經夠用了, 現在幾乎都是前後端分離的項目。
待改進
- 只支持GET和Post請求
- Post請求只默認提供支持的ContentType類型只有 “application/json” ,“multipart/form-data” ,
"application/x-www-form-urlencoded"和從url中獲取參數的Default幾種類型,但是提供了擴展接口。
只要實現HttpMessageConverter接口,並將實現類放入容器中,就可以了。
配置文件
參考上面使用示例中的配置文件
XORM 整合mybatis方便查詢的項目
支持的註解
- @Column 標註在實體類中, 可以通過value 指定數據庫字段的名稱。
- @Id 標註在實體類中, 可以通過value 指定數據庫字段的名稱。
- @MapperScan 一定要標註在啓動類中, value指定項目中的mybatis的mapper接口的地址
- @Table 標註在實體類上, 標識這個類對應一個數據庫表
支持的配置文件的配置
- mybatis.configLocation 指定自定義的mybatis的配置文件地址
- mybatis.mapperLocations 指定自定義的mapper.xml文件的路徑
- mybatis.typeAliasesPackage 參考mybatis-spring
- mybatis.typeHandlersPackage 參考mybatis-spring
數據源的配置
本框架中數據源的使用是完全使用hutool中的數據源的方式
基本配置
## 基本配置信息
# JDBC URL,根據不同的數據庫,使用相應的JDBC連接字符串
url = jdbc:mysql://127.0.0.1:8306/daily
# 用戶名,此處也可以使用 user 代替
username = root
# 密碼,此處也可以使用 pass 代替
password = 123456
# JDBC驅動名,可選(Hutool會自動識別)
driver = com.mysql.cj.jdbc.Driver
支持如下的數據源
- HikariCP
- Druid
- Tomcat
- Dbcp
- C3p0
本框架句自動識別數據源以及自動注入配置文件中的連接池配置(包括數據庫連接配置),如果引入了多種數據源的jar包。 本框架會按照上面的順序來檢測引入數據源包,檢測到則會自動構建數據源。如果沒有檢測到數據源包, 就會使用內置的簡單數據源(性能問題, 不推薦線上使用)
以druid爲例, 我們看下如何使用
- 先引入druid的包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
- 再在配置文件中增加如下
#----------------------------------------------------------------------------------------------------------------
## 基本配置信息
# JDBC URL,根據不同的數據庫,使用相應的JDBC連接字符串
url = jdbc:mysql://<host>:<port>/<database_name>
# 用戶名,此處也可以使用 user 代替
username = 用戶名
# 密碼,此處也可以使用 pass 代替
password = 密碼
# JDBC驅動名,可選(Hutool會自動識別)
driver = com.mysql.jdbc.Driver
# 是否在日誌中顯示執行的SQL
showSql = true
# 是否格式化顯示的SQL
formatSql = true
#----------------------------------------------------------------------------------------------------------------
## 連接池配置項
## ---------------------------------------------------- Druid
# 初始化時建立物理連接的個數。初始化發生在顯示調用init方法,或者第一次getConnection時
initialSize = 0
# 最大連接池數量
maxActive = 8
# 最小連接池數量
minIdle = 0
# 獲取連接時最大等待時間,單位毫秒。配置了maxWait之後, 缺省啓用公平鎖,併發效率會有所下降, 如果需要可以通過配置useUnfairLock屬性爲true使用非公平鎖。
maxWait = 0
# 是否緩存preparedStatement,也就是PSCache。 PSCache對支持遊標的數據庫性能提升巨大,比如說oracle。 在mysql5.5以下的版本中沒有PSCache功能,建議關閉掉。作者在5.5版本中使用PSCache,通過監控界面發現PSCache有緩存命中率記錄, 該應該是支持PSCache。
poolPreparedStatements = false
# 要啓用PSCache,必須配置大於0,當大於0時, poolPreparedStatements自動觸發修改爲true。 在Druid中,不會存在Oracle下PSCache佔用內存過多的問題, 可以把這個數值配置大一些,比如說100
maxOpenPreparedStatements = -1
# 用來檢測連接是否有效的sql,要求是一個查詢語句。 如果validationQuery爲null,testOnBorrow、testOnReturn、 testWhileIdle都不會其作用。
validationQuery = SELECT 1
# 申請連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能。
testOnBorrow = true
# 歸還連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能
testOnReturn = false
# 建議配置爲true,不影響性能,並且保證安全性。 申請連接的時候檢測,如果空閒時間大於 timeBetweenEvictionRunsMillis,執行validationQuery檢測連接是否有效。
testWhileIdle = false
# 有兩個含義: 1) Destroy線程會檢測連接的間隔時間 2) testWhileIdle的判斷依據,詳細看testWhileIdle屬性的說明
timeBetweenEvictionRunsMillis = 60000
# 物理連接初始化的時候執行的sql
connectionInitSqls = SELECT 1
# 屬性類型是字符串,通過別名的方式配置擴展插件, 常用的插件有: 監控統計用的filter:stat 日誌用的filter:log4j 防禦sql注入的filter:wall
filters = stat
# 類型是List<com.alibaba.druid.filter.Filter>, 如果同時配置了filters和proxyFilters, 是組合關係,並非替換關係
proxyFilters =
- 開始你的表演, 寫你最熟悉的mapper代碼
說明
- 目前沒有實現事務的功能, 還不支持事務。
- Mapper接口必須實現xyz.xiezc.ioc.starter.orm.common.BaseMapper接口,BaseMapper接口提供了基本的方便的查詢的方法,十分方便。
- 可以不用謝mapper.xml文件。 只有到BaseMapper接口不滿足需求的時候可以加上xml文件,
mapper.xml文件的默認路徑是classPath:mapper/ 。 也可以通過配置文件mybatis.mapperLocations來指定
本框架待改進的地方
- 實現事務的管理功能, 可以參考spring的事務管理實現
參考項目
hutool 這真的是一個很好的工具包項目, 全面而有強大, 依賴少。 本框架大量依賴這個工具包
blade是和本項目類似的框架。我的整合netty中的代碼參考這個項目很多
spring
netty