提出问题
在 Spring 的框架中开发,当我们想在服务器启动后,就立马执行一个方法时,用的比较多的就是 implements InitializingBean 然后在 afterPropertiesSet 中做我们想做的事情,下面为示例代码:
public class UserServiceImpl implements UserService, InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
@Resource
private MessageService messageService;
// region spring
/**
* 初始化的节点 {@link org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons()}
*/
@Override
public void afterPropertiesSet() {
work();
}
但是这么做,是有风险的,当两个Bean 出现循环依赖的时候,此时使用 InitializingBean 就会出现当前类中的属性被初始化,但是属性中依赖的属性未被初始化。
下面详细的介绍下我所遇到的问题。
示例
介绍问题
- 首先有两个服务 MessageService,UserService 然后在其对应的实现类中互相依赖对方。当我们在 UserServiceImpl 中的 afterPropertiesSet() 中去通过 messageService 引用 userService 属性的时候,会发现 MessageServiceImpl 中的 userService 属性为 NULL
- 代码
public interface MessageService {
String getMessage();
String getMessage(String msg);
boolean checkUserNotNull();
}
@Component
public class MessageServiceImpl implements MessageService, InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(MessageServiceImpl.class);
@Resource
private UserService userService;
// region spring
/**
* 初始化的节点 {@link org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons()}
*/
@Override
public void afterPropertiesSet() {
LOGGER.info("init-MessageServiceImpl-afterPropertiesSet-user:{}", userService);
// LOGGER.info("userNotNull:{}", checkUserNotNull());
}
// endregion spring
// region public
@Override
public String getMessage() {
return "hello world";
}
@Override
public String getMessage(String msg) {
return String.format("test-%s", msg);
}
@Override
public boolean checkUserNotNull() {
return userService != null;
}
// endregion public
}
public interface UserService {
String getUser();
String getUser(String msg);
}
@Component
public class UserServiceImpl implements UserService, InitializingBean{
private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
@Resource
private MessageService messageService;
// region spring
/**
* 初始化的节点 {@link org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons()}
*/
@Override
public void afterPropertiesSet() {
LOGGER.info("init-UserServiceImpl-afterPropertiesSet-message:{}", messageService);
LOGGER.info("afterPropertiesSet-userNotNull:{}", messageService.checkUserNotNull());
}
// endregion spring
// region public
@Override
public String getUser() {
return "hello world";
}
@Override
public String getUser(String msg) {
return String.format("test-%s", msg);
}
// endregion public
}
谈谈我的理解
在 spring 初始化bean 的时候。在 org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization 会去实例化所有的 bean,接着org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String) ,org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(),然后在
在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean 中
这里有关键的三步。
1: addSingletonFactory 是为了防止 spring 初始化的时候,如果出现了循环依赖而导致的无限递归。但恰恰也就是这一步导致了上述的问题。
2: 给当前bean 的属性填充实例。
3: 这个步骤中会检测:如果实例实现了 InitializingBean 则调用对应的 afterPropertiesSet() 方法。
我们来介绍下第一步,当准备初始化当前 messageService 的时候,会将当前bean 进行一些操作
/**
* Add the given singleton factory for building the specified singleton
* if necessary.
* <p>To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
* @param beanName the name of the bean
* @param singletonFactory the factory for the singleton object
*/
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
接着走第二步 populateBean() , 在postProcessPropertyValues 中会去实例化当前类中的属性,也就是 userService。然后在 postProcessPropertyValues inject 所有的属性。
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
if (hasInstAwareBpps || needsDepCheck) {
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}
// org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessPropertyValues
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
// org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject
public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
Collection<InjectedElement> elementsToIterate =
(this.checkedElements != null ? this.checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
boolean debug = logger.isDebugEnabled();
for (InjectedElement element : elementsToIterate) {
if (debug) {
logger.debug("Processing injected element of bean '" + beanName + "': " + element);
}
element.inject(target, beanName, pvs);
}
}
}
/**
* Either this or {@link #getResourceToInject} needs to be overridden.
*/
protected void inject(Object target, String requestingBeanName, PropertyValues pvs) throws Throwable {
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
field.set(target, getResourceToInject(target, requestingBeanName));
}
else {
if (checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method) this.member;
ReflectionUtils.makeAccessible(method);
method.invoke(target, getResourceToInject(target, requestingBeanName));
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
我们可以注意到上面的 getResourceToInject() 这里尝试从 org.springframework.context.support.AbstractApplicationContext#getBean() 中拿到 userService 这么一个实例,然后就会走到 org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean,org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(),org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean,最后又绕回到上图 image_doCreateBean.png,只不过这次是尝试 实例化 userService。当执行到 populateBean() 的时候,很容易想到,这个时候又会去尝试实例化 UserServiceImpl 中的 messageService 属性。然后又会走到 org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean ,尝试拿到 messageService 实例,可以看到在 doGetBean 中执行到 getSingleton(),而getSingleton 会尝试从 singletonFactories 这个一个属性中取得是否存在对应的实例,如果有则返回。而我们回到上面的 addSingletonFactory(),在执行这个方法的时候,已经将 messageService put 到了 map中。而此时这个messageService 是没有userService属性的,因为初始化 messageService 的栈还没结束。
紧接着 userService 拿到了不完整的 messageService 走完了 populateBean(),接着就去执行了 initializeBean() 方法,也就是会走到 UserServiceImpl 中的 afterPropertiesSet(),而这个时候 userService 中 messageService 的 userService 是不存在的,所以一切就明了了。
可以在 image_initializeBean_user.png 中看到 doCreateBean[1] 代表 实例化 messageService 的过程还未出栈,doCreateBean[2] 表示正在实例化 userService。我们可以看到在进入 initializeBean() 的过程中, UserServiceImpl 中的 messageService 的 userService 为 NULL
// org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
...
}
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
更好的办法
- 为了解决这种可能存在的 NPE,可以使用 implements ApplicationListener 来解决,在onApplicationEvent() 中是不会存在这种情况的。因为 onApplicationEvent 实在 finishRefresh() 中执行,而此时已经执行过 finishBeanFactoryInitialization() 容器内所有的bean 都初始化完成了。
@Component
public class UserServiceImpl implements UserService, InitializingBean, ApplicationListener<ContextRefreshedEvent> {
private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
@Resource
private MessageService messageService;
// region spring
/**
* 初始化的节点 {@link org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons()}
*/
@Override
public void afterPropertiesSet() {
LOGGER.info("init-UserServiceImpl-afterPropertiesSet-message:{}", messageService);
LOGGER.info("afterPropertiesSet-userNotNull:{}", messageService.checkUserNotNull());
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
LOGGER.info("onApplicationEvent-userNotNull:{}", messageService.checkUserNotNull());
}
}