Spring原理分析-BeanFactory后处理器

前置文章:
一、Spring原理分析-BeanFactory与ApplicationContext
二、Spring原理分析-Bean生命周期
三、Spring原理分析-Bean后处理器

零、本文纲要

  • 一、基础准备
  • 二、ConfigurationClassPostProcessor后处理器
  • 三、MapperScannerConfigurer后处理器
  • 四、深入ConfigurationClassPostProcessor后处理器-解析@ComponentScan的实现
  • 五、深入ConfigurationClassPostProcessor后处理器-解析@Bean的实现
  • 六、深入Mapper接口扫描添加后处理器

一、基础准备

0、基础依赖



    org.springframework.boot
    spring-boot-starter



    org.springframework.boot
    spring-boot-starter-web



    org.springframework.boot
    spring-boot-starter-test
    test



    mysql
    mysql-connector-java



    com.baomidou
    mybatis-plus-boot-starter
    3.4.3



    com.alibaba
    druid-spring-boot-starter
    1.2.8

1、待注入类

  • ① com.stone.demo05.Bean1
public class Bean1 {
    private static final Logger log = LoggerFactory.getLogger(Bean1.class);
    public Bean1() {log.debug("Bean1被Spring管理啦!");}
}
  • ② com.stone.demo05.component.Bean2
@Component
public class Bean2 {
    private static final Logger log = LoggerFactory.getLogger(Bean2.class);
    public Bean2(){log.debug("Bean2被Spring容器管理了!");}
}
  • ③ com.stone.demo05.Config
@Configuration
@ComponentScan("com.stone.demo05.component")
public class Config {
    @Bean
    public Bean1 bean1(){return new Bean1();};

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

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

2、测试类

public class Demo05 {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);

        context.refresh();

        for (String definitionName : context.getBeanDefinitionNames()) {
            System.out.println(definitionName);
        }

        context.close();
    }
}
基础准备测试.png

可以发现,仅我们手动注册的bean被添加到Spring容器了。

二、ConfigurationClassPostProcessor后处理器

1、作用

解析@ComponentScan、@Bean、@Import、@ImportResource 注解。

2、添加ConfigurationClassPostProcessor后处理器

// 解析@ComponentScan、@Bean、@Import、@ImportResource 注解
context.registerBean(ConfigurationClassPostProcessor.class);
ConfigurationClassPostProcessor后处理器.png

三、MapperScannerConfigurer后处理器

1、作用

类似于@MapperScanner注解,将@Mapper注解指定的Bean放入Spring容器。

2、添加MapperScannerConfigurer后处理器

// @MapperScanner
context.registerBean(MapperScannerConfigurer.class, bd -> {
    bd.getPropertyValues().add("basePackage", "com/stone/demo05/mapper");
});
MapperScannerConfigurer后处理器.png

使用MapperScannerConfigurer后,同时我们也看到其他后处理器也被添加进来了,如上日志后面的Processor们。

四、深入ConfigurationClassPostProcessor后处理器-解析@ComponentScan的实现

1、获取类上的@ComponentScan注解及其属性

这里使用AnnotationUtils工具类来查找指定类上的@ComponentScan注解,并从注解中取出basePackages属性内容,如下:

ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null){
    for (String basePackage : componentScan.basePackages()) {
        System.out.println(basePackage);
    }
}
获取类上的@ComponentScan注解中的属性.png

2、获取指定路径下的资源

注意:此处内容跟我们Spring原理分析-BeanFactory与ApplicationContext中:一、容器接口 → 3、ApplicationContext接口 → ② 实现ResourcePatternResolver接口 的内容串联。

在资源路径下添加一个没有@Component注解的类,如下:

public class Bean3 {
    private static final Logger log = LoggerFactory.getLogger(Bean3.class);
    public Bean3(){log.debug("Bean2被Spring容器管理了!");}
}

从指定路径下获取资源,如下:

// com.stone.demo05.component → classpath*:com/stone/demo05/component/**/*.class
String path = PATH_PREFIX
        + basePackage.trim().replace(".", "/") + PATH_SUFFIX;
System.out.println(path);
Resource[] resources = context.getResources(path);
for (Resource resource : resources) {
    System.out.println(resource);
}

常量类,如下:

public class DemoConstants {
    public static final String PATH_PREFIX = "classpath*:";
    public static final String PATH_SUFFIX = "/**/*.class";
}
扫描指定路径下的资源文件.png

3、获取资源内部元信息

通过CachingMetadataReaderFactory类获取MetadataReader对象,来获取元信息数据,如下:

// 用户获取元信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
Resource[] resources = context.getResources(path);
for (Resource resource : resources) {
    //System.out.println(resource);
    // 加载元信息的 reader
    MetadataReader metadataReader = factory.getMetadataReader(resource);
    // 读取 类名 和 注解 信息
    System.out.println("CLASSNAME IS: " + metadataReader.getClassMetadata().getClassName());
    System.out.println("HAS @Component ? " +
            metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
    System.out.println("-------------------------");
}
获取元信息数据.png

由上可以知道hasAnnotation方法可以帮助我们判断是否存在指定注解,下面我们演示判断派生注解的hasMetaAnnotation方法,如下:

System.out.println("HAS MATE @Component ? " +
        metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));

给Bean3添加@Controller注解或者@Configuration注解,测试如下:

判断派生注解的hasMetaAnnotation方法.png

4、将包含指定元信息的资源转换成BeanDefinition并注册

获取BeanName生成器,如下:

// 设置 BeanName 的 generator
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();

通过采用如上的元信息获取形式,进行判断,然后注册,如下:

if (metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())
|| metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())){
    // 将带有指定 @Component 注解及其派生注解的资源转换成 BeanDefinition
    AbstractBeanDefinition bd = BeanDefinitionBuilder
            .genericBeanDefinition(metadataReader.getClassMetadata().getClassName())
            .getBeanDefinition();
    // 通过 beanFactory 将 BeanDefinition 注册到 Spring
    DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
    String beanName = generator.generateBeanName(bd, beanFactory);
    beanFactory.registerBeanDefinition(beanName, bd);
}
将包含指定元信息的资源转换成BeanDefinition并注册.png

可以看到,此时我们带有@Component注解的Bean2已经被Spring管理了。

5、封装自定义解析@ComponentScan注解BeanFactory后处理器

实现BeanFactoryPostProcessor接口,重写postProcessBeanFactory方法,该方法在 context.refresh() 时被调用,如下:

public class ComponentScanBothMetaPostProcessor implements BeanFactoryPostProcessor {
    @Override // 该方法在 context.refresh() 时被调用
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        try {
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            if (componentScan != null){
                for (String basePackage : componentScan.basePackages()) {
                    System.out.println(basePackage);
                    // com.stone.demo05.component → classpath*:com/stone/demo05/component/**/*.class
                    String path = PATH_PREFIX
                            + basePackage.trim().replace(".", "/") + PATH_SUFFIX;
                    System.out.println(path);
                    // 用户获取元信息
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                    // 设置 BeanName 的 generator
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    for (Resource resource : resources) {
                        // 加载元信息的 reader
                        MetadataReader metadataReader = factory.getMetadataReader(resource);
                        // 读取元信息,判断是否带有 @Component 注解及其派生注解
                        if (metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())
                                || metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())){
                            // 将带有指定 @Component 注解及其派生注解的资源转换成 BeanDefinition
                            AbstractBeanDefinition bd = BeanDefinitionBuilder
                                    .genericBeanDefinition(metadataReader.getClassMetadata().getClassName())
                                    .getBeanDefinition();
                            // 通过 DefaultListableBeanFactory 将 BeanDefinition 注册到 Spring
                            if (beanFactory instanceof DefaultListableBeanFactory defaultListableBeanFactory){
                                String beanName = generator.generateBeanName(bd, defaultListableBeanFactory);
                                defaultListableBeanFactory.registerBeanDefinition(beanName, bd);
                            }
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

测试类中注册该后处理器,测试:

封装自定义的BeanFactory后处理器测试.png

至此我们就大致清楚整个解析@Component注解实现的过程:
获取资源路径 → 通过路径获取资源 → 读取资源元信息 → 判断符合指定元信息需求的的资源 → 注册筛选出的资源

五、深入ConfigurationClassPostProcessor后处理器-解析@Bean的实现

1、读取指定路径资源元信息

// 读取指定资源的元信息数据
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
MetadataReader metadataReader
        = factory.getMetadataReader(new ClassPathResource("com/stone/demo05/Config.class"));

2、获取被@Bean注解标注的方法集合

// 获取被 @Bean 注解标注的方法集合
Set annotatedMethods
        = metadataReader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());

3、遍历方法集合并注册BeanDefinition

遍历方法集合并注册BeanDefinition报错.png

因为sqlSessionFactoryBean需要注入dataSource,而默认是关闭自动注入的,我们需要配置开启,如下:

// 遍历被注解的方法
for (MethodMetadata annotatedMethod : annotatedMethods) {
    String methodName = annotatedMethod.getMethodName();
    // 设置工厂方法,创建 BeanDefinition 并注册
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
    builder.setFactoryMethodOnBean(methodName, "config");
    // 开启 工厂方法 和 构造方法 的自动装配
    builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
    AbstractBeanDefinition bd = builder.getBeanDefinition();
    context.getDefaultListableBeanFactory().registerBeanDefinition(methodName, bd);
}

builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);通过该方法,开启工厂方法的自动装配。

开启工厂方法的自动装配.png

4、获取@Bean注解中的属性值

// 获取 @Bean 注解中 initMethod 属性值
String initMethod
        = annotatedMethod.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();

if (initMethod.length() > 0){
    // 如果包含 initMethod 则设置
    builder.setInitMethodName(initMethod);
}
获取@Bean注解中的属性值.png

5、封装自定义解析@Bean注解BeanFactory后处理器

public class AtBeanPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        try {
            // 读取指定资源的元信息数据
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            MetadataReader metadataReader
                    = factory.getMetadataReader(new ClassPathResource("com/stone/demo05/Config.class"));
            // 获取被 @Bean 注解标注的方法集合
            Set annotatedMethods
                    = metadataReader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
            // 遍历被注解的方法
            for (MethodMetadata annotatedMethod : annotatedMethods) {
                String methodName = annotatedMethod.getMethodName();
                // 获取 @Bean 注解中 initMethod 属性值
                String initMethod
                        = annotatedMethod.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
                // 设置工厂方法,创建 BeanDefinition 并注册
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
                builder.setFactoryMethodOnBean(methodName, "config");
                // 开启 工厂方法 和 构造方法 的自动装配
                builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                if (initMethod.length() > 0){
                    // 如果包含 initMethod 则设置
                    builder.setInitMethodName(initMethod);
                }
                AbstractBeanDefinition bd = builder.getBeanDefinition();
                if (beanFactory instanceof DefaultListableBeanFactory defaultListableBeanFactory){
                    defaultListableBeanFactory.registerBeanDefinition(methodName, bd);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

六、深入Mapper接口扫描添加后处理器

1、原生添加Mapper接口方法

@Bean
public MapperFactoryBean mapper1(SqlSessionFactory sqlSessionFactory){
    MapperFactoryBean factory = new MapperFactoryBean<>(Mapper1.class);
    factory.setSqlSessionFactory(sqlSessionFactory);
    return factory;
}
原生添加Mapper接口方法.png

2、自定义批量扫描添加Mapper接口后处理器

这次我们实现BeanDefinitionRegistryPostProcessor接口,它是BeanFactoryPostProcessor的子接口。

public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            // 找到指定类的 @MapperScan 注解
            MapperScan mapperScan = AnnotationUtils.findAnnotation(Config.class, MapperScan.class);
            if (mapperScan != null){
                String[] basePackages = mapperScan.basePackages();
                for (String basePackage : basePackages) {
                    // 通过资源绝对路径,解析资源
                    String path = PATH_PREFIX
                            + basePackage.trim().replace(".", "/") + PATH_SUFFIX;
                    PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
                    Resource[] resources = resolver.getResources(path);
                    // 通过 CachingMetadataReaderFactory 对象,从资源 resources 中获取元信息
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    for (Resource resource : resources) {
                        // 获取 resource 资源的元信息 MetadataReader
                        MetadataReader reader = factory.getMetadataReader(resource);
                        // 获取类元信息 classMetadata,生成并注册 BeanDefinition
                        ClassMetadata classMetadata = reader.getClassMetadata();
                        if (classMetadata.isInterface()) {
                            // MapperFactoryBean 的 BeanDefinition
                            AbstractBeanDefinition bd1 = BeanDefinitionBuilder
                                    .genericBeanDefinition(MapperFactoryBean.class)
                                    .addConstructorArgValue(classMetadata.getClassName())
                                    .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                                    .getBeanDefinition();
                            // Mapper 接口的 BeanDefinition
                            AbstractBeanDefinition bd2 = BeanDefinitionBuilder
                                    .genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
                            // 对应 Mapper 来生成 beanName
                            String beanName = generator.generateBeanName(bd2, beanFactory);
                            // 对应 MapperFactoryBean 来注册 BeanDefinition
                            beanFactory.registerBeanDefinition(beanName, bd1);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

值得注意的地方,如下:

// MapperFactoryBean 的 BeanDefinition
AbstractBeanDefinition bd1 = BeanDefinitionBuilder
        .genericBeanDefinition(MapperFactoryBean.class)
        .addConstructorArgValue(classMetadata.getClassName())
        .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
        .getBeanDefinition();
// Mapper 接口的 BeanDefinition
AbstractBeanDefinition bd2 = BeanDefinitionBuilder
        .genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
// 对应 Mapper 来生成 beanName
String beanName = generator.generateBeanName(bd2, beanFactory);
// 对应 MapperFactoryBean 来注册 BeanDefinition
beanFactory.registerBeanDefinition(beanName, bd1);

由于Mapper是接口,我们是通过MapperFactoryBean工厂Bean对象来获取的,所以其BeanDefinition的内容是MapperFactoryBean。而每个Bean需要有不同的BeanName,不然会先后覆盖。所以此处两次获取BeanDefinition,第二次是为了通过它获取Mapper的自己的BeanName命名。最后再将beanName与bd1注册到Spring。

七、结尾

以上即为BeanFactory后处理器的全部内容,感谢阅读。

你可能感兴趣的:(Spring原理分析-BeanFactory后处理器)