• Spring的后处理器


    目录

    Spring后处理器

    Bean工厂后处理器-BeanFactoryPostProcessor

    修改beanDefinition对象

    添加beanDefiniton对象

    方法一

    方法二

    自定义@Component


    Spring后处理器

    • Spring后处理器是Spring对外开放的重要拓展点(让我们可以用添加自己的逻辑),允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition(向BeanDefitionMap中添加BeanDefition对象的过程),动态修改BeanDefition,以及动态修改Bean的作用。Spring主要有两种后处理器
      • BeanFactoryPostprocessor:Bean工厂后处理器,(执行时机)在BeanDefinitionMap填充完毕,Bean实例化之前执行
      • BeanPostProcessor:Bean后处理器,(执行时机)一般在Bean实例化后,填充到单例池singletonObjects之前执行

    Bean工厂后处理器-BeanFactoryPostProcessor

    • BeanFactoryPostProcessor是一个接口规范,实现该接口的类只要交由Spring容器管理(即在配置文件中注册该类称为Bean对象)的话,那么Spring就会回调该接口的方法,用于对BeanDefition注册和修改功能
    • BeanFactoryPostProcessor定义如下
        1. //
        2. // Source code recreated from a .class file by IntelliJ IDEA
        3. // (powered by FernFlower decompiler)
        4. //
        5. package org.springframework.beans.factory.config;
        6. import org.springframework.beans.BeansException;
        7. @FunctionalInterface
        8. public interface BeanFactoryPostProcessor {
        9. void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
        10. }
    修改beanDefinition对象
    • 创建一个实现类(修改beanDefinition对象

        1. package com.example.PostProcessor;
        2. import org.springframework.beans.BeansException;
        3. import org.springframework.beans.factory.config.BeanDefinition;
        4. import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
        5. import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
        6. public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        7. @Override
        8. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        9. System.out.println("beanDefinitionMap填充完毕后会回调该方法");
        10. // todo 修改Map集合中的BeanDefinition对象
        11. BeanDefinition userService = beanFactory.getBeanDefinition("userService");
        12. userService.setBeanClassName("com.example.DAO.Impl.UserDAOImpl");
        13. }
        14. }
    • 测试类

        1. package com.example.Test;
        2. import org.springframework.context.support.ClassPathXmlApplicationContext;
        3. public class TestApplicationContext {
        4. public static void main(String[] args) {
        5. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        6. System.out.println(context.getBean("userService"));
        7. }
        8. }
    • 运行结果如下

      • 显然bean对应的类被改变了 
    添加beanDefiniton对象
    方法一
    • 创建一个类(添加beanDefiniton对象
        1. package com.example.PostProcessor;
        2. import org.springframework.beans.BeansException;
        3. import org.springframework.beans.factory.config.BeanDefinition;
        4. import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
        5. import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
        6. import org.springframework.beans.factory.support.DefaultListableBeanFactory;
        7. import org.springframework.beans.factory.support.RootBeanDefinition;
        8. public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        9. @Override
        10. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        11. // todo 向Map集合中添加一个BeanDefinition对象,即在配置文件中没有注册
        12. // 创建一个新的beanDefinition对象
        13. BeanDefinition beanDefinition = new RootBeanDefinition();
        14. // 设置bean对应的类
        15. beanDefinition.setBeanClassName("com.example.DAO.Impl.UserDAOImpl");
        16. DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
        17. // 添加该beanDefinition对象
        18. listableBeanFactory.registerBeanDefinition("UserDAO", beanDefinition);
        19. }
        20. }

        在配置文件中没有配置UserDAO了

    • 测试类

        1. package com.example.Test;
        2. import com.example.DAO.Impl.UserDAOImpl;
        3. import org.springframework.context.support.ClassPathXmlApplicationContext;
        4. public class TestApplicationContext {
        5. public static void main(String[] args) {
        6. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        7. System.out.println(context.getBean(UserDAOImpl.class));
        8. }
        9. }
    • 运行结果

                    beanDefinition对象成功添加


    方法二
    • Spring提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作
        1. //
        2. // Source code recreated from a .class file by IntelliJ IDEA
        3. // (powered by FernFlower decompiler)
        4. //
        5. package org.springframework.beans.factory.support;
        6. import org.springframework.beans.BeansException;
        7. import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
        8. public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
        9. void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
        10. }
    • 创建一个类实现后处理器BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor(记得将该类注册到Spring容器中)

        1. package com.example.PostProcessor;
        2. import org.springframework.beans.BeansException;
        3. import org.springframework.beans.factory.config.BeanDefinition;
        4. import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
        5. import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
        6. import org.springframework.beans.factory.support.BeanDefinitionRegistry;
        7. import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
        8. import org.springframework.beans.factory.support.DefaultListableBeanFactory;
        9. import org.springframework.beans.factory.support.RootBeanDefinition;
        10. public class MyBeanFactoryPostProcessor02 implements BeanDefinitionRegistryPostProcessor {
        11. @Override
        12. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        13. // 注册beanDefinition
        14. BeanDefinition beanDefinition = new RootBeanDefinition();
        15. beanDefinition.setBeanClassName("com.example.DAO.Impl.UserDAOImpl");
        16. registry.registerBeanDefinition("UserDAO", beanDefinition);
        17. }
        18. @Override
        19. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        20. }
        21. }

        实现添加beanDefiniton就会简单很多

    • 测试类

        1. package com.example.Test;
        2. import com.example.DAO.Impl.UserDAOImpl;
        3. import org.springframework.context.support.ClassPathXmlApplicationContext;
        4. public class TestApplicationContext {
        5. public static void main(String[] args) {
        6. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        7. System.out.println(context.getBean(UserDAOImpl.class));
        8. }
        9. }
    • 运行结果如下

    完整流程图 

            
     


    自定义@Component
    • 案例
      • 使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描
    • 要求
      • 自定义@MyComponent注解,使用在类上
      • 使用资料中提供好的包扫描工具BaseClassScanUtils完成指定包的类扫描
      • 自定义BeanFactoryPostProcessor完成注解@MyComponent的解析,解析最终被Spring管理
    • 具体代码如下
      • 注解类
          1. package com.example.Anno;
          2. import java.lang.annotation.ElementType;
          3. import java.lang.annotation.Retention;
          4. import java.lang.annotation.RetentionPolicy;
          5. import java.lang.annotation.Target;
          6. @Target(ElementType.TYPE) // 设该注解的使用范围
          7. @Retention(RetentionPolicy.RUNTIME) // 设置该注解运行期间可见
          8. public @interface MyComponent {
          9. String value(); //用于设置注解的值
          10. }
      • 工具类

          1. package com.example.Utils;
          2. import com.example.Anno.MyComponent;
          3. import org.springframework.core.io.Resource;
          4. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
          5. import org.springframework.core.io.support.ResourcePatternResolver;
          6. import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
          7. import org.springframework.core.type.classreading.MetadataReader;
          8. import org.springframework.core.type.classreading.MetadataReaderFactory;
          9. import org.springframework.util.ClassUtils;
          10. import java.util.HashMap;
          11. import java.util.Map;
          12. public class BaseClassScanUtils {
          13. //设置资源规则
          14. private static final String RESOURCE_PATTERN = "/**/*.class";
          15. public static Map scanMyComponentAnnotation(String basePackage) {
          16. //创建容器存储使用了指定注解的Bean字节码对象
          17. Map annotationClassMap = new HashMap();
          18. //spring工具类,可以获取指定路径下的全部类
          19. ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
          20. try {
          21. String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
          22. ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;
          23. Resource[] resources = resourcePatternResolver.getResources(pattern);
          24. //MetadataReader 的工厂类
          25. MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver);
          26. for (Resource resource : resources) {
          27. //用于读取类信息
          28. MetadataReader reader = refractory.getMetadataReader(resource);
          29. //扫描到的class
          30. String classname = reader.getClassMetadata().getClassName();
          31. Class clazz = Class.forName(classname);
          32. //判断是否属于指定的注解类型
          33. if(clazz.isAnnotationPresent(MyComponent.class)){
          34. //获得注解对象
          35. MyComponent annotation = clazz.getAnnotation(MyComponent.class);
          36. //获得属value属性值
          37. String beanName = annotation.value();
          38. //判断是否为""
          39. if(beanName!=null&&!beanName.equals("")){
          40. //存储到Map中去
          41. annotationClassMap.put(beanName,clazz);
          42. continue;
          43. }
          44. //如果没有为"",那就把当前类的类名作为beanName
          45. annotationClassMap.put(clazz.getSimpleName(),clazz);
          46. }
          47. }
          48. } catch (Exception exception) {
          49. }
          50. return annotationClassMap;
          51. }
          52. public static void main(String[] args) {
          53. Map stringClassMap = scanMyComponentAnnotation("com.itheima");
          54. System.out.println(stringClassMap);
          55. }
          56. }
      • 使用注解来注册为Bean对象的类

          1. package com.example.Beans;
          2. import com.example.Anno.MyComponent;
          3. @MyComponent("otherBean")
          4. public class otherBeans {
          5. }

          在配置文件中没有配置该类作为bean对象

      • 后工厂处理器类(该类要交给Spring容器管理

          1. package com.example.PostProcessor;
          2. import com.example.Utils.BaseClassScanUtils;
          3. import org.springframework.beans.BeansException;
          4. import org.springframework.beans.factory.config.BeanDefinition;
          5. import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
          6. import org.springframework.beans.factory.support.BeanDefinitionRegistry;
          7. import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
          8. import org.springframework.beans.factory.support.RootBeanDefinition;
          9. import java.util.Map;
          10. public class MyComponentBeanFactoryProcessor implements BeanDefinitionRegistryPostProcessor {
          11. @Override
          12. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
          13. // 通过工具去扫描指定包及其子包下的所有类,收集使用@MyComponent注解的类,放在Map集合中
          14. Map MyComponentAnnotationMap = BaseClassScanUtils.scanMyComponentAnnotation("com.example");
          15. // 遍历Map,组装BeanDefinition进行注册
          16. MyComponentAnnotationMap.forEach((beanName,clazz)->{
          17. // 获取beanClassName
          18. String beanClassName = clazz.getName();
          19. // 创建beanDefinition
          20. BeanDefinition beanDefinition = new RootBeanDefinition();
          21. beanDefinition.setBeanClassName(beanClassName);
          22. // 注册
          23. registry.registerBeanDefinition(beanName,beanDefinition);
          24. });
          25. }
          26. @Override
          27. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
          28. }
          29. }

          通过后工厂处理器类来将标记了自己创建的@MyComponent注解的类创建为beanDefinition对象后添加到beanDefinitionMap集合中。

      • 测试类

          1. package com.example.Test;
          2. import com.example.Beans.otherBeans;
          3. import org.springframework.context.support.ClassPathXmlApplicationContext;
          4. public class TestApplicationContext {
          5. public static void main(String[] args) {
          6. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
          7. System.out.println(context.getBean(otherBeans.class));
          8. }
          9. }
      • 运行结果


        •  

        • 运行成功~

    • 使用注解注册bean的原理

      • 最主要是通过Bean工厂后处理器进行实现的,通过工具类获取到添加了注解的类的集合后,在后处理器中,对扫描结果进行遍历,然后生成对对应的beanDefinition对象后,注册到beanDefinitonMap集合后即可。

  • 相关阅读:
    八、线性代数二 ,矩阵的秩
    Vulhub靶场搭建与使用
    iOS开发-Xcode
    数据结构--字符串的模式匹配
    10.01
    net基于asp.net的二手商品的交易系统-二手网站-计算机毕业设计
    电商RN项目秒开优化实践
    Clion~Clion常用配置和插件
    HTA入门基础教程 | VBS脚本的GUI界面 HTA简明教程 ,附带完整历程及界面美化
    多个服务器的用户共享同一个用户目录的做法
  • 原文地址:https://blog.csdn.net/weixin_64939936/article/details/133089906