美文网首页一些收藏spring
Spring原理分析-BeanFactory后处理器

Spring原理分析-BeanFactory后处理器

作者: 石头耳东 | 来源:发表于2022-04-12 17:44 被阅读0次

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

    零、本文纲要

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

    一、基础准备

    0、基础依赖

    <!--base-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--mybatis_plus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.3</version>
    </dependency>
    <!--druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.8</version>
    </dependency>
    

    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<MethodMetadata> 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<MethodMetadata> 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> mapper1(SqlSessionFactory sqlSessionFactory){
        MapperFactoryBean<Mapper1> 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后处理器

        本文链接:https://www.haomeiwen.com/subject/bjggsrtx.html