前言
我們經常會看到或使用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!”的輸出。
這時候突然來了一個針對Dog
的BeanPostProcessor
,好巧不巧還依賴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!
嗯,一切都正常了。