• spring bean生命周期内拦截点和场景运用


    概述

    创建 bean 的过程中有多个可以自定义的扩展点,以便在 bean 的生命周期中执行自定义操作,以下是在整个生命周期内,可以自定义的点和场景举例

    BeanDefinitionRegistryPostProcessor

    bean定义注册后处理器,允许你在 Spring 容器初始化之前修改或注册 bean 定义。你可以实现这个接口来自定义 bean 的注册
    注入

    1. 注入
     
    
    • 1
    1. 自定义
    public class MyBeanDefinitionRegistryPostProcessor
        implements BeanDefinitionRegistryPostProcessor {
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
           
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
          
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    从哪开始调用的?以上我们定义个实现了BeanDefinitionRegistryPostProcessor的bean后spring在哪调用的?下面是初始化调用的地方

    1. AbstractApplicationContext方法refresh调用 bean工厂后置处理器:invokeBeanFactoryPostProcessors,此时bean工厂ConfigurableListableBeanFactory已经实例化完成
    2. 以上方法调用PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
      是个静态方法,就是在此方法调用的,关键代码如下:
    while (reiterate) {
    				reiterate = false;
            //根据BeanDefinitionRegistryPostProcessor这个class类型得到postProcessorNames,
            看到没,此时就得到了我们的bean name
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            
            for (String ppName : postProcessorNames) {
               if (!processedBeans.contains(ppName)) {
                  currentRegistryProcessors.add(beanFactory.getBean(ppName, 
            BeanDefinitionRegistryPostProcessor.class));
                  processedBeans.add(ppName);
                  reiterate = true;
               }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            //这里反射调用的
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
            currentRegistryProcessors.clear();
    	}
    //这里调用 invokeBeanFactoryPostProcessors
    invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    1. 方法invokeBeanDefinitionRegistryPostProcessors调用
    private static void invokeBeanDefinitionRegistryPostProcessors(
          Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {
    
       for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
          StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
                .tag("postProcessor", postProcessor::toString);
    			//这里调用postProcessBeanDefinitionRegistry
          postProcessor.postProcessBeanDefinitionRegistry(registry);
          postProcessBeanDefRegistry.end();
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    至此初始化调用流程分析完毕

    方法:postProcessBeanDefinitionRegistry

    对BeanDefinitionRegistry的一些操作,可以动态增删bean

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        genericBeanDefinition.setBeanClass(MyBean1.class);
        //注册bean
        registry.registerBeanDefinition("myBean", genericBeanDefinition);
        //删除bean,前提存在
        if(registry.containsBeanDefinition("beanName")){
            registry.removeBeanDefinition("beanName");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    场景1:自定义扫描包并注册bean

    1. 自定义注解
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface CustomComponent {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 扫描自定义package并注册bean
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        TypeFilter filter = new AnnotationTypeFilter(CustomComponent.class);
        scanner.addIncludeFilter(filter);
    
        // 扫描指定包
        for (BeanDefinition beanDefinition : scanner.findCandidateComponents("org.example.bean")) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
                ClassUtils.resolveClassName(Objects.requireNonNull(beanDefinition.getBeanClassName()), ClassUtils.getDefaultClassLoader()));
            registry.registerBeanDefinition(beanDefinition.getBeanClassName(), builder.getBeanDefinition());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    自定义包下,注解为CustomComponent的类都会被注册到bean中

    方法:postProcessBeanFactory

    ConfigurableListableBeanFactory的一些操作,这个就比较丰富
    以下是使用场景举例:

    场景1:动态注册单例bean

     @Override
      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
          beanFactory.registerSingleton("myBean1",new MyBean1());
      }
    
    • 1
    • 2
    • 3
    • 4

    场景2:添加bean的后置处理拦截器BeanPostProcessor

    BeanPostProcessor 是bean在初始化前后的拦截器,创建多少bean 拦截多少次,可以通过postProcessBeanFactory,动态把拦截器添加进去

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            beanFactory.registerSingleton("myBean1",new MyBean1());
    
            beanFactory.addBeanPostProcessor(new MyBeanPostProcessor());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    MyBeanPostProcessor 定义如下

    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class MyBeanPostProcessor implements BeanPostProcessor {
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("开始注册:" + beanName);
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("注册完成:" + beanName);
            return bean;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    场景3:自定义属性配置(挺不错)

    什么意思,就是可以给自定义的属性进行转换,将字符串值转换为特定类型的属性值。
    看个场景就明白 了
    bean为User的对象有个属性是private Address address;
    Address 里边也有自己的属性,如下:

    private String address1;
    private String address2;
    private String address3;
    
    • 1
    • 2
    • 3

    我在注入bean的时候,如果传输的是个字符串或者其他乱七八糟的格式,反正不是Address,我该如下转换呢?
    如下bean配置,address给的是河南-郑州-高新区这显然不是对象,那我们就可以利用自定义转换器来进行转换

    
    
        
        
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    自定义转换器定义
    1. 新建Address对应的转换器AddressEditor要继承PropertyEditorSupport,重写方法setAsText其实这里得到的就是以上的河南-郑州-高新区字符串,我们手动转换,最后调用setValue封装进去即可
    public class AddressEditor extends PropertyEditorSupport {
    
        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            String[] arrs = text.split("-");
            Address address = new Address();
            address.setAddress1(arrs[0]);
            address.setAddress2(arrs[1]);
            address.setAddress3(arrs[2]);
            super.setValue(address);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. AddressEditor肯定需要注册呀,怎么注册?新建个自定义转换注册器呗,实现PropertyEditorRegistrar
    public class CustomEditorRegistrar implements PropertyEditorRegistrar {
        @Override
        public void registerCustomEditors(PropertyEditorRegistry registry) {
            registry.registerCustomEditor(Address.class, new AddressEditor());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 那注册器要交给spring处理,那我们就可以通过postProcessBeanFactory方法的 把注册器加进去
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
       beanFactory.addPropertyEditorRegistrar(new CustomEditorRegistrar());
    }
    
    • 1
    • 2
    • 3

    执行后,bean的Address就是我们转换后的值

    场景4:属性值的动态注入

    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            // 修改属性
            BeanDefinition user = beanFactory.getBeanDefinition("user");
            MutablePropertyValues propertyValues = user.getPropertyValues();
            propertyValues.addPropertyValue("id","123456");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    场景5:bean定义修改

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 修改属性
        BeanDefinition bd = beanFactory.getBeanDefinition("user");
        //修改初始方法、是否为单例,等其他属性
        bd.setInitMethodName("test2");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    FactoryBeans

    用于创建和配置对象实例。它允许你将对象的创建和配置逻辑封装在一个独立的类中

    public class MyBeanFactory implements FactoryBean {
    
        @Override
        public User getObject() throws Exception {
            // 在这里可以编写创建和配置 MyBean 实例的逻辑
            return new User();
        }
    
        @Override
        public Class getObjectType() {
            return User.class;
        }
    
        @Override
        public boolean isSingleton() {
            return true; // 这里可以指定对象是否是单例
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18


    这样也能创建bean

    InitializingBean

    bean 实现 InitializingBean 重写方法afterPropertiesSet

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 调用时机?

    bean完成初始化(还未实例化)之后调用,类AbstractApplicationContext refresh 你应该不会陌生,此方法执行到
    finishBeanFactoryInitialization(beanFactory);

    实例化bean

    带着问题来看bean的实例化,并知道在何时执行的以上afterPropertiesSet

    1. 以上方法会执行beanFactory 实现类DefaultListableBeanFactory方法preInstantiateSingletons

    简化代码:

    public void preInstantiateSingletons() throws BeansException {
      
      //beanDefinitionNames 你应该不陌生,这就是储存所有bean name 的一个list
       List beanNames = new ArrayList<>(this.beanDefinitionNames);
    
       
       for (String beanName : beanNames) {
    		//就是根据bean 名称 实例化 bean
           getBean(beanName);
          }
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    以上代码就是循环bean名称,然后实例化

    1. getBean 会调用doGetBean 此方法就是根据bean name得到成员变量mergedBeanDefinitions 得到bean的元数据信息,也是个map,里边储存的是RootBeanDefinition他里边就储存了你在xml中定义的bean属性信息
    2. 有了name,有了 对应的元数据,可以创建bean了,最终创建代码在类AbstractAutowireCapableBeanFactory
    protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
       try {
          Object beanInstance;
          beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
          BeanWrapper bw = new BeanWrapperImpl(beanInstance);
          initBeanWrapper(bw);
          return bw;
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. getInstantiationStrategy 会得到实例化策略类,这里是SimpleInstantiationStrategy,代码就来到了这里,这里没啥好讲的,就是得到反射类Constructor然后根据 .class 创建,具体创建使用的是工具类BeanUtils.instantiateClass(constructorToUse)
    public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
       if (!bd.hasMethodOverrides()) {
          Constructor constructorToUse;
          synchronized (bd.constructorArgumentLock) {
             constructorToUse = (Constructor) bd.resolvedConstructorOrFactoryMethod;
             if (constructorToUse == null) {
                final Class clazz = bd.getBeanClass();
    
                constructorToUse = clazz.getDeclaredConstructor();
    
                bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                }
                catch (Throwable ex) {
                   throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                }
             }
          }
          return BeanUtils.instantiateClass(constructorToUse);
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    Constructor用法:

     Class personClass = Person.class;
    Constructor constructor = personClass.getConstructor(String.class, int.class);
    
    // 使用构造函数创建对象
    Person person = constructor.newInstance("Alice", 25);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 那以上创建好后返回的最终是个BeanWrapper对象,他是spring提供的一个bean工具类,用于方便地访问和操作 bean 对象的属性

    具体用法如下:

    public static void main(String[] args) {
            // 创建一个简单的对象
            Person person = new Person();
            
            // 使用 BeanWrapper 包装这个对象
            BeanWrapper wrapper = new BeanWrapperImpl(person);
            
            // 设置属性值
            wrapper.setPropertyValue("name", "Alice");
            wrapper.setPropertyValue("age", 25);
            // 获取属性值
            String name = (String) wrapper.getPropertyValue("name");
            int age = (int) wrapper.getPropertyValue("age");
            
            System.out.println("Name: " + name);
            System.out.println("Age: " + age);
        	  //获取对象
    			Object bean = wrapper.getWrappedInstance();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    到此,bean实例化完成,最终返回一个bean包装类BeanWrapper

    调用afterPropertiesSet 以及定义的初始化方法

    回到最初,刚刚只有bean name和RootBeanDefinition 如何关联上BeanWrapper 的?

    1. 方法doCreateBean 关联代码,mbd 就是RootBeanDefinition对象,getWrappedClass就是获取 bean的class
    Class beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
      mbd.resolvedTargetType = beanType;
    }
    
    • 1
    • 2
    • 3
    • 4
    1. 初始化bean 方法doCreateBean 调用initializeBean调用invokeInitMethods,简化代码如
    protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
          throws Throwable {
    
       boolean isInitializingBean = (bean instanceof InitializingBean);
       if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
          
          ((InitializingBean) bean).afterPropertiesSet();
       }
    
       if (mbd != null && bean.getClass() != NullBean.class) {
          String initMethodName = mbd.getInitMethodName();
          if (StringUtils.hasLength(initMethodName) &&
                !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
             invokeCustomInitMethod(beanName, bean, mbd);
          }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    1. 以上第一个if 就是进入afterPropertiesSet关键代码,啥意思,bean满足继承了InitializingBean,就调用
    2. 看第二个if是啥mbd.getInitMethodName()mbd 就是RootBeanDefinitiongetInitMethodName就是你在xml中定义的初始化方法,xml配置如下:

    public void test(){
        System.out.println("初始化");
    }
    
    • 1
    • 2
    • 3

    init-method 定义的方法,如果他有长度,而且方法名不是afterPropertiesSet而且不是mbd.isExternallyManagedInitMethod(initMethodName)这个是什么意思?这个引申出另一个概念了:待续
    然后利用反射执行我们定义的初始化方法

    总结

    afterPropertiesSet是在初始化bean和实例化之后,调用的,在他执行之后才会调用其他xml定义的init-method

    • 注意init-method 只接受一个无参的初始化方法
  • 相关阅读:
    动手写prometheus的exporter-03-HIstogram(直方图)
    CRC16计算FC(博途SCL语言)
    Linux 文件读写
    从0到1了解大数据可视化平台
    Springboot实现Web组件注册
    第29课 绘制原理图——放置电源端口
    Docker使用ssh连接ubuntu容器
    【无标题】
    语音和噪声相关数据集(持续更新)
    【JVM学习】Jconsole 配置jmx 监控JVM
  • 原文地址:https://blog.csdn.net/qq_32196937/article/details/134040793