前言 我們經常會看到或使用InitializingBean(或@PostConstruct)進行Bean的一個初始化過程,但是有時候會發現InitializingBean存在一些不太適用的場景。 比如我們有以下一個Dog類 @Service @Scope(scopeName = Configurab ...
前言
我們經常會看到或使用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!
嗯,一切都正常了。