自定义MyBatisAutoConfiguration,MyBatis的配置从未如此简单过~(实现自己的 start)

项目 github地址:https://github.com/winterme/pkusoft-mybatis

jar 包地址:链接:https://pan.baidu.com/s/1MMsZgjbm9yKkn4GfcZ1WIQ   提取码:to0t 

 

在工作中,发现我们经理写的一个包很好用,就是只需要在 yml 上配置 数据库连接信息,包路径,xml路径,mybatis 就能跑了,很方便,也是也想自己写一个,参考了下经理的代码,自己写了个

其具体思路就是,实现 BeanDefinitionRegistryPostProcessor 接口,在 ioc注入的时候,读取 配置文件,进行动态注入。

注入方法和原来的java配置mybatis 一样,先创建 DataSource,然后 sqlSessionFactory,然后transaction。

yml 格式:

mybatis:
  datasource:
    config[0]:
      # druid 监控类
      type: com.alibaba.druid.pool.DruidDataSource
      # 数据库连接驱动
      driver-class-name: com.mysql.jdbc.Driver
      # 数据库连接地址
      url: jdbc:mysql://127.0.0.1:3306/zhangzq?useUnicode=true&characterEncoding=utf-8&useSSL=false&useLegacyDatetimeCode=false&serverTimezone=Hongkong&zeroDateTimeBehavior=convertToNull
      username: root
      password: xxxx
      # tkmybatis 的扫描器,不写默认 MyBatis 的扫描器
      mapper-scanner: tk.mybatis.spring.mapper.MapperScannerConfigurer
      # mapper.java 所在包
      base-package: com.zzq.zhangzq.mapper
      # mapper.xml 所在包
      mapper-location: classpath:com/zzq/zhangzq/mapper/*.xml

默认 0 为主数据源,其他的为从数据源。从 config[0] 开始,config[1],config[2]....

代码参考:

import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import tk.mybatis.spring.mapper.MapperScannerConfigurer;

import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Configuration
public class MyBatisAutoConfiguration implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {

    private static String PREFIX = "mybatis.datasource.config";

    private static final Logger logger = LoggerFactory.getLogger(MyBatisAutoConfiguration.class);

    private Environment environment;

    private Integer [] integers;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        HashSet set = new HashSet();
        Iterator<PropertySource<?>> iterator = ((ConfigurableEnvironment) this.environment).getPropertySources().iterator();

        while (iterator.hasNext()){
            PropertySource localPropertySource = (PropertySource)iterator.next();
            if( localPropertySource instanceof EnumerablePropertySource){
                for (String str1 : ((EnumerablePropertySource)localPropertySource).getPropertyNames()){
                    Matcher matcher = Pattern.compile("mybatis.datasource.config\\D+(\\d+)\\D+").matcher(str1);
                    if ( matcher.find() ){
                        set.add( matcher.group(1) );
                    }
                }
            }
        }

        this.integers = new Integer[set.size()];
        int i = 0;
        for (Object o: set) {
            this.integers[i++] = Integer.valueOf(  o.toString() );
        }
        bind(beanDefinitionRegistry);

        for (String name: beanDefinitionRegistry.getBeanDefinitionNames()) {
            logger.info("bean name :" + name );
        }

    }

    public void bind(BeanDefinitionRegistry beanDefinitionRegistry){
        for (Integer i: this.integers) {
            bindDataSource(i , beanDefinitionRegistry);
            bindSqlSessionFactory(i , beanDefinitionRegistry);
            bindMapperScaner(i , beanDefinitionRegistry);
            bindTransation(i , beanDefinitionRegistry);
        }
    }

    public void bindTransation(int i , BeanDefinitionRegistry beanDefinitionRegistry){
        String tranName = "transactionManager" + i;
        RootBeanDefinition transaction = new RootBeanDefinition(DataSourceTransactionManager.class);
        transaction.getPropertyValues()
                .add("dataSource", new RuntimeBeanReference("datasource" + i ));

        if( i == 0 ){
            transaction.setPrimary(true);
        }

        beanDefinitionRegistry.registerBeanDefinition(tranName, transaction);
    }

    public void bindMapperScaner(int i , BeanDefinitionRegistry beanDefinitionRegistry){
        String mapperScanner = this.environment.getProperty(MyBatisAutoConfiguration.PREFIX + "[" + i + "].mapper-scanner");
        String basePackage = this.environment.getProperty(MyBatisAutoConfiguration.PREFIX + "[" + i + "].base-package");

        String sqlSessionFactoryName = "sqlSessionFactory" + i;

        Object clazz = "";
        try {
            clazz = Class.forName(mapperScanner);
        } catch (NullPointerException e){
            clazz = MapperScannerConfigurer.class;
        } catch (ClassNotFoundException e) {
            clazz = MapperScannerConfigurer.class;
        }

        RootBeanDefinition mapperScannerObject = new RootBeanDefinition((Class) clazz);
        mapperScannerObject.getPropertyValues()
                .add("basePackage", basePackage)
                .add("sqlSessionFactoryBeanName", sqlSessionFactoryName);

        if( i == 0 ){
            mapperScannerObject.setPrimary(true);
        }

        beanDefinitionRegistry.registerBeanDefinition("mapperScannerConfigurer" + i, mapperScannerObject);
    }

    public void bindSqlSessionFactory(int i , BeanDefinitionRegistry beanDefinitionRegistry){
        String mapperLocations = this.environment.getProperty(MyBatisAutoConfiguration.PREFIX + "[" + i + "].mapper-location");

        String sqlSessionFactoryName = "sqlSessionFactory" + i;

        RootBeanDefinition sqlSession = new RootBeanDefinition(SqlSessionFactoryBean.class);
        try {
            sqlSession.getPropertyValues()
                    .add("dataSource", new RuntimeBeanReference("datasource" + i ))
                    .add("vfs", SpringBootVFS.class)
                    .add("mapperLocations", new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        } catch (IOException e) {
            logger.error(sqlSessionFactoryName + " bind fail!!!");
        }

        if( i == 0 ){
            sqlSession.setPrimary(true);
        }

        beanDefinitionRegistry.registerBeanDefinition(sqlSessionFactoryName, sqlSession);
    }

    public void bindDataSource(Integer paramInt, BeanDefinitionRegistry beanDefinitionRegistry){
        String driverClassName = this.environment.getProperty(MyBatisAutoConfiguration.PREFIX + "[" + paramInt + "].driver-class-name");
        String url = this.environment.getProperty(MyBatisAutoConfiguration.PREFIX + "[" + paramInt + "].url");
        String username = this.environment.getProperty(MyBatisAutoConfiguration.PREFIX + "[" + paramInt + "].username");
        String password = this.environment.getProperty(MyBatisAutoConfiguration.PREFIX + "[" + paramInt + "].password");

        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(DruidDataSource.class);
        if( paramInt==0 ){
            rootBeanDefinition.setPrimary(true);
        }

        rootBeanDefinition.getPropertyValues()
                .add("driverClassName",driverClassName)
                .add("url",url)
                .add("username",username)
                .add("password",password)
                .add("filters","stat");
        beanDefinitionRegistry.registerBeanDefinition("datasource"+paramInt , rootBeanDefinition);

        logger.info( String.format("driverClassName %s  url %s  username %s  password %s" , driverClassName,url,username,password ) );
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        for (int i : this.integers) {
            DruidDataSource source = (DruidDataSource)configurableListableBeanFactory.getBean("datasource" + i );
            logger.info( source.getUrl() );
        };
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}

使用方法:

在启动类上排除myBatis ,jdbc 的自动配置

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
        DruidDataSourceAutoConfigure.class, JdbcTemplateAutoConfiguration.class})

然后启动即可,项目结构

自定义MyBatisAutoConfiguration,MyBatis的配置从未如此简单过~(实现自己的 start)