欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > 【Spring编程常见错误50例】04. Spring Bean 生命周期常见错误-上

【Spring编程常见错误50例】04. Spring Bean 生命周期常见错误-上

2025/1/30 14:12:08 来源:https://blog.csdn.net/jia970426/article/details/143989342  浏览:    关键词:【Spring编程常见错误50例】04. Spring Bean 生命周期常见错误-上

案例 1 构造器内空指针异常

在实际的开发中,会对用户操作,但是如果使用下面的方式,发现出现了异常。

@Repository
public class UserDao {public void saveData() {System.out.println("保存数据");}}
@Service
public class UserService {@Autowiredprivate UserDao userDao;public UserService() {userDao.saveData();}}

问题

Caused by: java.lang.NullPointerException: Cannot invoke "com.qxlx.beanlifecycle.UserDao.saveData()" because "this.userDao" is null

异常调用栈,其实异常调用栈是一个非常好的排查问题的方式,可以定位到框架执行的关键调用链路,然后分析最终异常的点,除了这种方式,还有就是如果不知道一个地方在什么时候调用的,可以通过debug的方式,直接看用调用栈。

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in file [/Users/qxlx/work/source/spring-code/target/classes/com/qxlx/beanlifecycle/UserService.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.qxlx.beanlifecycle.UserService]: Constructor threw exception; nested exception is java.lang.NullPointerException: Cannot invoke "com.qxlx.beanlifecycle.UserDao.saveData()" because "this.userDao" is nullat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1302)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1196)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:847)at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:89)at com.qxlx.beanlifecycle.SpringApplication.main(SpringApplication.java:12)

原因分析

在这里插入图片描述
上面的整体流程,注册配置类,以及加载公共的资源类,并初始化成对象。然后在初始化用户类对象。

	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {this();register(componentClasses);refresh();}

在这里插入图片描述

创建bean核心代码,可以看到 整体的流程,就是先创建对象实例,然后 填充实例需要的属性,然后执行初始化方法。

	protected Object doCreateBean() {// 其他代码if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}populateBean(beanName, mbd, instanceWrapper);exposedObject = initializeBean(beanName, exposedObject, mbd);// 其他代码}

实例化Bean,可以看到 因为没有设置构造参数,使用默认参数。但是这里构造方法执行userDao的方法,显然userDao 还没有注入。

public static <T> T instantiateClass(Constructor<T> ctor, Object... args)  {return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));}

解决方案

方式1

直接在构造函数内 引入,这样当实例话bean的实例,发现需要参数UserDao,就会先依赖查找userDao。

    @Autowiredprivate UserService (UserDao userDao) {userDao.saveData();}

方式2 @PostConstruct注解

在实例化之后,Spring还提供了默认的一些可拓展方法,

exposedObject = initializeBean(beanName, exposedObject, mbd);wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization 具体可以对应流程图和代码梳理逻辑。

在这里插入图片描述

    @Autowiredprivate UserDao userDao;@PostConstructpublic void init () {userDao.saveData();}private UserService () {}

方式3 实现 InitializingBean

通过提供高度封装的接口,用户如果实现了该接口,会回调用户的afterPropertiesSet方法。或者用户提供自定义的初始化方法,Spring通过解析获取,然后通过反射调用。

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"))) {((InitializingBean) bean).afterPropertiesSet();}
}
	// 判断bd不为空 并且这个bean不是一个空Beanif (mbd != null && bean.getClass() != NullBean.class) {// 从bd中获取初始化方法String initMethodName = mbd.getInitMethodName();// 该方法不为空 & 不是 afterPropertiesSet方法if (StringUtils.hasLength(initMethodName) &&!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&!mbd.isExternallyManagedInitMethod(initMethodName)) {// 调用用户自定义的方法-反射调用invokeCustomInitMethod(beanName, bean, mbd);}}
	Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);ReflectionUtils.makeAccessible(methodToInvoke);methodToInvoke.invoke(bean);
public class UserService implements InitializingBean {@Autowiredprivate UserDao userDao;@Overridepublic void afterPropertiesSet() throws Exception {userDao.saveData();}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com