最近看到了逻辑大概像下面这样的代码:
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
如果,对这些还不太清楚可以看一下下面两篇文章: