Spring5学习笔记05--BeanFactory后处理器

内容概要:

  • BeanFactory 后处理器的作用: 为 BeanFactory 提供扩展
    • 为 BeanFactory 补充一些bean的定义,用于解析 @Configuration @Component @Bean…
  • 常见的 BeanFactory 后处理器
    • ConfigurationClassPostProcessor
  • 准备工作,定义Bean 以及配置类
@Configuration
@ComponentScan("com.spring.learn.component")
public class Config {

    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }
}

@Slf4j
class Bean1 {

    public Bean1() {
        log.debug("初始化 Bean1");
    }
  
}

// com.spring.learn.component 包下的 Bean2 Bean3 Bean4
@Slf4j
@Component
public class Bean2 {

    public Bean2() {
        log.debug("初始化 Bean2");
    }
}

@Slf4j
@Controller
public class Bean3 {

    public Bean3() {
        log.debug("初始化 Bean3");
    }
}

@Slf4j
public class Bean4 {

    public Bean4() {
        log.debug("初始化 Bean4");
    }
}

ConfigurationClassPostProcessor

  • 直接注册Config类,没有添加BeanFactory 后处理器
    • config里的 @ComponentScan @Bean 注解都没有解析生效
public static void test(String[] args) {
  GenericApplicationContext context = new GenericApplicationContext();
  context.registerBean("config", Config.class);

  // 初始化容器
  context.refresh(); // 用于初始化所有单例,执行BeanFactory后处理器和Bean后处理器

  // 只有config的BeanDefinition, config里的 @ComponentScan @Bean 注解都没有解析生效
  for (String beanDefinitionName : context.getBeanDefinitionNames()) {
    System.out.println(beanDefinitionName); // 只有config
  }

  // 销毁容器
  context.close();
}
  • ConfigurationClassPostProcessor: BeanFactory 后处理器,可以解析 @ComponentScan @Bean @Import @ImportResource…等注解
public static void testConfigurationClassPostProcessor(String[] args) {
  GenericApplicationContext context = new GenericApplicationContext();
  context.registerBean("config", Config.class);
  // 添加BeanFactory后处理器,可以解析 @ComponentScan @Bean @Import @ImportResource...
  context.registerBean(ConfigurationClassPostProcessor.class);

  // 初始化容器
  context.refresh(); // 用于初始化所有单例,执行BeanFactory后处理器和Bean后处理器

  // 5个的BeanDefinition,config里的 @ComponentScan @Bean 注解有解析生效
  for (String beanDefinitionName : context.getBeanDefinitionNames()) {
    System.out.println(beanDefinitionName); // config、ConfigurationClassPostProcessor、bean2、bean3、bean1
  }

  // 销毁容器
  context.close();
}

@ComponentScan 注解解析执行过程

  • 以解析 @ComponentScan 为例,分析ConfigurationClassPostProcessor BeanFactory 后处理器的执行过程
  • @ComponentScan: 扫描指定包下添加了 @Component 注解的Bean,并将Bean管理到Spring容器中(即将Bean的定义注册到BeanFactory里)
    • 根据配置的basePackage ,Spring 最终是以文件的方式获取资源,如 com.spring.learn.component -> classpath*:com/spring/learn/component/**/*.class (** 是指所有子包)
public static void testComponentScan(String[] args) throws IOException {
  GenericApplicationContext context = new GenericApplicationContext();
  context.registerBean("config", Config.class);

  // 查找某各类上有没有指定的注解
  ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);

  // 以解析 @ComponentScan 为例,扫描指定包下添加了 @Component 注解的Bean,并将Bean管理到Spring容器中(即将Bean的定义注册到BeanFactory里)
  if (componentScan != null) {
    // 获取 @ComponentScan 扫描的包,扫描包下的所有class文件
    for (String basePackage : componentScan.basePackages()) {
      System.out.println(basePackage);

      // Spring 最终是以文件的方式获取资源,如 com.spring.learn.component -> classpath*:com/spring/learn/component/**/*.class (** 是指所有子包)
      String path = "classpath*:" + basePackage.replace(".", "/") + "/**/*.class";
      System.out.println(path);
      // 用于读取类(*.class文件)的元信息,包括类的名字、注解等信息
      CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
      // 获取文件资源
      Resource[] resources = context.getResources(path);

      // 根据注解获取BeanName,向BeanFactory中注册Bean的定义时,需要beanName,所以用到了这个类
      AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
      DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

      for (Resource resource : resources) {
        System.out.println(resource);
        MetadataReader reader = factory.getMetadataReader(resource);
        System.out.println("类名:" + reader.getClassMetadata().getClassName());
        // 判断类上是否直接添加了指定注解
        AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
        System.out.println("是否添加了 @Component 注解:" + annotationMetadata.hasAnnotation(Component.class.getName()));
        // 判断类上是否添加了指定注解的派生注解(只添加了注解,该方法返回false)
        // 派生注解: 如 @Controller 注解上有 @Component,则类上添加 @Controller 注解就是添加了 @Component 派生注解
        System.out.println("是否添加了 @Component 派生注解:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));

        if (annotationMetadata.hasAnnotation(Component.class.getName()) || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
          // 生成当前类的BeanDefinition,将Bean的定义注册到BeanFactory里
          AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();
          String beanName = beanNameGenerator.generateBeanName(beanDefinition, beanFactory);
          beanFactory.registerBeanDefinition(beanName, beanDefinition);
        }
      }
    }

  }

  // 初始化容器
  context.refresh(); // 用于初始化所有单例,执行BeanFactory后处理器和Bean后处理器

  System.out.println(">>>>>>>>>>");
  // 3个的BeanDefinition,@ComponentScan 注解有解析生效
  for (String beanDefinitionName : context.getBeanDefinitionNames()) {
    System.out.println(beanDefinitionName); // config bean2 bean3
  }

  // 销毁容器
  context.close();
}
自定义 @ComponentScan 注解解析的BeanFactory后处理器
  • 方式一: 实现 BeanFactoryPostProcessor, 重写postProcessBeanFactory() 方法,实现上面的功能
    • postProcessBeanFactory() 在执行context.refresh()时会回调这个方法调用
  • 方式二:实现 BeanFactoryPostProcessor 的子接口 BeanDefinitionRegistryPostProcessor, 重写 postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) 方法
/**
 * 根据 TestBeanFactoryPostProcessor.testComponentScan() 方法,自定义一个解析 @ComponentScan 注解的BeanFactory后处理器
 * 实现 BeanFactoryPostProcessor(或者其子接口 BeanDefinitionRegistryPostProcessor), 重写postProcessBeanFactory() 方法,在执行context.refresh()时会回调这个方法调用
 */
public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {

    /**
     * 执行时机: 执行context.refresh()时,会调用该方法
     * @param configurableListableBeanFactory
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

        try {
            // 查找某各类上有没有指定的注解
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);

            // 以解析 @ComponentScan 为例,扫描指定包下添加了 @Component 注解的Bean,并将Bean管理到Spring容器中(即将Bean的定义注册到BeanFactory里)
            if (componentScan != null) {
                // 获取 @ComponentScan 扫描的包,扫描包下的所有class文件
                for (String basePackage : componentScan.basePackages()) {
                    System.out.println(basePackage);

                    // Spring 最终是以文件的方式获取资源,如 com.spring.learn.component -> classpath*:com/spring/learn/component/**/*.class (** 是指所有子包)
                    String path = "classpath*:" + basePackage.replace(".", "/") + "/**/*.class";
                    // 用于读取类(*.class文件)的元信息,包括类的名字、注解等信息
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                    // 获取文件资源
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);

                    // 根据注解获取BeanName,向BeanFactory中注册Bean的定义时,需要beanName,所以用到了这个类
                    AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
                    DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;

                    for (Resource resource : resources) {
                        MetadataReader reader = factory.getMetadataReader(resource);
                        AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                        // 判断类上是否直接添加了指定注解 / 派生注解
                        // 派生注解: 如 @Controller 注解上有 @Component,则类上添加 @Controller 注解就是添加了 @Component 派生注解
                        if (annotationMetadata.hasAnnotation(Component.class.getName()) || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                            // 生成当前类的BeanDefinition,将Bean的定义注册到BeanFactory里
                            AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();
                            String beanName = beanNameGenerator.generateBeanName(beanDefinition, beanFactory);
                            beanFactory.registerBeanDefinition(beanName, beanDefinition);
                        }
                    }
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

@Bean 注解解析执行过程

  • 添加 @Configuration 注解的类可以看做是个Bean工厂类, @Bean注解标注的方法充当的就是工厂方法
  • 下面这种方式生成BeanDefinition, Bean初始化是直接通过默认构造器创建的
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(beanMethod.getReturnTypeName()).getBeanDefinition();
  • 如果要使用工厂方法来创建Bean,要设置工厂方法名 以及 工厂类名
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition()
                    .setFactoryMethodOnBean(factoryMethodName, factoryBeanName)
                    .getBeanDefinition();
  • @Bean 方式可以传入参数,解析这个参数需要在 BeanDefinitionBuilder 中指定自动装配模式
build.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
  • 配置类
@Configuration
@ComponentScan("com.huat.lisa.studyspring.s05.component")
public class Config {

    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean(initMethod = "init")
    public DruidDataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }
}
public static void testBean(String[] args) throws IOException {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean("config", Config.class);

    // 以解析 @Bean 为例, 如何将Bean添加到Spring容器中进行管理,又如何初始化以及获取Bean对象
    // 根据class文件读取 @Configuration 注解标注的类(Config类)的元信息,根据配置类元信息,获取所有 @Bean 注解标注的方法
    // 这种方式获取类信息不会使用反射,效率更高,更推荐这种方式
    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
    MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/huat/lisa/studyspring/s05/Config.class"));

    DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

    AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
    // 获取使用 @Bean 注解标注的所有方法,将方法返回的Bean注册到BeanFactory
    Set<MethodMetadata> beanMethods = annotationMetadata.getAnnotatedMethods(Bean.class.getName());
    for (MethodMetadata beanMethod : beanMethods) {
        System.out.println(beanMethod + "--" + beanMethod.getDeclaringClassName() + "--" + beanMethod.getMethodName() + "--" + beanMethod.getReturnTypeName());
        String initMethodName = beanMethod.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
        // 设置工厂方法名 工厂类名,创建Bean的定义,告诉BeanFactory要通过哪个工厂方法来创建Bean
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition()
            .setFactoryMethodOnBean(beanMethod.getMethodName(), "config")
            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); // 设置自动装配模式
        if (initMethodName.length() > 0) {
            builder.setInitMethodName(initMethodName); // 设置初始化方法
        }
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        beanFactory.registerBeanDefinition(beanMethod.getMethodName(), beanDefinition);
    }
  
    // 初始化容器,用于初始化所有单例,执行BeanFactory后处理器和Bean后处理器
    context.refresh();

    // config bean1 sqlSessionFactoryBean dataSource, 其中sqlSessionFactoryBean设置dataSource成功,dataSource设置db连接信息成功,也执行了init方法
    for (String beanDefinitionName : context.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }

    context.close();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值