前言
Spring
是一款轻量级、易上手的开源框架,其中最核心的两大特性就是:IOC
和AOP
,通过使用Spring框架可以帮助开发人员快速完成构建企业级应用,不过,也正是因为其强大的功能,导致我们在工程实践中遇到各种各样的错误,而这些错误大多数都是因为使用者对Spring框架的不了解导致的,尤其像是Bean的生命周期相关的,非常容器出问题,本篇文章就来分享一个之前遇到的有关Bean生命周期相关的案例。
问题现象
有两个通过@Service
注解注入到Spring Bean
容器中的对象,我们希望MyServiceImpl
对象在初始化时能够调用UserServiceImpl
的init
方法,于是我们写成下面这样。
java
@Service
public class MyServiceImpl {
@Resource
private UserServiceImpl userService;
public MyServiceImpl() {
userService.init();
}
}
@Service
public class UserServiceImpl {
public void init() {
System.out.println("UserServiceImpl method");
}
}
结果,项目启动时会报如下错误:
java
Constructor threw exception; nested exception is java.lang.NullPointerException
问题分析
要搞清楚这个问题,就需要对Spring
初始化Bean
对象的过程有所了解。
Spring
加载Bean
对象的流程,大致可以分为以下三大部分:
- 扫描所有需要被
Spring
管理的Bean
对象。 - 实例化、初始化
Bean
对象。 - 确认是否需要为
Bean
对象生成代理对象。
根据本次问题的情况分析,可以排除1、3
两步,所以我们重点分析第2
步。
在Spring
中实例化、初始化Bean
对象关键的入口方法为:doCreateBean
,以下省略了非关键代码逻辑,从整体上来看其过程又主要分为三步:实例化、属性注入、初始化。
java
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[]
throws BeanCreationException {
// ...省略非关键代码
if (instanceWrapper == null) {
// 实例化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// ...省略非关键代码
Object exposedObject = bean;
try {
// bean的依赖注入
populateBean(beanName, mbd, instanceWrapper);
// 初始化bean对象
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
// ...省略非关键代码
}
所以,本次在调用构造方法时产生空指针的原因,就是因为在对象实例化时,对象的属性还没有注入。Spring管理Bean对象是先实例化对象本身,再实例化对象属性。
问题解决
其实,Spring中有多种方式可以实现上述需求。
1. 构造方法注入
最简单的一种方式就是改用构造方法注入,Spring
会首先完成构造参数的实例化,并随之注入到Bean
对象的属性中。
java
@Service
public class MyServiceImpl {
// @Resource
// private UserServiceImpl userService;
public MyServiceImpl(UserServiceImpl userService) {
userService.method();
}
}
2. 利用@PostConstruct注解
java
@Service
public class MyServiceImpl {
@Resource
private UserServiceImpl userService;
@PostConstruct
public void init() {
userService.method();
}
}
这个注解是在第三步,Bean
对象初始化时调用的。
java
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// ...省略非关键代码
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 包含了@PostConstruct注解的调用
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
// ...省略非关键代码
}
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
调用InitDestroyAnnotationBeanPostProcessor
的postProcessBeforeInitialization
方法。
java
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
// ...省略非关键代码
}
private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
if (this.lifecycleMetadataCache == null) {
// Happens after deserialization, during destruction...
return buildLifecycleMetadata(clazz);
}
// ...省略非关键代码
}
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
return this.emptyLifecycleMetadata;
}
List<LifecycleElement> initMethods = new ArrayList<>();
List<LifecycleElement> destroyMethods = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<LifecycleElement> currInitMethods = new ArrayList<>();
final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// initAnnotationType对应@PostConstruct注解
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
LifecycleElement element = new LifecycleElement(method);
currInitMethods.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
// destroyAnnotationType对应@PreDestroy注解
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
currDestroyMethods.add(new LifecycleElement(method));
if (logger.isTraceEnabled()) {
logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
});
initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
new LifecycleMetadata(clazz, initMethods, destroyMethods));
}
3. 实现InitializingBean接口
afterPropertiesSet
接口的调用,也是在第三步,对象初始化时触发的。
java
@Service
public class MyServiceImpl implements InitializingBean {
@Resource
private UserServiceImpl userService;
@Override
public void afterPropertiesSet() {
userService.method();
}
}
入口在invokeInitMethods
方法中
java
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// ...省略非关键代码
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 包含了@PostConstruct注解的调用
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 调用实现了InitializingBean接口的afterPropertiesSet方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
// ...省略非关键代码
}
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
// ...省略非关键代码
4. 实现ApplicationContextAware接口
setApplicationContext
接口的调用,还是在第三步,对象初始化时触发的。
java
@Service
public class MyServiceImpl implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
UserServiceImpl userService = (UserServiceImpl) applicationContext.getBean("userServiceImpl");
userService.method();
}
}
调用入口和@PostConstruct
注解一样,也是postProcessBeforeInitialization
这个方法,只不过是接口的实现类变成了:ApplicationContextAwareProcessor
。
java
@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
return bean;
}
AccessControlContext acc = null;
if (System.getSecurityManager() != null) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
// 调用相应的接口方法
invokeAwareInterfaces(bean);
}
return bean;
}
java
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
总结
针对最初的问题,我们使用了四种不同的方式来解决,而这些解决方式只有在你分析过源码后才能真正了解,我们一方面不得不佩服Spring
设计时留下的丰富的扩展点,另一方面也希望在能熟练使用框架之后,也更多的了解框架背后运行的原理。