關於Spring Bean建立的一些問題
阿新 • • 發佈:2021-01-22
技術標籤:SpringspringSpring單例Bean建立
最近看到了邏輯大概像下面這樣的程式碼:
import org.springframework.stereotype.Component;
import vip.mycollege.spring.aware.ApplicationHolder;
@Component
public class DataComponent {
private static DataComponent instance;
public static DataComponent getInstance() {
if (instance == null) {
createInstance();
}
return instance;
}
private static synchronized void createInstance() {
if (instance != null) {
return;
}
instance = ApplicationHolder.getBean(DataComponent.class);
}
public void initData() {
System.out.println("init data");
}
public void destroy() {
System.out.println("destroy...");
}
}
其中ApplicationHolder獲取bean是通過Spring的ApplicationContext。
看著是不是有點眼熟,是不是像建立類的單例模式,還是使用雙重檢查方式。
你能看出哪些問題?
最重要的問題是:從註解和程式碼邏輯我們可以看出,這個Bean是通過Spring管理的。
Spring建立Bean它本身預設就是單例,就是使用@Lazy懶載入也一樣,除非使用@Scope(“prototype”)。
所以完全沒有必要考慮是否為空的問題,直接使用ApplicationContext.getBean(DataComponent.class)就可以,如果為null,Spring自己會去建立,除非Bean不存在,可以通過ApplicationContextAware注入。
Spring建立Bean已經考慮了執行緒安全問題,下面是DefaultSingletonBeanRegistry通過三級快取獲取Bean的邏輯。
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
計算使用Spring管理Bean了,並且使用註解方式,那麼初始化方法、銷燬方法就加上註解,不要手動去呼叫。我之所以發現這個問題就是因為,他們沒有呼叫初始化方法,報空指標才注意到。
我們對這個類稍微改造一下:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class DataComponent implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
DataComponent.applicationContext = applicationContext;
}
public static DataComponent getInstance() {
return applicationContext.getBean(DataComponent.class);
}
@PostConstruct
public void initData() {
System.out.println("init data");
}
@PreDestroy
public void destroy() {
System.out.println("destroy...");
}
}
@PostConstuct方法執行是在afterPropertiesSet之前,下面是相關初始化方法大致先後順序:
- @PostConstuct方法
- afterPropertiesSet
- init-method
如果,對這些還不太清楚可以看一下下面兩篇文章: