下面代码中,LightMgrService
管理LightService
控制宿舍灯的开启和关闭,让LightMgrService
初始化时能够自动调用LightService
的check
方法来检查所有宿舍灯的电路是否正常
@Component
public class LightMgrService {
@Autowired
private LightService lightService;
public LightMgrService() {
lightService.check();
}
}
@Service
public class LightService {
...
public void check() {
System.out.println("check all lights");
}
}
但是当启动项目后出现NullPointerException
先了解Spring类初始化过程:
将一些必要的系统类(如Bean的后置处理器类)注册到Spring容器
将这些后置处理器实例化,并注册到Spring的容器中
实例化所有用户定制类,调用后置处理器进行辅助装配、类初始化等
getBean()- >doGetBean()->getSingleton()
,如Bean不存在则调用createBean()->doCreateBean()
进行实例化,下面为doGetBean
方法:// AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
...
if (instanceWrapper == null) {
// 实例化Bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
...
Object exposedObject = bean;
try {
// 注入Bean依赖
populateBean(beanName, mbd, instanceWrapper);
// 初始化Bean,如执行@PostConstruct标记的方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
...
}
...
}
createBeanInstance
方法最终执行到instantiateClass
方法:// BeanUtils#instantiateClass
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
return KotlinDelegate.instantiateClass(ctor, args);
}
else {
Class<?>[] parameterTypes = ctor.getParameterTypes();
Assert.isTrue(args.length <= parameterTypes.length, "Can't specify more arguments than constructor parameters");
Object[] argsWithDefaultValues = new Object[args.length];
for (int i = 0 ; i < args.length; i++) {
if (args[i] == null) {
Class<?> parameterType = parameterTypes[i];
argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null);
}
else {
argsWithDefaultValues[i] = args[i];
}
}
// 最终执行该方法
return ctor.newInstance(argsWithDefaultValues);
}
}
...
}
对instantiateClass
方法进行分析后可看出,默认构造器是在类实例化的时候被自动调用,Spring无法控制,此时负责自动装配的populateBean
方法还没执行,所以LightMgrService
的属性LightService
还是null
问题根源在于使用@Autowired
直接标记在成员属性上而引发的装配行为是发生在构造器执行之后,修改方案如下:
// 构造器参数LightService会被自动注入LightService的Bean,从而在构造器执行时不会出现空指针
private LightService lightService;
public LightMgrService(LightService lightService) {
this.lightService = lightService;
lightService.check();
}
init
方法,并使用PostConstruct
注解进行修饰@Autowired
private LightService lightService;
@PostConstruct
public void init() {
lightService.check();
}
InitializingBean
接口,并在其afterPropertiesSet
方法中执行初始化代码@Component
public class LightMgrService implements InitializingBean {
@Autowired
private LightService lightService;
@Override
public void afterPropertiesSet() throws Exception {
lightService.check();
}
}
对于方案2和3进行以下分析:
populateBean
方法后会调用initializeBean
方法:// AbstractAutowireCapableBeanFactory#initializeBean
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
...
// applyBeanPostProcessorsBeforeInitialization和invokeInitMethods分别处理@PostConstruct注解和InitializingBean接口的逻辑
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
...
}
applyBeanPostProcessorsBeforeInitialization
与@PostConstruct
:// InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata(applyBeanPostProcessorsBeforeInitialization最终执行到这)
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
...
do {
final List<LifecycleElement> currInitMethods = new ArrayList<>();
final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// 此处的this.initAnnotationType值为PostConstruct.class
// Spring将遍历查找被PostConstruct.class注解过的方法,返回到上层并最终调用此方法
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);
}
}
...
});
...
}
invokeInitMethods
与InitializingBean
接口// AbstractAutowireCapableBeanFactory#invokeInitMethods
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
// 判断当前Bean是否实现了InitializingBean接口,实现了该接口才会调用该Bean的接口实现方法afterPropertiesSet()
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
...
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
...
}
将上面的案例进行改造,去除LightService
的@Service
注解,使用配置类BeanConfiguration
来创建并注册LightService
类型的Bean(配置类就是用于创建一堆的Bean):
public class LightService {
public void start() {
System.out.println("turn on all lights");
}
public void shutdown() {
System.out.println("turn off all lights");
}
public void check() {
System.out.println("check all lights");
}
}
@Configuration
public class BeanConfiguration {
@Bean
public LightService getTransmission(){
return new LightService();
}
}
同时在修改启动类,让Spring启动后立马关闭当前Spring的上下文:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
context.close();
}
}
按照预期代码运行后不会有任何输出,但实际却打印了shutting down all lights
,即执行了shutDown
方法
只有通过使用@Bean
注册到Spring容器的对象才会在容器被关闭的时候自动调用shutdown
方法,而使用@Component
(即@Service
)将当前类自动注入到 Spring容器时该方法不会被自动执行
使用
@Bean的方法所注册的Bean对象,如果用户不设置destroyMethod
属性,其属性值为AbstractBeanDefinition.INFER_METHOD
(方案2的依据)shutdown
或close
方法// DisposableBeanAdapter#inferDestroyMethodIfNecessary
private String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
String destroyMethodName = beanDefinition.getDestroyMethodName();
// 如果destroyMethodName=INFER_METHOD
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
(destroyMethodName == null && bean instanceof AutoCloseable)) {
// 且没实现DisposableBean接口
if (!(bean instanceof DisposableBean)) {
try {
// 尝试查找close方法
return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex) {
try {
// 尝试查找shutdown方法
return bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex2) {
// no candidate destroy method found
}
}
}
return null;
}
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
}
inferDestroyMethodIfNecessary
方法的调用链如下:
doCreateBean- >registerDisposableBeanIfNecessary->registerDisposableBean(new DisposableBeanAdapter)->inferDestroyMethodIfNecessary
doCreateBean
方法,它包含了Bean的整个生命周期几乎所有的关键点-Bean实例的创建、Bean对象依赖的注入、定制类初始化方法的回调、Disposable方法的注册// AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
...
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
...
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
...
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
registerDisposableBean
方法:disposableBeans
将暂存DisposableBeanAdapter
实例,直到AnnotationConfigApplicationContext
的close
方法被调用// DefaultSingletonBeanRegistry#registerDisposableBean
public void registerDisposableBean(String beanName, DisposableBean bean) {
synchronized (this.disposableBeans) {
this.disposableBeans.put(beanName, bean);
}
}
AnnotationConfigApplicationContext
的close
方法被调用时(即Spring容器被销毁时),最终会调用destroySingleton
方法:遍历disposableBeans
属性获取DisposableBean
,并调用其close
方法或shutdown
方法// DefaultSingletonBeanRegistry#destroySingleton
public void destroySingleton(String beanName) {
// Remove a registered singleton of the given name, if any.
removeSingleton(beanName);
// Destroy the corresponding DisposableBean instance.
DisposableBean disposableBean;
synchronized (this.disposableBeans) {
disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
}
destroyBean(beanName, disposableBean);
}
为什么@Service
注入的LightService
的shutdown
方法没有被调用?因为该注解标记的LightService
没实现 AutoCloseable
、 DisposableBean
,也没添加DisposableBeanAdapter
shutdown
方法须添加DisposableBeanAdapter
,而添加是有条件的:// AbstractBeanFactory#registerDisposableBeanIfNecessary
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
// ①确定是否是单例 ②确定是否在需要在关闭时销毁(这一步返回了false)
if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
if (mbd.isSingleton()) {
registerDisposableBean(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
else {
...
}
}
}
// AbstractBeanFactory#requiresDestruction
protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
return (bean.getClass() != NullBean.class &&
// 关键方法
(DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||
(hasDestructionAwareBeanPostProcessors() &&
DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessors()))));
}
// DisposableBeanAdapter#hasDestroyMethod
public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
// *使用@Service产生Bean时,类并没有实现AutoCloseable、DisposableBean
if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {
return true;
}
String destroyMethodName = beanDefinition.getDestroyMethodName();
// *使用@Service产生Bean时destroyMethodName是null,而使用使@Bean默认值为AbstractBeanDefinition.INFER_METHOD
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) {
return (ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME) ||
ClassUtils.hasMethod(bean.getClass(), SHUTDOWN_METHOD_NAME));
}
return StringUtils.hasLength(destroyMethodName);
}
shutdown
进行命名destroyMethod
属性设置为空的方式@Configuration
public class BeanConfiguration {
@Bean(destroyMethod="")
public LightService getTransmission(){
return new LightService();
}
}
极客时间-Spring 编程常见错误 50 例
https://github.com/jiafu1115/springissue/tree/master/src/main/java/com/spring/puzzle/class4