【SpringBoot】覆盖jar包中@Bean的方法

问题

项目启动类使用@Import引入公司封装的基础框架中的jar包中的MysqlConfig.class,里面封装了一些使用规范。

@SpringCloudApplication
@Import({WebBaseConfig.class, ServiceBaseConfig.class, MysqlConfig.class})
@EnableScheduling
public class ImServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(ImServiceApplication.class, args);
        BootstrapLogDoneFilter.bootstrapDone();
    }
}

nacos中配置基础框架中定义的配置项

xx.infra.common.env.deploy=dev
xx.infra.common.env.app-name=service-im-data
xx.mysql.url=xxxxx
xx.mysql.username=root
xx.mysql.password=123456
xx.infra.service.mybatis.aliases-package=com.xxx.service.im

MysqlConfig.java

@ComponentScan({"xx.yy.feature.mysql.mybatis"})
@EnableConfigurationProperties({XxMysqlProperties.class})
public class MysqlConfig {
    public MysqlConfig() {}

    @Bean(name = {"mysqlSqlSessionFactory"})
    public SqlSessionFactory mysqlSqlSessionFactory(DruidDataSource mysqlDataSource, XxInfraServiceProperties baseProperties) throws Exception {
        SqlSessionFactory factory = ConfigFactory.makeSqlSessionFactory(baseProperties, mysqlDataSource, MySqlDialect.class);
        return factory;
    }
    
	//......
}
public class BaseDaoConfigFactory {
    public BaseDaoConfigFactory() {}

    public static SqlSessionFactory makeSqlSessionFactory(XxInfraServiceProperties baseProperties, DataSource dataSource, Class<? extends Dialect> type) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        VFS.addImplClass(SpringBootVFS.class);
        Properties properties = new Properties();
        properties.setProperty("cacheEnabled", Boolean.toString(baseProperties.getMybatis().isCacheEnabled()));
        properties.setProperty("defaultStatementTimeout", Integer.toString(baseProperties.getMybatis().getDefaultStatementTimeout()));
        bean.setConfigurationProperties(properties);
        BaseInterceptor multiDataSourceInterceptor = new BaseInterceptor();
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        Properties pageProperties = new Properties();
        pageProperties.setProperty("dialectClass", type.getName());
        paginationInterceptor.setProperties(pageProperties);
        List<Interceptor> pluginList = CommonListUtil.NEW(new Interceptor[]{multiDataSourceInterceptor, paginationInterceptor});
        if (baseProperties.getMybatis().isStatSql()) {
            SqlStatInterceptor.turnOn();
            pluginList.add(new SqlStatInterceptor());
        }

        if (CommonListUtil.isNotEmpty(ServiceBaseConfig.getCustomizeMybatisInterceptors())) {
            pluginList.addAll(ServiceBaseConfig.getCustomizeMybatisInterceptors());
        }

        bean.setPlugins((Interceptor[])pluginList.toArray(new Interceptor[0]));
        bean.setDataSource(dataSource);
        bean.setTypeAliasesPackage(baseProperties.getMybatis().getAliasesPackage());
        bean.setTypeAliasesSuperType(Entity.class); // 这里写死了superType,很烦,我要想办法去掉这个限制~
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        bean.setMapperLocations(resolver.getResources(baseProperties.getMybatis().getMapperLocation()));
        SqlSessionFactory factory = bean.getObject();
        factory.getConfiguration().setCacheEnabled(baseProperties.getMybatis().isCacheEnabled());
        factory.getConfiguration().setDefaultStatementTimeout(baseProperties.getMybatis().getDefaultStatementTimeout());
        return factory;
    }
    
	//......
}

由于jar包中MysqlConfig的限制,只能为superType是基础框架中定义的Entity的类注册typeAlias,但是我现在项目中写的实体类都没有继承自Entity,所以mapper文件还是得写实体类的全路径,很烦~

<insert id="insertSelective" parameterType="com.xx.service.im.model.ImUserModel">
        insert into t_im_user
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="userId != null">
                FUSER_ID,
            </if>
            <if test="userSig != null">
                FUSER_SIG,
            </if>
        </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
        <if test="userId != null">
            #{userId},
        </if>
        <if test="userSig != null">
            #{userSig},
        </if>
    </trim>
</insert >

分析

方案一:allow-bean-definition-overriding

spring:
    main:
        allow-bean-definition-overriding: true # 允许bd覆盖
/**
 * @description: 先设置allow-bean-definition-overriding=true,然后在@Import{@link Xx.infra.feature.mysql.MysqlConfig}后再导入该类,从而覆盖其中的mysqlSqlSessionFactory
 * @author: panxili
 * @date: 2021/11/12
 */
public class MyMysqlConfig {
    /**
     * 改造{@link Xx.infra.feature.mysql.MysqlConfig#mysqlSqlSessionFactory},使得mybatis注册类别名时,取消对superType是{@link Xx.infra.common.model.Entity}的限制
     *
     * @param mysqlDataSource
     * @param baseProperties
     * @return
     * @throws Exception
     */
    @Bean(name = {"mysqlSqlSessionFactory"})
    public SqlSessionFactory mysqlSqlSessionFactory(DruidDataSource mysqlDataSource, XxInfraServiceProperties baseProperties) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        VFS.addImplClass(SpringBootVFS.class);
        Properties properties = new Properties();
        properties.setProperty("cacheEnabled", Boolean.toString(baseProperties.getMybatis().isCacheEnabled()));
        properties.setProperty("defaultStatementTimeout", Integer.toString(baseProperties.getMybatis().getDefaultStatementTimeout()));
        bean.setConfigurationProperties(properties);
        BaseInterceptor multiDataSourceInterceptor = new BaseInterceptor();
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        Properties pageProperties = new Properties();
        pageProperties.setProperty("dialectClass", MySqlDialect.class.getName());
        paginationInterceptor.setProperties(pageProperties);
        List<Interceptor> pluginList = CommonListUtil.NEW(new Interceptor[]{multiDataSourceInterceptor, paginationInterceptor});
        if (baseProperties.getMybatis().isStatSql()) {
            SqlStatInterceptor.turnOn();
            pluginList.add(new SqlStatInterceptor());
        }

        if (CommonListUtil.isNotEmpty(ServiceBaseConfig.getCustomizeMybatisInterceptors())) {
            pluginList.addAll(ServiceBaseConfig.getCustomizeMybatisInterceptors());
        }

        bean.setPlugins((Interceptor[])pluginList.toArray(new Interceptor[0]));
        bean.setDataSource(mysqlDataSource);
        bean.setTypeAliasesPackage(baseProperties.getMybatis().getAliasesPackage());
        // 取消对superType的限制
        // bean.setTypeAliasesSuperType(Entity.class);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        bean.setMapperLocations(resolver.getResources(baseProperties.getMybatis().getMapperLocation()));
        SqlSessionFactory factory = bean.getObject();
        factory.getConfiguration().setCacheEnabled(baseProperties.getMybatis().isCacheEnabled());
        factory.getConfiguration().setDefaultStatementTimeout(baseProperties.getMybatis().getDefaultStatementTimeout());
        return factory;
    }
}
@SpringCloudApplication
@Import({WebBaseConfig.class, ServiceBaseConfig.class, MysqlConfig.class, MyMysqlConfig.class})
@EnableScheduling
public class ImServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(ImServiceApplication.class, args);
        BootstrapLogDoneFilter.bootstrapDone();
    }
}

方案二:BeanFactoryPostProcessor

允许bd覆盖,可能会有隐患,SpringBoot默认也是false

@Component
@Slf4j
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    /**
     * 可以修改应用程序上下文的内部bd注册表,即对bd注册表进行crud
     * @param registry
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        log.debug("MyBeanDefinitionRegistryPostProcessor...postProcessBeanDefinitionRegistry...");
        final var sqlSessionFactoryBeanName = "mysqlSqlSessionFactory";
        final var sqlSessionFactoryBeanName2 = "mysqlSqlSessionFactory2";
        // 若jar包和程序都注册了sqlSessionFactory,就将程序里的bd覆盖掉jar包里的bd(继续使用jar包里的beanName,防止jar包中其它bean指定依赖了这个beanName)
        if (registry.containsBeanDefinition(sqlSessionFactoryBeanName) && registry.containsBeanDefinition(sqlSessionFactoryBeanName2)) {
            registry.removeBeanDefinition(sqlSessionFactoryBeanName);
            registry.registerBeanDefinition(sqlSessionFactoryBeanName, registry.getBeanDefinition(sqlSessionFactoryBeanName2));
            registry.removeBeanDefinition(sqlSessionFactoryBeanName2);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        log.debug("MyBeanDefinitionRegistryPostProcessor...postProcessBeanFactory...");
        final var beanDefinition = beanFactory.getBeanDefinition("mysqlSqlSessionFactory");
    }
}
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值