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的属性初始化后都会执行该方法。
java 复制代码
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的配置文件中:

XML 复制代码
<!-- Spring的配置文件bean管理 -->
<bean id="myBean" class="com.example.MyBean" init-method="customInitMethod"/>
java 复制代码
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数据库自动初始化

定义枚举类

java 复制代码
//定义策略枚举,用于选择
public enum DataBaseType {

    MYSQL,

    KINGBASE;

}

首先定义策略模式的接口(InitDataBase):

java 复制代码
/**
 * 初始化数据库机制
 */
public interface InitDataBase {

 
    /**
     * 核心任务
     * 1、建立链接
     * 2、是否可链接
     * 3、是否存在库
     * 4、版本是否需要升级
     */
    void startCoreJob() throws SQLException;

  
}

定义静态策略控制工具类DataBaseStrategyUtil

java 复制代码
@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类,用来数据库升级流程,并固定绑定逻辑

java 复制代码
**
 * 抽象核心升级父类
 * 实现用户自定义的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):

java 复制代码
@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";
    }


}

金仓数据库实现类:

java 复制代码
@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。

java 复制代码
@Component
public class DataBaseInitorStrategy {

  //利用Spring上下文来获取对应的数据库bean
  @Autowired
  private ApplicationContext applicationContext;

  public InitDataBase getInit(DataBaseType type){
        return (InitDataBase)context.getBean(type.getBeanName());
  }  
}
相关推荐
吾日三省吾码3 小时前
JVM 性能调优
java
弗拉唐4 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi775 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
2401_857610035 小时前
SpringBoot社团管理:安全与维护
spring boot·后端·安全
少说多做3435 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀5 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20205 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深6 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
凌冰_6 小时前
IDEA2023 SpringBoot整合MyBatis(三)
spring boot·后端·mybatis