zoukankan      html  css  js  c++  java
  • Spring Boot小组件

    前言

    我们经常会看到或使用InitializingBean(或@PostConstruct)进行Bean的一个初始化过程,但是有时候会发现InitializingBean存在一些不太适用的场景。

    比如我们有以下一个Dog

    @Service
    @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class Dog {
    
        public void makeSound() {
            System.out.println("bark!");
        }
    }
    

    这个Dog是一个prototype的Bean,每次我们从BeanFactory中获取这个类时都会创建一个新的类。

    然后我们有一个Person类,初始化的时候会需要一只狗叫一下

    @Service
    public class Person implements InitializingBean, BeanFactoryAware {
    
        private BeanFactory beanFactory;
        private List<Dog> managedDogs = new LinkedList<>();
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = beanFactory;
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            Dog dog = beanFactory.getBean(Dog.class);
            dog.makeSound();
        }
    
        public void addDog(Dog dog) {
            managedDogs.add(dog);
        }
    }
    

    到这还没啥事,运行一下可以正常地找到“bark!”的输出。

    这时候突然来了一个针对DogBeanPostProcessor,好巧不巧还依赖Person

    @Component
    public class DogPostProcessor implements BeanPostProcessor {
    
        @Autowired
        private Person person;
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (!(bean instanceof Dog)) {
                return null;
            }
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Dog.class);
            enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
                if (method.getName().equals("makeSound")) {
                    System.out.println("注意了狗狗要开始叫了!");
                }
                return method.invoke(bean, args);
            });
            Dog proxyDog = (Dog) enhancer.create();
            person.addDog(proxyDog);
            return proxyDog;
        }
    }
    

    在这个DogPostProcessor里对makeSound方法做了一个前置处理:预告要狗狗要开始叫了。最后还会把狗狗给到Person类进行管理。

    这时候我们再次运行程序,发现并没有"注意了狗狗要开始叫了!"的输出,同时person中也没有发现新增的Dog实例。为什么会这样呢?

    我们可以发现,在程序启动过程中,出现了一个信息

    Bean 'dog' of type [com.example.Dog] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
    

    这表示dog没有经过所有的BeanPostProcessor就被创建了。造成这种局面的原因主要是DogPostProcessor的创建依赖Person的创建,而在Person创建的过程中有一个调用初始化方法的子过程,在这个子过程里需要从BeanFactory获得一个Dog的实例。这时DogPostProcessor都没初始化完呢,自然Dog也无法被处理了。

    说来说去,还是这个调用初始化方法的时机不太合适。能不能有一种办法,提供一个在所有Bean都创建后才调用的初始化方法呢?没错,SmartInitializingSingleton正是因此而生。

    本文所使用的源码版本为 2.2.2.RELEASE,如有出入请检查版本是否不一致。

    从哪开始

    我们来到org.springframework.context.support.AbstractApplicationContext#refresh方法,这是上下文创建时调用的方法,里面会一步一步构建好整个上下文。当所有的前置工作都做好时,会调用到finishBeanFactoryInitialization(beanFactory)进行所有在前置工作时还没初始化的(也是绝大多数的)Singleton Bean的初始化工作。

    最关键的部分是DefaultListableBeanFactory#preInstantiateSingletons方法,精简后如下所示:

        // 请注意以下代码有大量删改,只留下了关键示意代码,请自行查看源代码。
    	@Override
    	public void preInstantiateSingletons() throws BeansException {
    		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
    
    		for (String beanName : beanNames) {
    			getBean(beanName);
    		}
    
    		for (String beanName : beanNames) {
    			Object singletonInstance = getSingleton(beanName);
    			if (singletonInstance instanceof SmartInitializingSingleton) {
    				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
    				smartSingleton.afterSingletonsInstantiated();
    			}
    		}
    	}
    

    可以看到在所有Bean都初始完后,遍历判断了每个Singleton Bean是否实现了SmartInitializingSingleton接口,然后对实现此接口的实例调用afterSingletonsInstantiated方法。自然这个初始化方法被调用时,所有的Bean都创建好了。也就是说这个接口将初始化方法的调用和Bean的创建过程分开了。

    其实在前言中提到的这个场景,你可能会想到另一种办法处理,那就是通过实现ApplicationListener<ContextRefreshedEvent>接口。上下文都刷新完成后,自然会通知到这个接口,当然这样稍显复杂,而且看起来也不太像一个Bean的初始化了。

    重新出发

    我们把Person改改

    @Service
    public class Person implements SmartInitializingSingleton, BeanFactoryAware {
    
        private BeanFactory beanFactory;
        private List<Dog> managedDog = new LinkedList<>();
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = beanFactory;
        }
    
        public void addDog(Dog dog) {
            managedDog.add(dog);
        }
    
        @Override
        public void afterSingletonsInstantiated() {
            Dog dog = beanFactory.getBean(Dog.class);
            dog.makeSound();
        }
    }
    

    然后再运行一下

    注意了狗狗要开始叫了!
    bark!
    

    嗯,一切都正常了。

  • 相关阅读:
    反射工具类
    序列化反序列化工具类
    开发SCM系统笔记001
    卸载Oracle
    log4j日志级别
    类加载器与methodinterceptor接口
    hibernate 查询、二级缓存、连接池
    Hibernate缓存、组件、继承映射
    Hibernate映射1
    Hibernate配置文件
  • 原文地址:https://www.cnblogs.com/imyijie/p/12702617.html
Copyright © 2011-2022 走看看