記一次Maven項(xiàng)目改造成SpringBoot項(xiàng)目的過程實(shí)踐
背景概述
團(tuán)隊(duì)有一個(gè)項(xiàng)目,是maven構(gòu)建的普通Java項(xiàng)目。
項(xiàng)目沒有使用spring,用到了mysql、mybatis,還有其他大數(shù)據(jù)技術(shù),比如flink、pulsar。
項(xiàng)目里連接數(shù)據(jù)庫的部分,需要用到多個(gè)配置文件,一個(gè)是mybatis配置文件,一個(gè)是數(shù)據(jù)庫配置文件。如果用SpringBoot可以簡化為一個(gè)application.yml文件。
項(xiàng)目里打包方式復(fù)雜,依賴一個(gè)maven-assemble的插件,打出的包是兩個(gè)jar,出現(xiàn)過由于配置文件讀取方式的錯(cuò)誤,導(dǎo)致jar包還運(yùn)行不了。使用這個(gè)插件打包,還需要寫一個(gè)自定義的配置文件,配置各個(gè)資源打包的參數(shù)。如果用SpringBoot,直接引入spring-boot-maven-plugin,打出的就是可執(zhí)行jar包,不需要繁瑣的配置,不需要自己寫讀取配置的代碼。
為什么要改造成SpringBoot項(xiàng)目呢,因?yàn)镾pringBoot
- 簡化配置,不用寫這么多配置文件
- 自動(dòng)配置,引入starter依賴,可以自動(dòng)把默認(rèn)配置配好
- 內(nèi)嵌web容器
- 自動(dòng)版本管理,maven和starter配置使用
- 生態(tài)集成容易,如果項(xiàng)目想要集成另外的能力,引一些starter依賴,少量的配置就可以快速接入
此外也是一次技術(shù)提升的機(jī)會(huì),技術(shù)的優(yōu)勢,SpringBoot早就熟爛了。 所以打算改造成SpringBoot項(xiàng)目。
過程
加依賴
改造過程一步步來, 先把SpringBootStarter引入進(jìn)來
<properties> <spring-boot.version>2.3.0.RELEASE</spring-boot.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- 此處省略其他的依賴 --> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.3.12.RELEASE</version> <configuration> <mainClass>com.xxx.pulsar.PulsarMain</mainClass> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
修改main方法所在類
在原先的main方法上加上注解
引入數(shù)據(jù)庫依賴
首先把main函數(shù)中配置的數(shù)據(jù)庫連接硬編碼刪除,后面將要使用application.yml來配置
<!-- mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.13</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> <exclusions> <exclusion> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> </exclusion> <exclusion> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> </exclusion> </exclusions> </dependency>
啟動(dòng)類上加入mapper掃描
@MapperScan("com.xxx.pulsar.mapper")
添加application.yml
# 端口 server: port: 8001 mybatis: # mapper映射文件 mapper-locations: classpath:mapper/*.xml spring: application: # 應(yīng)用名稱 name: pulsar_demo datasource: type: com.alibaba.druid.pool.DruidDataSource druid: driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/db_test?useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&allowPublicKeyRetrieval=true username: root password: 123456 initial-size: 10 max-active: 100 min-idle: 10 max-wait: 60000 pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 60000 max-evictable-idle-time-millis: 300000 validation-query: SELECT 1 FROM DUAL # validation-query-timeout: 5000 test-on-borrow: false test-on-return: false test-while-idle: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 #filters: #配置多個(gè)英文逗號(hào)分隔(統(tǒng)計(jì),sql注入,log4j過濾) filters: stat,wall stat-view-servlet: enabled: true url-pattern: /druid/*
sqlSessionFactory空指針異常分析
啟動(dòng)測試,報(bào)錯(cuò),數(shù)據(jù)庫連接的地方報(bào)sqlSessionFactory空指針異常
查看錯(cuò)誤堆棧,項(xiàng)目啟動(dòng)的時(shí)候,會(huì)從RuleFunction這個(gè)類的構(gòu)造函數(shù)里面開始初始化資源。
setFields和setExtInfo這兩個(gè)方法寫在構(gòu)造函數(shù)中,在類初始化時(shí),就會(huì)調(diào)用,從數(shù)據(jù)庫查初始化資源,
這兩個(gè)方法內(nèi)部會(huì)去查數(shù)據(jù)庫獲取基礎(chǔ)資源,見下圖
RuleFunction初始化時(shí),Spring還沒有幫我們將MybatisSessionFactory類實(shí)例化,所以報(bào)了空指針異常。
改造MybatisSessionFactory類
改造前的MybatisSessionFactory類代碼如下
public class MybatisSessionFactory { private volatile static SqlSessionFactory sqlSessionFactory; private MybatisSessionFactory() {} public static void init(String configStr, Properties prop) { if (sqlSessionFactory == null) { synchronized (MybatisSessionFactory.class) { if (sqlSessionFactory == null) { InputStream is = new ByteArrayInputStream(configStr.getBytes()); sqlSessionFactory = new SqlSessionFactoryBuilder().build(is, prop); } } } } public interface Action<RESULT, MAPPER> { RESULT action(MAPPER mapper); } public static <MAPPER, RESULT> RESULT query(Class<MAPPER> mapperClass, Action<RESULT, MAPPER> action) { if (sqlSessionFactory == null) { throw new NullPointerException("Mybatis未初始化"); } try (SqlSession sqlSession = sqlSessionFactory.openSession()) { MAPPER mapper = sqlSession.getMapper(mapperClass); return action.action(mapper); } } }
這個(gè)MybatisSessionFactory類,是在main方法中去初始化的,main方法中調(diào)用MybatisSessionFactory.init方法,傳入配置文件和配置參數(shù),從而初始化SqlSesstionFactory。
改造的過程中,我們把main方法中調(diào)用MybatisSessionFactory.init方法給刪除了,導(dǎo)致SqlSesstionFactory未初始化。
為什么不在main方法中調(diào)用MybatisSessionFactory.init,從而初始化SqlSesstionFactory?因?yàn)槲蚁Mㄟ^Spring注入和管理SqlSesstionFactory的對(duì)象。
在static工具類方法里調(diào)用Spring托管的bean對(duì)象[1]
這里遇到一個(gè)問題,注意SqlSessionFactory聲明方式上用了static關(guān)鍵字。即這個(gè)屬性是類的,不是對(duì)象的。生命周期比較早,在類初始化時(shí)就會(huì)初始化。
private volatile static SqlSessionFactory sqlSessionFactory;
我使用下面的方式,在MybatisSessionFactory類中加入下面代碼,并在MybatisSessionFactory類上加注解@Component。
@Autowired private SqlSessionFactory sqlSessionFactory1; @PostConstruct public void update(){ sqlSessionFactory = sqlSessionFactory1; }
- 首先使用@Autowired注入SqlSessionFactory
- 使用@PostConstruct修飾update方法,方法名任意,不能有參數(shù)。這樣是為了保證這個(gè)順序:依賴注入之后,才執(zhí)行update方法。該注解的方法在整個(gè)Bean初始化中的執(zhí)行順序:Constructor(構(gòu)造方法) -> @Autowired(依賴注入) -> @PostConstruct(注釋的方法)
RuleFunction類改造
還要改一個(gè)地方,初始化數(shù)據(jù)庫資源的入口方法是在RuleFunction類的構(gòu)造函數(shù)中調(diào)用的。由于構(gòu)造函數(shù)會(huì)先于依賴注入執(zhí)行,需要把setFields和setExtInfo這兩個(gè)方法提取出來,且需要在依賴注入后執(zhí)行。
修改成如下,并在RuleFunction類上加注解@Component。
改造前的執(zhí)行流程
- main方法內(nèi)部調(diào)用MybatisSessionFactory的init方法
- MybatisSessionFactory的init方法中new一個(gè)SqlSessionFactory
- RuleFunction初始化時(shí),調(diào)用自身構(gòu)造方法
- RuleFunction調(diào)用MybatisSessionFactory的query方法查詢數(shù)據(jù)庫
改造后的執(zhí)行流程
- PulsarMain和MybatisSessionFactory是松耦合的,
- MybatisSessionFactory初始化時(shí),因?yàn)橥ㄟ^@Autowired注解注入了SqlSessionFactory,所以需要初始化SqlSessionFactory,SqlSessionFactory初始化過程中會(huì)去使用配置文件中的數(shù)據(jù)庫連接參數(shù)初始化。
- MybatisSessionFactory初始化完成后,由于MybatisSessionFactory.update方法使用了@PostConstruct注解,會(huì)執(zhí)行update方法,將SqlSessionFactory賦值給靜態(tài)屬性sqlSessionFactory。
- 后續(xù)RuleFunction的setFields方法執(zhí)行過程中,就可以使用MybatisSessionFactory的query方法查詢數(shù)據(jù)庫了
總結(jié)
這次改造過程,對(duì)類加載過程、對(duì)象的實(shí)例化、static關(guān)鍵字、spring bean的生命周期有了更深入的理解。
- 類加載過程,會(huì)初始化調(diào)用static修飾的屬性、方法、代碼塊
- 類加載過程[2]:加載、鏈接、初始化
- 其中鏈接的過程:驗(yàn)證、準(zhǔn)備、解析
- 類初始化后,可以通過new關(guān)鍵字實(shí)例化一個(gè)對(duì)象,其它方式:通過反射api實(shí)例化
- spring bean的生命周期[3]:實(shí)例化、屬性賦值、初始化、銷毀
擴(kuò)展
對(duì)于這個(gè)問題抽象一下:Spring項(xiàng)目中,如果需要在一個(gè)類初始化時(shí)加載數(shù)據(jù)庫資源,可以有哪些方式?
參考
- [1]靜態(tài)方法(工具類)中調(diào)用Spring管理的Bean
- [2]類加載過程
- [3]Spring Bean 的生命周期
- Markdown 基于 Mermaid 的時(shí)序圖、流程圖和甘特圖
到此這篇關(guān)于記一次Maven項(xiàng)目改造成SpringBoot項(xiàng)目的過程實(shí)踐的文章就介紹到這了,更多相關(guān)Maven改造成SpringBoot項(xiàng)目內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- IDEA創(chuàng)建SpringBoot的maven項(xiàng)目的方法步驟
- 使用maven開發(fā)springboot項(xiàng)目時(shí)pom.xml常用配置(推薦)
- SpringBoot使用Maven插件進(jìn)行項(xiàng)目打包的方法
- springboot maven 項(xiàng)目打包jar 最后名稱自定義的教程
- Maven搭建springboot項(xiàng)目的方法步驟
- 在SpringBoot項(xiàng)目中利用maven的generate插件
- SpringBoot+Maven 多模塊項(xiàng)目的構(gòu)建、運(yùn)行、打包實(shí)戰(zhàn)
- SpringBoot創(chuàng)建maven多模塊項(xiàng)目實(shí)戰(zhàn)代碼
- springboot+maven快速構(gòu)建項(xiàng)目的示例代碼
相關(guān)文章
spring security國際化及UserCache的配置和使用
這篇文章主要介紹下國際化的配置及UserCache的配置及使用教程,感興趣的朋友參考下實(shí)現(xiàn)代碼吧2017-09-09Java Springboot websocket使用案例詳解
這篇文章主要介紹了Java Springboot websocket使用案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09swing組件JScrollPane滾動(dòng)條實(shí)例代碼
這篇文章主要介紹了swing組件JScrollPane滾動(dòng)條實(shí)例代碼,分享了兩個(gè)相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02mybatis中resultMap 標(biāo)簽的使用教程
resultMap 標(biāo)簽用來描述如何從數(shù)據(jù)庫結(jié)果集中來加載對(duì)象,這篇文章重點(diǎn)給大家介紹mybatis中resultMap 標(biāo)簽的使用,感興趣的朋友一起看看吧2018-07-07SpringBoot?2.5.5整合輕量級(jí)的分布式日志標(biāo)記追蹤神器TLog的詳細(xì)過程
分布式追蹤系統(tǒng)是一個(gè)最終的解決方案,如果您的公司已經(jīng)上了分布式追蹤系統(tǒng),這篇文章主要介紹了SpringBoot?2.5.5整合輕量級(jí)的分布式日志標(biāo)記追蹤神器TLog,需要的朋友可以參考下2022-10-10Java超詳細(xì)分析講解final關(guān)鍵字的用法
關(guān)于final關(guān)鍵字,它也是我們一個(gè)經(jīng)常用的關(guān)鍵字,可以修飾在類上、或者修飾在變量、方法上,以此看來定義它的一些不可變性!像我們經(jīng)常使用的String類中,它便是final來修飾的類,并且它的字符數(shù)組也是被final所修飾的。但是一些final的一些細(xì)節(jié)你真的了解過嗎2022-06-06