当前位置: 首页>后端>正文

自定义springboot-starter实现通用基础配置(数据库案例)

背景说明

数据库基础配置如数据源配置,mybatis配置,sharding相关配置等在多个项目中如果都进行配置,大致会有以下几个问题

  • 需要编写代码(虽然可以复制,还是有些东西需要改),调试等,需要耗费一定的时间
  • 不同项目的这些基础配置不一致,会带来一些问题(我们总是希望同一体系的这些通用组件尽量一致)
  • 当有些内容需要修改时,需要识别哪些项目使用并一一修改.耗费时间的同时,也带来一定风险.如遗漏等

这些都是通用基础配置普遍的问题,解决方案很自然的想到抽离封装成通用组件.
本案例采用方式为抽离封装成自定义springboot-starter组件.

starter组件说明

先看使用步骤以及效果

  • 引入自定义starter依赖
<dependency>
    <groupId>com.jiujiu</groupId>
    <artifactId>jiu-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>
  • 启动类添加数据库开关注解,指定需要注册的数据库列表.
    如@EnableDB(value = {DBConfigEnum.TEST_DB, DBConfigEnum.ZCJ_DB})
@SpringBootApplication
@EnableDB(value = {DBConfigEnum.TEST_DB, DBConfigEnum.ZCJ_DB})
public class JiuDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(JiuDemoApplication.class, args);
    }
}
  • 配置文件指定加载starter项目配置文件以及指定mapper的位置(包含mapper和对应的xml)
spring:
  application:
    name: jiu-demo
  profiles:
    # 指定加载starter项目配置文件,这个顺序会以排在后面的为准
    active: starter,starter-dev,dev

db:
  mapperLocation:
    test_db: com.example.jiudemo.mapper.testdb
    zcj_db: com.example.jiudemo.mapper.zcjdb
  mapperXmlLocation:
    test_db: classpath:mapper/testdb/*.xml
    zcj_db: classpath:mapper/zcjdb/*.xml
自定义springboot-starter实现通用基础配置(数据库案例),第1张

测试结果
调用mapper,返回数据


自定义springboot-starter实现通用基础配置(数据库案例),第2张

自定义组件jiu-spring-boot-starter核心代码(如何自定义starter网上很多资料)

  • 依赖在文章末尾

  • @EnableDB

/**
 * 通过定义枚举列表动态注册数据库
 */
@Retention(RUNTIME)
@Target(TYPE)
@Import({DBCommonAutoConfiguration.class,
//        DBSelector.class
        DBRegistrar.class

})
public @interface EnableDB {

    DBConfigEnum[] value() default {};

}
  • 配置类DBCommonAutoConfiguration主要解决MapperScannerConfigurer读取不到配置文件属性的问题
@Configuration
@ConditionalOnMissingBean(value = {DBCommonAutoConfiguration.class})
@EnableAutoConfiguration(exclude= {DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class, MybatisAutoConfiguration.class})
// 解决MapperScannerConfigurer读取不到配置文件属性的问题
@Import(value = {CustomPropertySourcesPlaceholder.class})
public class DBCommonAutoConfiguration {
}
  • 配置类DBRegistrar或者DBSelector(实现动态注册bean的不同方式,选其一即可)
@Slf4j
public class DBRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {


        // 根据注解的枚举值,注入对应的XXXAutoConfiguration
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableDB.class.getName());
        DBConfigEnum[] values = (DBConfigEnum[]) annotationAttributes.get("value");

        if (ArrayUtils.isEmpty(values)) {
            return;
        }
        List<String> dbList = Arrays.stream(values).map(DBConfigEnum::getDbName).collect(Collectors.toList());
        log.info("准备注册数据库,dbList->{}", dbList);
        for (int i = 0; i < values.length; i++) {
            DBConfigEnum dbConfigEnum = values[i];
            BeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClassName(dbConfigEnum.getConfigClassName());
            registry.registerBeanDefinition(dbConfigEnum.getConfigClassName(), beanDefinition);
        }
    }
}
@Slf4j
public class DBSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        // 根据注解的枚举值,注入对应的XXXAutoConfiguration
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableDB.class.getName());
        DBConfigEnum[] values = (DBConfigEnum[]) annotationAttributes.get("value");

        if (ArrayUtils.isEmpty(values)) {
            return new String[0];
        }
        String[] array = new String[values.length];
        List<String> dbList = Arrays.stream(values).map(DBConfigEnum::getDbName).collect(Collectors.toList());
        log.info("准备注册数据库,dbList->{}", dbList);
        for (int i = 0; i < values.length; i++) {
            DBConfigEnum dbConfigEnum = values[i];
            array[i] = dbConfigEnum.getConfigClassName();
        }

        return array;
    }
}
  • DBConfigEnum
public enum DBConfigEnum {

    TEST_DB("test_db", TestDBAutoConfiguration.class.getName()),
    ZCJ_DB("zcj_db", ZcjDBAutoConfiguration.class.getName())
    ;


    private String dbName;

    private String configClassName;

    DBConfigEnum(String dbName, String configClassName) {
        this.dbName = dbName;
        this.configClassName = configClassName;
    }

    public String getDbName() {
        return dbName;
    }

    public String getConfigClassName() {
        return configClassName;
    }
}
  • TestDBAutoConfiguration
@Configuration
// 指定配置文件
@ImportResource(locations = {"classpath:jdbc-testdb.xml"})
public class TestDBAutoConfiguration {

}
  • jdbc-testdb.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sharding="http://shardingsphere.apache.org/schema/shardingsphere/sharding"
       xmlns:master-slave="http://shardingsphere.apache.org/schema/shardingsphere/masterslave"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/tx
                        http://www.springframework.org/schema/tx/spring-tx.xsd
                        http://shardingsphere.apache.org/schema/shardingsphere/sharding
                        http://shardingsphere.apache.org/schema/shardingsphere/sharding/sharding.xsd
                        http://shardingsphere.apache.org/schema/shardingsphere/masterslave
                        http://shardingsphere.apache.org/schema/shardingsphere/masterslave/master-slave.xsd">
    <context:annotation-config />
    <context:component-scan base-package="io.shardingsphere.example.spring.namespace.jpa" />

    <context:property-placeholder location="classpath:**/*.yml" ignore-unresolvable="true"/>

    <bean id="ds-testdb-master" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close" >
        <property name="name" value="ds-testdb-master"/>
        <property name="driverClassName" value="${db.driver-class-name}"/>
        <property name="url" value="${db.testdb-master.url}"/>
        <property name="username" value="${db.testdb-master.username}"/>
        <property name="password" value="${db.testdb-master.password}"/>

        <!-- 启动程序时,在连接池中初始化多少个连接 -->
        <property name="initialSize" value="10"/>
        <!-- 启动程序时,在连接池中初始化多少个连接 -->
        <property name="minIdle" value="5"/>
        <!-- 连接池中最多支持多少个活动会话 -->
        <property name="maxActive" value="300"/>
        <!-- 程序向连接池中请求连接时,超过maxWait的值后,认为本次请求失败,即连接池没有可用连接,单位毫秒,设置-1时表示无限等待 -->
        <property name="maxWait" value="60000"/>
        <property name="queryTimeout" value="600000"/>
        <property name="socketTimeout" value="600000"/>
        <!-- 检查空闲连接的频率,单位毫秒, 非正整数时表示不进行检查 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <!-- 池中某个连接的空闲时长达到 N 毫秒后, 连接池在下次检查空闲连接时,将回收该连接,要小于防火墙超时设置 -->
        <property name="minEvictableIdleTimeMillis" value="300000"/>

        <property name="transactionQueryTimeout" value="600000"/>
        <!-- 用来检测连接是否有效的sql -->
        <property name="validationQuery" value="SELECT 'x'"/>
        <!-- 空间时执行 validationQuery -->
        <property name="testWhileIdle" value="true"/>
        <!-- 程序 申请 连接时,进行连接有效性检查(低效,影响性能) -->
        <property name="testOnBorrow" value="false"/>
        <!-- 程序 返还 连接时,进行连接有效性检查(低效,影响性能) -->
        <property name="testOnReturn" value="false"/>
        <!-- 别名的方式配置扩展插件. stat:监控统计 -->
        <property name="filters" value="stat"/>
    </bean>


    <bean id="ds-testdb-slave" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close" >
        <property name="name" value="ds-testdb-slave"/>
        <property name="driverClassName" value="${db.driver-class-name}"/>
        <property name="url" value="${db.testdb-slave.url}"/>
        <property name="username" value="${db.testdb-slave.username}"/>
        <property name="password" value="${db.testdb-slave.password}"/>

        <!-- 启动程序时,在连接池中初始化多少个连接 -->
        <property name="initialSize" value="10"/>
        <!-- 启动程序时,在连接池中初始化多少个连接 -->
        <property name="minIdle" value="5"/>
        <!-- 连接池中最多支持多少个活动会话 -->
        <property name="maxActive" value="300"/>
        <!-- 程序向连接池中请求连接时,超过maxWait的值后,认为本次请求失败,即连接池没有可用连接,单位毫秒,设置-1时表示无限等待 -->
        <property name="maxWait" value="60000"/>
        <property name="queryTimeout" value="600000"/>
        <property name="socketTimeout" value="600000"/>
        <!-- 检查空闲连接的频率,单位毫秒, 非正整数时表示不进行检查 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <!-- 池中某个连接的空闲时长达到 N 毫秒后, 连接池在下次检查空闲连接时,将回收该连接,要小于防火墙超时设置 -->
        <property name="minEvictableIdleTimeMillis" value="300000"/>

        <property name="transactionQueryTimeout" value="600000"/>
        <!-- 用来检测连接是否有效的sql -->
        <property name="validationQuery" value="SELECT 'x'"/>
        <!-- 空间时执行 validationQuery -->
        <property name="testWhileIdle" value="true"/>
        <!-- 程序 申请 连接时,进行连接有效性检查(低效,影响性能) -->
        <property name="testOnBorrow" value="false"/>
        <!-- 程序 返还 连接时,进行连接有效性检查(低效,影响性能) -->
        <property name="testOnReturn" value="false"/>
        <!-- 别名的方式配置扩展插件. stat:监控统计 -->
        <property name="filters" value="stat"/>
    </bean>


    <!-- 读写分离 -->
    <master-slave:load-balance-algorithm id="randomStrategy" type="RANDOM" />

    <sharding:inline-strategy id="orderTableStrategy" sharding-column="account_no" algorithm-expression="product_order_$->{ account_no % 2 }" />

    <sharding:data-source id="testdb-shardingDataSource">
        <sharding:sharding-rule data-source-names="ds-testdb-master,ds-testdb-slave">
            <sharding:master-slave-rules>
                <sharding:master-slave-rule id="ds_testdb_ms" master-data-source-name="ds-testdb-master" slave-data-source-names="ds-testdb-slave" strategy-ref="randomStrategy" />
            </sharding:master-slave-rules>
            <sharding:table-rules>
                <sharding:table-rule logic-table="product_order" actual-data-nodes="ds_testdb_ms.product_order_$->{0..1}" table-strategy-ref="orderTableStrategy" />
            </sharding:table-rules>
        </sharding:sharding-rule>

        <sharding:props>
            <prop key="sql.show">true</prop>
        </sharding:props>
    </sharding:data-source>

    <!-- myBatis文件 -->
    <bean id="sqlSessionFactory-testdb" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="testdb-shardingDataSource"/>
        <!-- mapperXml路径 -->
        <property name="mapperLocations" value="${db.mapperXmlLocation.test_db}"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <bean id="mapperScan-testdb" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- mapper路径 -->
        <property name="basePackage" value="${db.mapperLocation.test_db}"/>
<!--        <property name="basePackage" value="com.example.jiudemo.mapper.testdb"/>-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory-testdb"/>
    </bean>

    <bean id="sqlSessionTemplate-zcjdb" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg ref="sqlSessionFactory-testdb"/>
    </bean>

    <!-- 事务 -->
    <bean id="transactionManager-testdb" name="transactionManager-testdb" primary="false" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="testdb-shardingDataSource"/>
    </bean>

    <!-- 配置 Annotation 驱动,扫描@Transactional注解的类定义事务  -->
<!--    <tx:annotation-driven transaction-manager="transactionManager-testdb" proxy-target-class="true"/>-->

</beans>
  • 配置文件


    自定义springboot-starter实现通用基础配置(数据库案例),第3张
db:
  driver-class-name: com.mysql.cj.jdbc.Driver
  maxActive: 30
  zcjdb:
    url: jdbc:mysql://${instance.ip}:3306/zcjdb?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8&useSSL=false
    username: root
    password: xxxxxx
  testdb-master:
    url: jdbc:mysql://${instance.ip}:3306/testdb_master?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8&useSSL=false
    username: root
    password: xxxxxx
  testdb-slave:
    url: jdbc:mysql://${instance.ip}:3306/testdb_slave?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8&useSSL=false
    username: root
    password: xxxxxx
  • 相关依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.jiujiu</groupId>
    <artifactId>jiu-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>jiu-spring-boot-starter</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 配置自动提示 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.8</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>


        <!--数据库连接-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.12</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>

        <!-- pagehelper 分页插件 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.3.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>mybatis-spring-boot-starter</artifactId>
                    <groupId>org.mybatis.spring.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-core</artifactId>
            <version>4.1.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-namespace</artifactId>
            <version>4.1.1</version>
        </dependency>


        <!-- 工具包 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.6.2</version>
        </dependency>

        <!--用于加密-->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.15</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>17.0</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.10</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.69</version>
        </dependency>

    </dependencies>

</project>


https://www.xamrdz.com/backend/3zx1945724.html

相关文章: