设计第三方jar包中有bean对象时,要如何自动加载到被引用的应用中(EnableAutoConfiguration、BeanDefinitionRegistryPostProcessor使用)

1. 前言

  1. 之前工作的时候被临时借去其他小组帮忙开发项目,在其他小组中发现他们的项目结构有点意思,他们是把很多可以单独模块写成类似第三方工具jar包,每个模块只干好自己的事情,然后把这个模块写成jar包[SDK]

  2. 有些人可能会说,这跟第三方工具类没啥区别,是的,也就是所谓的抽取,但这是业务方面的抽取,而不是工具方面的抽取,这个业务方面的抽取,里面可以是有业务有bean,而当某个应用需要该业务,则只需要引入该业务的SDK,即可开封即用

  3. 举个例子,把UserDao层的逻辑封装到jar包,比如A应用需要校验用户,则引用jar包并使用UserDao.checkUser(),B应用需要校验用户,则也引用jar并使用UserDao.checkUser()。如下图:
    在这里插入图片描述
    如上图,自身项目是没任何有关dao层、mapper文件相关的东西,它只是引入了jar包,从而完成自身需要的需求。

  4. 根据实现上述的功能,必然jar中的UserService、UserDao是bean对象,从而引入以下几个问题:

    1. jar包中的bean对象要如何被引用的应用加载到自身的bean容器中可以被使用呢?
    2. 有的人会说,简单啦,在应用启动类上加上@ComponentScan(xxxx),是的,这也是一种解决方法。但既然是第三方jar包,解决上述问题应该从自身角度思考,否则每次被其他应用引用,应用自身需要加额外的配置@ComponentScan(xxxx),属实体验性不佳

2. 先说结论

  1. 使用@ComponentScan、@MapperScan指定需要扫描路径
    1. 需要使用方自行补充到项目的启动类上
  2. 使用EnableAutoConfiguration自动装配 + @Configuration
    1. 第三方jar包中编写,将需要生成的bean对象,编写到被@Configuration注释的类A上。
    2. 再使用EnableAutoConfiguration自动装配 将类A生成bean对象,然后类A再将自身中@Bean的生成bean对象。
    3. Dao层是接口没办法new对象生成对象,因此使用方还是需要自行补充@MapperScan到项目的启动类上。
  3. 使用EnableAutoConfiguration自动装配 + BeanDefinitionRegistryPostProcessor
    1. 第三方jar包中编写,将要扫描的包路径写好到类A上。

    2. 再使用EnableAutoConfiguration自动装配 将类A生成bean对象,然后类A再对指定的包路径进行扫描补充beanDefinition到Registry中。

    3. 扫描的可以是被@Component与@Mapper注解的类,因此使用方不需要补充任何扫描的注解。真正做到引用即用。

      public class AutoConfig implements BeanDefinitionRegistryPostProcessor {
      
          // 指定需要扫描的路径
          private static final String[] SCAN_PACKAGES = {"com.example"};
      
          @Override
          public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
              // 扫描@Component的类
              ClassPathBeanDefinitionScanner pathScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry);
              pathScanner.scan(SCAN_PACKAGES);
      
              // 扫描@Mapper的接口类
              ClassPathMapperScanner mapperScanner
                      = new ClassPathMapperScanner(beanDefinitionRegistry);
              mapperScanner.scan(SCAN_PACKAGES);
          }
      
          @Override
          public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
              // 这里涉及到beanFactory 对这里无需关注
              // 即 空实现
          }
      }
      

3. 代码解释

1. 使用@ComponentScan指定需要扫描路径[不推荐]

  1. 我们知道一个spring项目启动的时候,会扫描启动类所在包下以及其子包下的类是否加入@Component @Service 等这些生成bean的注解,从而将这些类生成bean对象放入spring容器中。
  2. 而如果要扫描其他指定的包路径,可以在启动类上使用**@ComponentScan指定需要扫描路径**
  3. 这种方式确实可以指定扫描第三方jar包的包路径,对应生成bean对象。
  4. 不足:每次新的应用使用该jar包,都必须在启动类加上@ComponentScan指定需要扫描路径。
  5. 代码如下:
    在这里插入图片描述
    由于这里的jar包内部还使用了mybatis与数据库进行交互,因此还需要加上@MapperScan注解,扫描被注解@Mapper的接口

2. 使用EnableAutoConfiguration自动装配 + @Configuration

  1. 先了解:EnableAutoConfiguration自动装配
  2. 根据上述第一点文章,我们可以知道,EnableAutoConfiguration自动装配作用即把指定的类构造成对象,并放入spring容器中,使其成为bean对象
    在这里插入图片描述
  3. 由于可能bean对象很多,可能需要写类路径很多,嫌弃到时候看一堆路径麻烦,可以配合使用@Configuration + @Bean
    在这里插入图片描述
  4. 该jar包中与数据库进行了交互,而dao层又是接口没办法使用@Bean创建对象呀,因此在项目中仍然要使用@MapperScan注解
    在这里插入图片描述

3. 使用EnableAutoConfiguration自动装配 + BeanDefinitionRegistryPostProcessor

  1. 首先得知道spring在启动的时候,是会扫描项目中的类,判断类是否是个bean,如果是个bean会先生成BeanDefinition[bean的定义信息],然后会根据该定义信息生成bean对象

  2. 还需要知道XXXPostProcessor 其实是bean的后置处理器,作用:可以在bean实例化的时候执行自己加上的逻辑

  3. 这里使用BeanDefinitionRegistryPostProcessor作用:为了补充剩下没被扫描到的BeanDefinition信息

  4. 快速入门:

    1. 准备个bean类,在项目扫描之外,如下:
      在这里插入图片描述

    2. 编写实现BeanDefinitionRegistryPostProcessor的类,如下:

      @Component
      public class TestDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
      
          // 指定需要扫描的路径
          private static final String[] SCAN_PACKAGES = {"com.example.out"};
      
          @Override
          public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
              
              // 使用扫描类,将扫描到的BeanDefinition加入到BeanDefinitionRegistry里面
              ClassPathBeanDefinitionScanner pathScanner = new ClassPathBeanDefinitionScanner(registry);
              // 扫描刚刚指定的包路径
              pathScanner.scan(SCAN_PACKAGES);
          }
      
          @Override
          public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      
          }
      }
      

      在这里插入图片描述

    3. 编写测试代码
      在这里插入图片描述
      在这里插入图片描述

  5. 上述快速入门,是将把在项目外的包的BeanDefinition信息补充到BeanDefinitionRegistry,从而可以实现生成对应的bean对象了。

  6. 注意

    1. 是把项目外的BeanDefinition信息补充,补充,补充,什么是补充,也就是该类自身要被spring认为它是bean对象,因此还是需要在OutBean1的类上加上@Component@Service等注解
    2. 而像EnableAutoConfiguration、或者@Bean 都是将一个类变成bean对象,但这个类上可以不用加@Component@Service等注解,因为使用EnableAutoConfiguration、或者@Bean 是默认你这个类就是bean了。
  7. 因此,在第三方jar包这里,对,大干一场。

  8. 第三方jar包里,我们加上如下代码
    在这里插入图片描述

    public class AutoConfig implements BeanDefinitionRegistryPostProcessor {
    
        private static final String[] SCAN_PACKAGES = {"com.example"};
    
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        	// 扫描@Component的类
            ClassPathBeanDefinitionScanner pathScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry);
            pathScanner.scan(SCAN_PACKAGES);
    		// 扫描@Mapper的类
            ClassPathMapperScanner mapperScanner
                    = new ClassPathMapperScanner(beanDefinitionRegistry);
            mapperScanner.scan(SCAN_PACKAGES);
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
            // 这里涉及到beanFactory 对这里无需关注
            // 即 空实现
        }
    }
    
  9. 因为上述类要先被创建为bean对象才会执行postProcessBeanDefinitionRegistry方法,因此需要先让它变成bean对象,又在项目包路径之外,当然是使用EnableAutoConfiguration来配合啦。

  10. 使用EnableAutoConfiguration来创建上述类为bean对象

    	org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
        com.example.jardemo.config.AutoConfig
    

    在这里插入图片描述

  11. 项目启动类上,无需加上任何扫描的注解,因为BeanDefinitionRegistryPostProcessor类都进行扫描了。
    在这里插入图片描述

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值