SpringBoot利用InitializingBean实现策略模式
InitializingBean接口
Spring的Bean生命周期
在Spring中,bean的默认作用域是单例(singleton)。这意味着每个bean在Spring容器中只会被创建一个实例,并且该实例会被缓存,以便后续的请求中重复使用。这种单例模式是Spring框架中bean作用域的默认设置,旨在确保每个bean在应用程序的生命周期内都是唯一的。
bean的创建和管理是由Spring的IoC(控制反转)容器负责的。这个容器负责实例化、配置和组装对象。
- 首先,通过注解(如
@Component
、@Service
、@Repository
、@Controller
)或XML配置文件来声明bean,也就是定义bean - Spring容器在启动时会扫描指定的包路径,查找带有上述注解的类,并注册这些类为bean定义。此时也将bean存储到
DefaultListableBeanFactory
的实例中 - Spring容器可以根据注册的Bean需要创建bean的实例。
- 对这些实例化的bean对象,进行依赖的补全
- bean的初始化是指容器会调用实现了
InitializingBean
接口的bean的afterPropertiesSet
方法,或者执行通过init-method
指定的方法。 - 在初始化后,会将可以使用的bean实例放到Spring上下文中,也就是ApplicationContext中,此操作后,在Spring环境中就可以使用@autowired注解进行Bean的获取。
- 通过注解@autowired或ApplicationContext来获取相应的bean去使用
- 容器关闭,bean销亡。
InitializingBean接口
InitializingBean是Spring提供的拓展性接口,InitializingBean接口为bean提供了属性初始化后的处理方法。
- 它提供一个afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。
import org.springframework.beans.factory.InitializingBean;
public class MyBean implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
// 在这里执行初始化逻辑
System.out.println("MyBean is initialized");
}
}
- 提供一个可选配置initMethod,用于指定一个自定义的初始化方法。如果你在bean的定义中指定了
init-method
属性,那么这个方法将在afterPropertiesSet()
方法之后被调用。
首先在Spring的配置文件中:
<!-- Spring的配置文件bean管理 -->
<bean id="myBean" class="com.example.MyBean" init-method="customInitMethod"/>
public class MyBean {
public void customInitMethod() {
// 在这里执行初始化逻辑
System.out.println("MyBean is initialized with custom method");
}
}
SpringBoot优雅的实现策略模式
策略模式
策略模式可以大大降低程序中的if else逻辑,可以更好的以类为单位将内容组件化,从而实现高扩展和维护。相关具体的策略模式可以查看设计模式:详细拆解策略模式-CSDN博客
使用InitializingBean实现策略模式
以数据库自动升级SQL脚本的逻辑为例,进行样例实现,数据库自动升级SQL的源码在Conscript: java数据库自动初始化,主要逻辑是根据不同类型的数据库,执行相应的SQL递增脚本,考虑到不同数据库的特性,这里策略模式最为适合。
由于该文章主要是策略模式部分,此样例源码除策略模式外还使用了模板模式,所以此处省略模板模式相关的代码(省略AbstractInitDataBase类代码),直接到具有差异性的子类上,具体源码可以查看Conscript: java数据库自动初始化,
定义枚举类
//定义策略枚举,用于选择
public enum DataBaseType {
MYSQL,
KINGBASE;
}
首先定义策略模式的接口(InitDataBase):
/**
* 初始化数据库机制
*/
public interface InitDataBase {
/**
* 核心任务
* 1、建立链接
* 2、是否可链接
* 3、是否存在库
* 4、版本是否需要升级
*/
void startCoreJob() throws SQLException;
}
定义静态策略控制工具类DataBaseStrategyUtil
@Slf4j
public class DataBaseStrategyUtil {
//用于存放可用的InitDataBase相关bean
private static ConcurrentMap<DataBaseType, InitDataBase> context = new ConcurrentHashMap<>();
//获取
public static InitDataBase getInitDataBase(DataBaseType dataBaseType){
return context.get(dataBaseType);
}
//注册数据库初始化bean
public static void register(DataBaseType dataBaseType,InitDataBase initDataBase){
context.put(dataBaseType,initDataBase);
}
}
利用模板模式定义AbstractInitDataBase 类,用来数据库升级流程,并固定绑定逻辑
**
* 抽象核心升级父类
* 实现用户自定义的InitDataBase 提供基本的执行方法及调度升级逻辑顺序
* 实现InitializingBean 接口,用于定义自我注册机制
*/
@Slf4j
public abstract class AbstractInitDataBase implements InitDataBase,InitializingBean {
//……省略其他代码
private DataBaseType dataBaseType;
//固定构造函数,必须传入DataBaseType枚举
public AbstractInitDataBase(DataBaseProperties dataBaseProperties,ConscriptProperties conscriptProperties,DataBaseType dataBaseType){
this.conscriptProperties = conscriptProperties;
this.dataBaseProperties = dataBaseProperties;
this.dataBaseType = dataBaseType;
}
//……省略其他方法及代码
@Override
public void startCoreJob() throws SQLException {
reloadConfigFile();
initCommConn();
if(testConnection()){
Map<String,String> sqlcontent;
if(databaseIsExitd(commConn)) {
initDbConn();
if(!exitDbVersionTable(dbConn)){
createDbVersionTable();
}
//省略部分代码
sqlcontent = getSqlFromFile(0f);
}
flag = excuteSQL(sqlcontent);
}else{
//省略……
}
close();
}
@Override
public void afterPropertiesSet() {
//在基类中固定注册逻辑,所有子类则可以不关心注册逻辑,专注于本身的逻辑
DataBaseStrategyUtil.register(this.dataBaseType,this);
}
}
下面就是具体的数据库升级实现类,这里的具体子类,主要是为了体系不同类型数据库的差异性;
子类只需要实现AbstractInitDataBase 类,完成AbstractInitDataBase 类定义的固定方法即可(模板模式的精髓)。
Mysql相关实现类(MySqlDataBase):
@Slf4j
//定义bean的名称,便于后续使用策略选择器获取
@Component()
public class MySqlDataBase extends AbstractInitDataBase {
public MySqlDataBase(DataBaseProperties dataBaseProperties, ConscriptProperties conscriptProperties) {
//调用父类的构造函数,固定MYSQL的数据类型,让父类完成bean的注册
super(dataBaseProperties, conscriptProperties,MYSQL);
}
@Override
public void initCommConn() {
try {
//省略代码……
}catch (Exception e){
log.error("【Conscript】Database initialization :data base is not connection.....");
}
}
@Override
public void initDbConn() {
try{
//逻辑代码
}catch (Exception e){
log.warn("");
}
}
@Override
public boolean databaseIsExitd(Connection connection){
try {
//逻辑代码
return true;
}catch (Exception e){
log.error("【Conscript】Database initialization :database base query is error");
}
return false;
}
@Override
public String createDataBaseSQL() {
return "CREATE DATABASE IF NOT EXISTS "+ dataBaseProperties.getDbName()+" DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci";
}
}
金仓数据库实现类:
@Slf4j
@Component()
public class KingBase8DataBase extends AbstractInitDataBase {
public KingBase8DataBase(DataBaseProperties dataBaseProperties, ConscriptProperties conscriptProperties) {
super(dataBaseProperties, conscriptProperties,KINGBASE);
}
@Override
public void initCommConn() {
try{
Class.forName(dataBaseProperties.getDriverClassName());
//逻辑代码
}catch (Exception e){
log.error("【Conscript】Database initialization :data base is not connection.....");
}
}
@Override
public void initDbConn() {
try{
Class.forName(dataBaseProperties.getDriverClassName());
//逻辑代码
}catch (Exception e){
log.error("【Conscript】Database initialization :data base is not connection.....");
}
}
@Override
public boolean createDataBase() {
try {
//逻辑代码
}catch (Exception e){
log.error("【Conscript】Create {} Database Fail!",dataBaseProperties.getDbName());
return false;
}
return true;
}
@Override
public String createDataBaseSQL() {
return "CREATE SCHEMA \""+dataBaseProperties.getSchema()+"\" AUTHORIZATION \""+dataBaseProperties.getUsername()+"\" ";
}
@Override
public boolean databaseIsExitd(Connection connection) {
try {
//逻辑代码
}catch (Exception e){
log.error("【Conscript】Database initialization :database base query is error");
}
return false;
}
}
不使用InitializingBean接口实现策略分发方法
如果不使用InitializingBean,还可以利用ApplicationContext来获取bean,构建一个DataBaseInit的Strategy策略控制类,以此来通过枚举DataBaseType来控制选择使用哪一种bean。
@Component
public class DataBaseInitorStrategy {
//利用Spring上下文来获取对应的数据库bean
@Autowired
private ApplicationContext applicationContext;
public InitDataBase getInit(DataBaseType type){
return (InitDataBase)context.getBean(type.getBeanName());
}
}