文章目录
前言
有些时候当你不太了解Spring管理Bean的方式时,你可能会遇到各种问题,比如在构造方法中调用容器中某个Bean对象的方法时,例如这样:
@Service
public class MyServiceImpl {
@Resource
private UserServiceImpl userService;
public MyServiceImpl() {
userService.method();
}
}
然后就会抛出如下异常:
Constructor threw exception; nested exception is java.lang.NullPointerException
一、案例演示
两个Service
类,都通过@Service
注解注入到Spring的Bean对象容器中,我们希望在MyServiceImpl
对象在初始化时能够调用UserServiceImpl
的init
方法,于是我们写成下面这样。
@Service
public class MyServiceImpl {
@Resource
private UserServiceImpl userService;
public MyServiceImpl() {
userService.init();
}
}
@Service
public class UserServiceImpl {
public void init() {
System.out.println("UserServiceImpl method");
}
}
最终,你会发现在项目启动时,会抛出空指针异常。
二、问题分析
1.Spring加载Bean对象的主要流程
之所以会犯这样的错误,完全是因为对Spring初始化Bean对象的过程不了解,那么,我们不妨先来简单分析一下其关键流程。
Spring加载Bean对象的流程,大致可以分为三大部分:
- 扫描所有需要被Spring管理的Bean对象。
- 实例化、初始化Bean对象。
- 确认是否需要为Bean对象生成代理对象。
可以确认1、3两步和我们此次的问题没有关联,所以我们重点分析第二步。
而实例化、初始化Bean对象关键的方法就是doCreateBean
,可以看出其过程又主要分为三步:实例化、属性注入、初始化。
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);
}
// ...省略非关键代码
}
很明显,之所以产生空指针的问题就在于,在对象实例化时,属性还没有注入。
2.问题解决
现在,已经找到了问题产生的根源,那么修改起来就很简单了,有多种方式可以实现我们的需求。
2.1、构造方法注入
最简单的一种方式就是改用构造方法注入,因为如果是构造方法注入,那Spring就会提前完成构造方法中参数的实例化,也就是说现在UserServiceImpl
的实例化是在MyServiceImpl
之前了。
@Service
public class MyServiceImpl {
//@Resource
//private UserServiceImpl userService;
public MyServiceImpl(UserServiceImpl userService) {
userService.method();
}
}
2.2、利用@PostConstruct注解
@Service
public class MyServiceImpl {
@Resource
private UserServiceImpl userService;
@PostConstruct
public void init() {
userService.method();
}
}
这个注解是在Bean对象初始化时调用的
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
方法
@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);
}
// ...省略非关键代码
}
其中initAnnotationType
和destroyAnnotationType
分别对应@PostConstruct
和@PreDestroy
注解
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 -> {
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);
}
}
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));
}
2.3、实现InitializingBean接口
@Service
public class MyServiceImpl implements InitializingBean {
@Resource
private UserServiceImpl userService;
@Override
public void afterPropertiesSet() {
userService.method();
}
}
initializeBean
方法除了会调用@PostConstruct
注解的方法,还完成了InitializingBean
接口的afterPropertiesSet
方法调用
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();
}
}
// ...省略非关键代码
2.4、实现ApplicationContextAware接口
@Service
public class MyServiceImpl implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
UserServiceImpl userService = (UserServiceImpl) applicationContext.getBean("userServiceImpl");
userService.method();
}
}
实际上ApplicationContextAware
接口中的setApplicationContext
方法也是在Bean对象初始化时调用的,调用入口还是postProcessBeforeInitialization
这个方法,只不过调用的是ApplicationContextAwareProcessor
中的。
@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;
}
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);
}
}
总结
针对最初的问题,我们使用了4种不同的方式来解决并实现了最终的业务需求,很明显,这些解决方式只有在你分析过源码后才能发现,我们一方面不得不佩服Spring设计时留下的丰富的扩展点,另一方面也表明了学习源码的重要性。