记一次Maven项目改造成SpringBoot项目的过程实践

网友投稿 522 2022-08-22


记一次Maven项目改造成SpringBoot项目的过程实践

目录背景概述过程加依赖修改main方法所在类引入数据库依赖启动类上加入mapper扫描添加application.ymlsqlSessionFactory空指针异常分析改造MybatisSessionFactory类改造前的执行流程改造后的执行流程总结扩展参考

背景概述

团队有一个项目,是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早就熟烂了。 所以打算改造成SpringBoot项目。

过程

加依赖

改造过程一步步来, 先把SpringBootStarter引入进来

2.3.0.RELEASE

org.springframework.boot

spring-boot-starter

http:// org.springframework.boot

spring-boot-dependencies

${spring-boot.version}

pom

import

org.springframework.boot

spring-boot-maven-plugin

2.3.12.RELEASE

com.xxx.pulsar.PulsarMain

repackage

repackage

org.apache.maven.plugins

maven-compiler-plugin

3.8.0

1.8

1.8

修改main方法所在类

在原先的main方法上加上注解

引入数据库依赖

首先把main函数中配置的数据库连接硬编码删除,后面将要使用application.yml来配置

com.baomidou

mybatis-plus-boot-starter

3.4.1

com.alibaba

druid-spring-boot-starter

1.1.13

mysql

mysql-connector-java

8.0.27

org.apache.logging.log4j

log4j-core

org.apache.logging.log4j

log4j-api

启动类上加入mapper扫描

@MapperScan("com.xxx.pulsar.mapper")

添加application.yml

# 端口

server:

port: 8001

mybatis:

# mapper映射文件

mapper-locations: classpath:mapper/*.xml

spring:

application:

# 应用名称

name: pulsar_demo

datasource:

type: com.alibaba.druid.pool.DruidDataSource

druid:

drpoqVHOxQrkiverClassName: 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类

改http://造前的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 action(MAPPER mapper);

}

public static RESULT query(Class mapperClass, Action 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。

@Autowired

private SqlSessionFactory sqlSessionFactory1;

@PostConstruct

public 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 的时序图、流程图和甘特图


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Python 输入/输出(python下载安装教程)
下一篇:跟我学Python图像处理丨基于灰度三维图的图像顶帽运算和黑帽运算(python图像处理生成灰值)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~