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

有时候,不是因为你没有能力,也不是因为你缺少勇气,只是因为你付出的努力还太少,所以,成功便不会走向你。而你所需要做的,就是坚定你的梦想,你的目标,你的未来,然后以不达目的誓不罢休的那股劲,去付出你的努力,成功就会慢慢向你靠近。

导读:本篇文章讲解 Spring之BeanFactoryPostProcessor(bean工厂后置处理器),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

一句话: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生命周期>>>>>>>>>>>>>>>>>>>销毁");
    }
}

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;
    }
}

启动类:

@SpringBootApplication
public class Test {
    public static void main(String[] args) throws IOException {
        //创建容器
        ConfigurableApplicationContext context = SpringApplication.run(Test.class, args);
        //关闭容器
        context.close();
    }
}

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、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到容器");
            }
        }
    }
}

生命周期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

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;
    }
}

bean:

@Slf4j
public class Bean1 {
    public Bean1() {
        log.debug("我被 Spring 管理啦");
    }
}

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 管理啦");
    }
}

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

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();
    }
}

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

config

Process finished with exit code 0

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();
    }
}

输出结果:

[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

解析@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();
        }
    }
}

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();
        }
    }
}

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

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/148604.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!