springboot创建maven工程,springbootmavenplugin改造
00-1010后台概述流程加依赖修改main方法类介绍数据库依赖启动类添加mapper扫描到应用程序。ymlsqlsessionfactory空指针异常分析及转换MybatisSessionFactory类转换前执行过程转换后执行过程总结扩展引用
00-1010团队有一个项目,是maven搭建的普通Java项目。
项目没有使用spring,而是使用了mysql、mybatis等大数据技术,比如flink、pulsar等。
在项目连接数据库的部分,需要使用多个配置文件,一个是mybatis配置文件,一个是数据库配置文件。如果使用SpringBoot,可以简化为application.yml文件。
项目的打包方式比较复杂,依靠一个maven-assemble插件,产生的包是两个jar。配置文件的读取模式出现了错误,导致JAR包无法运行。要使用这个插件包,还需要编写一个定制的配置文件来配置每个资源包的参数。如果使用SpringBoot,直接引入spring-boot-maven-plugin,可执行的jar包就打出来了,不需要复杂的配置,也不需要自己编写和读取配置好的代码。
为什么要改造成SpringBoot项目呢,因为SpringBoot
简化配置。自动配置不需要写那么多配置文件。通过引入starter依赖项,您可以自动将默认配置与用于自动版本管理的嵌入式web容器相匹配。对于maven和starter配置,很容易使用生态集成。如果项目想集成其他能力,引入一些starter依赖,少量配置就可以快速接入。此外,这也是一个技术改进的机会。技术的优势已经成熟。所以我打算把它变成一个SpringBoot项目。
目录
00-1010转化过程是一步一步来的。先介绍SpringBootStarter。
属性spring-boot . version 2 . 3 . 0 . release/spring-boot . version/properties dependencies依赖关系groupIdorg.springframework.boot/groupId artifactId spring-boot-starter/artifactId/dependency!-此处省略其他依赖项-/依赖项依赖项依赖项groupIdorg.springframework.boot/groupId artifact spring-boot-Dependencies/artifact id version $ { spring-boot . version }/version typepom/type scope import/scope/dependency/dependencymanagement build插件groupIdorg.springframework.boot/groupId artifact id spring-boot-maven-plugin/artifact id version2.3.12.RELEASE/version配置mainClasscom.xxx.pulsar.PulsarMain/mainClass/配置执行执行idrepack
age</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方法上加上注解
引入数据库依赖
首先把main函数中配置的数据库连接硬编码删除,后面将要使用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>
启动类上加入mapper扫描
@MapperScan("com.xxx.pulsar.mapper")
添加application.yml
# 端口server: port: 8001mybatis: # mapper映射文件 mapper-locations: classpath:mapper/*.xmlspring: application: # 应用名称 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: #配置多个英文逗号分隔(统计,sql注入,log4j过滤) filters: stat,wall stat-view-servlet: enabled: true url-pattern: /druid/*
sqlSessionFactory空指针异常分析
启动测试,报错,数据库连接的地方报sqlSessionFactory空指针异常
查看错误堆栈,项目启动的时候,会从RuleFunction这个类的构造函数里面开始初始化资源。
setFields和setExtInfo这两个方法写在构造函数中,在类初始化时,就会调用,从数据库查初始化资源,
这两个方法内部会去查数据库获取基础资源,见下图
RuleFunction初始化时,Spring还没有帮我们将MybatisSessionFactory类实例化,所以报了空指针异常。
改造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); } }}
这个MybatisSessionFactory类,是在main方法中去初始化的,main方法中调用MybatisSessionFactory.init方法,传入配置文件和配置参数,从而初始化SqlSesstionFactory。
改造的过程中,我们把main方法中调用MybatisSessionFactory.init方法给删除了,导致SqlSesstionFactory未初始化。
为什么不在main方法中调用MybatisSessionFactory.init,从而初始化SqlSesstionFactory?因为我希望通过Spring注入和管理SqlSesstionFactory的对象。
在static工具类方法里调用Spring托管的bean对象[1]
这里遇到一个问题,注意SqlSessionFactory声明方式上用了static关键字。即这个属性是类的,不是对象的。生命周期比较早,在类初始化时就会初始化。
private volatile static SqlSessionFactory sqlSessionFactory;
我使用下面的方式,在MybatisSessionFactory类中加入下面代码,并在MybatisSessionFactory类上加注解@Component。
@Autowiredprivate SqlSessionFactory sqlSessionFactory1;@PostConstructpublic void update(){ sqlSessionFactory = sqlSessionFactory1;}
首先使用@Autowired注入SqlSessionFactory使用@PostConstruct修饰update方法,方法名任意,不能有参数。这样是为了保证这个顺序:依赖注入之后,才执行update方法。该注解的方法在整个Bean初始化中的执行顺序:Constructor(构造方法)->@Autowired(依赖注入)->@PostConstruct(注释的方法)RuleFunction类改造
还要改一个地方,初始化数据库资源的入口方法是在RuleFunction类的构造函数中调用的。由于构造函数会先于依赖注入执行,需要把setFields和setExtInfo这两个方法提取出来,且需要在依赖注入后执行。
修改成如下,并在RuleFunction类上加注解@Component。
改造前的执行流程
main方法内部调用MybatisSessionFactory的init方法MybatisSessionFactory的init方法中new一个SqlSessionFactoryRuleFunction初始化时,调用自身构造方法RuleFunction调用MybatisSessionFactory的query方法查询数据库
改造后的执行流程
PulsarMain和MybatisSessionFactory是松耦合的,MybatisSessionFactory初始化时,因为通过@Autowired注解注入了SqlSessionFactory,所以需要初始化SqlSessionFactory,SqlSessionFactory初始化过程中会去使用配置文件中的数据库连接参数初始化。MybatisSessionFactory初始化完成后,由于MybatisSessionFactory.update方法使用了@PostConstruct注解,会执行update方法,将SqlSessionFactory赋值给静态属性sqlSessionFactory。后续RuleFunction的setFields方法执行过程中,就可以使用MybatisSessionFactory的query方法查询数据库了
总结
这次改造过程,对类加载过程、对象的实例化、static关键字、spring bean的生命周期有了更深入的理解。
类加载过程,会初始化调用static修饰的属性、方法、代码块类加载过程[2]:加载、链接、初始化其中链接的过程:验证、准备、解析类初始化后,可以通过new关键字实例化一个对象,其它方式:通过反射api实例化spring bean的生命周期[3]:实例化、属性赋值、初始化、销毁
扩展
对于这个问题抽象一下:Spring项目中,如果需要在一个类初始化时加载数据库资源,可以有哪些方式?
参考
[1]静态方法(工具类)中调用Spring管理的Bean[2]类加载过程[3]Spring Bean 的生命周期Markdown 基于 Mermaid 的时序图、流程图和甘特图到此这篇关于记一次Maven项目改造成SpringBoot项目的过程实践的文章就介绍到这了,更多相关Maven改造成SpringBoot项目内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。