当前位置: 首页 > article >正文

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());
  }  
}


http://www.kler.cn/a/373238.html

相关文章:

  • openGauss开源数据库实战十
  • [ 问题解决篇 ] 解决windows虚拟机安装vmtools报错-winserver2012安装vmtools及安装KB2919355补丁 (附离线工具)
  • 【C++刷题】力扣-#575-分糖果
  • 书生大模型实战营 L0 入门岛
  • C++——写一函数,将一个3x3的整型矩阵转置。用指针或引用方法处理。
  • 提示词高级阶段学习day4.1
  • 一:Linux学习笔记(第一阶段)-- 安装软件 vmware workstation 虚拟机软件 centos系统
  • XQT_UI 组件|01|颜色
  • Vue.js 构建可复用的组件
  • 小型语言模型(LLM)综述!
  • TVB被嘲讽工资低,张兆辉得体且高情商的回应,赢得网友赞赏
  • 【JIT/极态云】技术文档--发起申请
  • Chrome DevTools:Console Performance 汇总篇
  • LabVIEW涡扇发动机加力泵测试
  • 知难而进:什么是Web开发——关于软件开发你需要知道些什么
  • AIGC时代的数据盛宴:R语言引领数据分析新风尚
  • C++算法第五天
  • 无人机产校融合,突破理论与实战代沟,快速转化市场价值
  • php解密,sg11解密-sg15解密 如何由sourceGuardian11-sourceGuardian15加密(sg11加密~sg15加密)的源码
  • Flutter主题切换
  • Apache Linkis:重新定义计算中间件
  • 事务的四大隔离级别、数据库中的共享锁与排他锁、MySQL 的行级锁与表级锁
  • C++虚函数(详解)
  • 无人机避障——路径规划篇(一) JPS跳点搜索算法A*算法对比
  • React四官方文档总结一UI与交互
  • 4.2-7 运行MR应用:词频统计