• Spring之BeanFactoryPostProcessor(bean工厂后置处理器)


    一句话:bean工厂后置处理器就是对bean实例化前的beanDefinition的注册和修改

    1、BeanFactoryPostProcessor的执行时机和作用

    准备一个bean对象

    @Component
    @Slf4j
    public class LifeCycleBean {
    
        public LifeCycleBean() {
            log.debug("bean生命周期>>>>>>>>>>>>>>>>>>>构造");
        }
    
        @PostConstruct
        public void init() {
            log.debug("bean生命周期>>>>>>>>>>>>>>>>>>>初始化");
        }
    
        @PreDestroy
        public void destroy() {
            log.debug("bean生命周期>>>>>>>>>>>>>>>>>>>销毁");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    bean后置处理器,bean初始化前后执行

    @Component
    @Slf4j
    public class MyBeanPostProcessor implements BeanPostProcessor {
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals("lifeCycleBean"))
                log.debug("初始化后置处理器>>>>>>> 初始化之前执行");
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals("lifeCycleBean"))
                log.debug("初始化后置处理器>>>>>>>  初始化之后执行");
            return bean;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    启动类:

    @SpringBootApplication
    public class Test {
        public static void main(String[] args) throws IOException {
            //创建容器
            ConfigurableApplicationContext context = SpringApplication.run(Test.class, args);
            //关闭容器
            context.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.1、BeanFactoryPostProcessor-bean工厂定义后置处理器

    • bean工厂后置处理器核心接口
    • 执行时机: 在bean调用构造函数,init方法,bean后处理器执行之前执行
    • 方法作用: 主要用来对bean定义做一些改变

    自定义bean工厂后置处理器:

    @Component
    @Slf4j
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            for(String name:beanFactory.getBeanDefinitionNames()) {
                if("lifeCycleBean".equals(name)) {
                    log.debug("BeanFactoryPostProcessor-->bean工厂后置处理器>>>>>>> bean定义之后,实例化之前执行");
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    1.2、BeanDefinitionRegistryPostProcessor-bean工厂定义注册后置处理器

    • BeanFactoryPostProcessor工厂后置处理器子接口
    • 执行时机: 在bean调用构造函数,init方法,bean后处理器执行,BeanFactoryPostProcessor-bean工厂后置处理器之前执行
    • 方法作用: 用来注册更多的bean到spring容器

    自定义bean工厂后置处理器:

    @Component
    @Slf4j
    public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            for(String name:beanFactory.getBeanDefinitionNames()) {
                if("lifeCycleBean".equals(name)) {
                    log.debug("BeanDefinitionRegistryPostProcessor-->bean工厂后置处理器>>>>>>> bean定义之后,实例化之前执行,对bean做一些改变");
                }
            }
        }
    
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
            for(String name:beanDefinitionRegistry.getBeanDefinitionNames()) {
                if("lifeCycleBean".equals(name)) {
                    log.debug("BeanDefinitionRegistryPostProcessor-->bean工厂后置处理器>>>>>>> bean定义之后,实例化之前执行,注册更多的bean到容器");
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    生命周期bean添加bean工厂后置处理器后输出结果:

    [INFO ] 22:09:09.210 [main] o.s.b.w.e.tomcat.TomcatWebServer    - Tomcat initialized with port(s): 8080 (http) 
    [INFO ] 22:09:09.234 [main] o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"] 
    [INFO ] 22:09:09.236 [main] o.a.catalina.core.StandardService   - Starting service [Tomcat] 
    [INFO ] 22:09:09.236 [main] o.a.catalina.core.StandardEngine    - Starting Servlet engine: [Apache Tomcat/9.0.53] 
    [INFO ] 22:09:09.413 [main] o.a.c.c.C.[Tomcat].[localhost].[/]  - Initializing Spring embedded WebApplicationContext 
    [INFO ] 22:09:09.413 [main] o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 3704 ms 
    [DEBUG] 22:09:09.557 [main] c.xc.a05.beanfactory.LifeCycleBean  - bean生命周期>>>>>>>>>>>>>>>>>>>构造 
    [DEBUG] 22:09:09.559 [main] c.x.a.b.MyBeanPostProcessor         - 初始化后置处理器>>>>>>> 初始化之前执行 
    [DEBUG] 22:09:09.560 [main] c.xc.a05.beanfactory.LifeCycleBean  - bean生命周期>>>>>>>>>>>>>>>>>>>初始化 
    [DEBUG] 22:09:09.560 [main] c.x.a.b.MyBeanPostProcessor         - 初始化后置处理器>>>>>>>  初始化之后执行 
    [INFO ] 22:09:09.995 [main] o.s.b.a.w.s.WelcomePageHandlerMapping - Adding welcome page: class path resource [static/index.html] 
    [INFO ] 22:09:10.249 [main] c.a.d.s.b.a.DruidDataSourceAutoConfigure - Init DruidDataSource 
    [INFO ] 22:09:10.584 [main] c.a.druid.pool.DruidDataSource      - {dataSource-1} inited 
    [INFO ] 22:09:11.129 [main] o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"] 
    [INFO ] 22:09:11.184 [main] o.a.c.c.C.[Tomcat].[localhost].[/]  - Initializing Spring DispatcherServlet 'dispatcherServlet' 
    [INFO ] 22:09:11.184 [main] o.s.web.servlet.DispatcherServlet   - Initializing Servlet 'dispatcherServlet' 
    [TRACE] 22:09:11.184 [main] o.s.web.servlet.DispatcherServlet   - Detected org.springframework.web.multipart.support.StandardServletMultipartResolver@4f7be6c8 
    [TRACE] 22:09:11.184 [main] o.s.web.servlet.DispatcherServlet   - Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@647b9364 
    [TRACE] 22:09:11.184 [main] o.s.web.servlet.DispatcherServlet   - Detected org.springframework.web.servlet.theme.FixedThemeResolver@b6bccb4 
    [TRACE] 22:09:11.188 [main] o.s.web.servlet.DispatcherServlet   - Detected DefaultRequestToViewNameTranslator 
    [TRACE] 22:09:11.188 [main] o.s.web.servlet.DispatcherServlet   - Detected SessionFlashMapManager 
    [DEBUG] 22:09:11.188 [main] o.s.web.servlet.DispatcherServlet   - enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data 
    [INFO ] 22:09:11.188 [main] o.s.web.servlet.DispatcherServlet   - Completed initialization in 4 ms 
    [INFO ] 22:09:11.192 [main] o.s.b.w.e.tomcat.TomcatWebServer    - Tomcat started on port(s): 8080 (http) with context path '' 
    [INFO ] 22:09:11.220 [main] com.xc.a05.beanfactory.Test         - Started Test in 6.616 seconds (JVM running for 8.194) 
    [INFO ] 22:09:12.418 [main] c.a.druid.pool.DruidDataSource      - {dataSource-1} closing ... 
    [INFO ] 22:09:12.420 [main] c.a.druid.pool.DruidDataSource      - {dataSource-1} closed 
    [DEBUG] 22:09:12.422 [main] c.xc.a05.beanfactory.LifeCycleBean  - bean生命周期>>>>>>>>>>>>>>>>>>>销毁 
    Disconnected from the target VM, address: '127.0.0.1:58089', transport: 'socket'
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    2、常用Bean工厂后置处理器实现

    配置类:

    @Configuration
    @ComponentScan("com.xc.a05.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("root");
            return dataSource;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    bean:

    @Slf4j
    public class Bean1 {
        public Bean1() {
            log.debug("我被 Spring 管理啦");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    com.xc.a05.component包下的bean:

    @Component
    @Slf4j
    public class Bean2 {
        public Bean2() {
            log.debug("我被 Spring 管理啦");
        }
    }
    @Controller
    @Slf4j
    public class Bean3 {
        public Bean3() {
            log.debug("我被 Spring 管理啦");
        }
    }
    @Slf4j
    public class Bean4 {
        public Bean4() {
            log.debug("我被 Spring 管理啦");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    启动类:干净的容器则为任务后置处理器,什么都没有

    public class A05 {
        public static void main(String[] args) throws IOException {
            // GenericApplicationContext 是一个【干净】的容器
            GenericApplicationContext context = new GenericApplicationContext();
            context.registerBean("config", Config.class);
            // 初始化容器
            context.refresh();
            for (String name : context.getBeanDefinitionNames()) {
                System.out.println(name);
            }
            // 销毁容器
            context.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    输出结果:Config配置类@ComponentScan和@Bean都没有被扫描

    config
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3

    2.1、ConfigurationClassPostProcessor-bean工厂后置处理器实现类

    添加此bean工厂后置处理器:

    public class A05 {
        public static void main(String[] args) throws IOException {
            // GenericApplicationContext 是一个【干净】的容器
            GenericApplicationContext context = new GenericApplicationContext();
            context.registerBean("config", Config.class);
            context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource
            // 初始化容器
            context.refresh();
            for (String name : context.getBeanDefinitionNames()) {
                System.out.println(name);
            }
            // 销毁容器
            context.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    输出结果:

    [DEBUG] 23:05:41.352 [main] com.xc.a05.component.Bean2          - 我被 Spring 管理啦 
    [DEBUG] 23:05:41.362 [main] com.xc.a05.component.Bean3          - 我被 Spring 管理啦 
    [DEBUG] 23:05:41.378 [main] com.xc.a05.Bean1                    - 我被 Spring 管理啦 
    [INFO ] 23:05:41.584 [main] c.a.druid.pool.DruidDataSource      - {dataSource-1} inited 
    config
    org.springframework.context.annotation.ConfigurationClassPostProcessor
    bean2
    bean3
    bean1
    dataSource
    [INFO ] 23:05:41.637 [main] c.a.druid.pool.DruidDataSource      - {dataSource-1} closing ... 
    [INFO ] 23:05:41.638 [main] c.a.druid.pool.DruidDataSource      - {dataSource-1} closed 
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    解析@ComponentScan @Bean @Import @ImportResource注解添加bean定义

    2.1.1、手写代码-解析@ComponentScan

    • 这是一个手写模拟解析@ComponentScan的bean工厂后置处理器
    • 因为是注册添加bean定义所有在postProcessBeanDefinitionRegistry,修改bean定义则为postProcessBeanFactory
    • 只需要将此自定义后置处理器与ConfigurationClassPostProcessor一样加载到bean容器即可
    public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
        @Override 
        public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        }
    
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
            try {
                //获取配置类的@ComponentScan注解
                ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
                if (componentScan != null) {
                    //遍历@ComponentScan注解下多个包路径进行操作
                    for (String p : componentScan.basePackages()) {
                        System.out.println("包路径:"+p);
                        // 将包路径转换为类路径
                        // com.itheima.a05.component -> classpath*:com/itheima/a05/component/**/*.class
                        String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
                        System.out.println("类路径:"+path);
                        //读取类class元数据信息
                        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                        AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                        // 通过类路径获取class文件
                        Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                        for (Resource resource : resources) {
                            System.out.println("class资源文件:"+resource);
                            // 获取类class详细信息
                            MetadataReader reader = factory.getMetadataReader(resource);
                             System.out.println("类名:" + reader.getClassMetadata().getClassName());
                            AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                             System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
                             System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
                             // 如果加了@Component或其派生注解
                            if (annotationMetadata.hasAnnotation(Component.class.getName())
                                || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                                // 创建bean定义
                                BeanDefinition bd = BeanDefinitionBuilder
                                        .genericBeanDefinition(reader.getClassMetadata().getClassName())
                                        .getBeanDefinition();
                                // 通过bean定义创建bean名称
                                String name = generator.generateBeanName(bd, beanFactory);
                                // 将bean添加到bean容器
                                beanFactory.registerBeanDefinition(name, bd);
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    2.1.2、手写代码-解析@Bean

    • 这是一个手写模拟解析@Bean的bean工厂后置处理器
    • 因为是注册添加bean定义所有在postProcessBeanDefinitionRegistry,修改bean定义则为postProcessBeanFactory
    • 只需要将此自定义后置处理器与ConfigurationClassPostProcessor一样加载到bean容器即可
    public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        }
    
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
            try {
                //读取类class元数据信息
                CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                //获取class类资源
                MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/xc/a05/Config.class"));
                //1、获取class类注解信息 2、获取被@Bean注解标注的信息
                Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
                for (MethodMetadata method : methods) {
                    System.out.println("被@Bean标注方法:"+method);
                    //创建bean定义
                    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
                    //获取工厂对象和工厂方法
                    builder.setFactoryMethodOnBean(method.getMethodName(), "config");
                    //解析@Bean上面的初始化方法
                    String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
                    if (initMethod.length() > 0) {
                        builder.setInitMethodName(initMethod);
                    }
                    BeanDefinition bd = builder.getBeanDefinition();
                    beanFactory.registerBeanDefinition(method.getMethodName(), bd);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    2.2、MapperScannerConfigurer-bean工厂后置处理器实现类

    扫描解析@Mapper注解接口,原理与解析@ComponentScan类似

    总结:解析@ComponentScan, @Bean, @Mapper 等注解为bean容器注册添加bean定义==

  • 相关阅读:
    【GCN基础学习】GCN基本模型概述,图卷积的基本计算方法,邻接矩阵的变换,GCN变换原理
    每日算法4:计算字符串中的字节数;正则匹配
    【PAT(甲级)】1059 Prime Factors
    wget参数使用说明
    [声明]这篇书评不是我们的反串炒作
    【面试经典150题】跳跃游戏Ⅱ
    Opencv笔记
    yii1 使用memcache 缓存不更新、过期时间不生效
    Linux ubuntu 20.04.5 Server安装远程桌面
    安卓 BottomSheetDialog
  • 原文地址:https://blog.csdn.net/qq_35512802/article/details/126167419