一个 Bean 就这样走完了它的一生之 Bean 的出生
生命周期流程
Spring 中的一个 Bean 从被创建到被销毁,需要经历很多个阶段的生命周期,下图是一个 Bean 从创建到销毁的生命周期流程:
在 Bean 的各个生命周期流程点,Spring 都提供了对应的接口或者注解,以便开发者在各个生命周期的流程点能够做一些自己的操作。
案例解析
定义 Spring 上下文工具类
Spring 中生命周期最常见的应用可能是定义一个 Spring 上下文的工具类。这个工具类也使用 @Component
注解修饰,表明它也是一个 Bean ,其次它实现了 ApplicationContextAware
接口,则说明它作为一个 Bean 被创建以及初始化的过程中需要调用 setApplicationContext()
方法,设置它所在的 Spring 上下文。代码如下:
@Component
public class SpringContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
/**
* Spring会自动调用这个方法,注入ApplicationContext
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
}
/**
* 获取ApplicationContext
* @return ApplicationContext
*/
public static ApplicationContext getApplicationContext() {
if (applicationContext == null) {
throw new IllegalStateException("ApplicationContext is not set. Make sure SpringContextUtils is properly initialized.");
}
return applicationContext;
}
/**
* 通过名称获取Bean
* @param name Bean的名称
* @return Bean实例
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过名称和类型获取Bean
* @param name Bean的名称
* @param requiredType Bean的类型
* @param <T> Bean的类型
* @return Bean实例
*/
public static <T> T getBean(String name, Class<T> requiredType) {
return getApplicationContext().getBean(name, requiredType);
}
/**
* 通过类型获取Bean
* @param requiredType Bean的类型
* @param <T> Bean的类型
* @return Bean实例
*/
public static <T> T getBean(Class<T> requiredType) {
return getApplicationContext().getBean(requiredType);
}
}
在 Bean 的依赖注入之后执行初始化操作
比如下面的案例中,MyService
这个 Bean 需要在它的依赖 MyRepository
这个 Bean 注入完成之后,调用依赖的 loadInitialData()
方法加载初始数据。代码如下:
@Service
public class MyService {
private MyRepository myRepository;
private List<String> initialData;
@Autowired
public void setMyRepository(MyRepository myRepository) {
this.myRepository = myRepository;
}
// 依赖注入完成后执行的初始化方法
@PostConstruct
public void init() {
this.initialData = myRepository.loadInitialData();
}
public void doBusinessLogic() {
}
}
@Service
class MyRepository {
public List<String> loadInitialData() {
}
}
@PostConstruct
注解是 JSR-250 标准定义的注解,它与 Spring 框架的耦合度比较低。除此之外还可以实现 InitializingBean
接口,在它的 afterPropertiesSet()
方法中来完成初始化;通过 XML 配置 init-method
或者 @Bean
注解的 initMethod
属性来指定任意的方法作为初始化方法来完成初始化。
Bean 创建源码解析
在 Spring 源码实现中实际上分为了三个大的步骤:实例化 -> 填充属性 -> 初始化 。填充属性可以看前面的文章[Spring 中@Autowired,@Resource,@Inject 注解实现原理](https://mp.weixin.qq.com/s/WVpWDU2JXFbpl-hgsywUXQ)。在上面生命周期图片中的从 XXXAware
的 setXXXAware()
方法到 postProcessAfterInitialization()
都属于初始化的这个步骤中。
在 AbstractAutowireCapableBeanFactory
中提供的 doCreateBean()
方法中提现了这三个大的步骤,其中的 createBeanInstance()
方法完成 Bean 的实例化;populateBean()
方法完成 Bean的属性填充;initializeBean()
方法完成 Bean 的初始化。代码如下:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd,
@Nullable Object[] args) throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
//实例化Bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton()
&& this.allowCircularReferences
&& isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName,
() -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//填充Bean的属性,比如处理@Autowired,@Resource,@Inject注解
populateBean(beanName, mbd, instanceWrapper);
//初始化Bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch {
}
}
initializeBean()方法流程
在 initializeBean()
方法中又分为:调用 invokeAwareMethods() 方法 -> 调用 applyBeanPostProcessorsBeforeInitialization() 方法 -> 调用 invokeInitMethods() 方法 -> 调用 applyBeanPostProcessorsAfterInitialization() 方法,代码如下:
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
//调用Aware()方法
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//调用BeanPostProcessor的postProcessBeforeInitialization()方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//调用初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
}
if (mbd == null || !mbd.isSynthetic()) {
//调用BeanPostProcessor的postProcessAfterInitialization()方法
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
invokeAwareMethods()方法流程
需要注意的是 invokeAwareMethods()
方法中仅仅只调用实现了 BeanNameAware
,BeanClassLoaderAware
,BeanFactoryAware
接口的方法。而常见的 ApplicationContextAware
接口的 setApplicationContext()
方法则是在 ApplicationContextAwareProcessor
的 postProcessBeforeInitialization()
方法中调用的。代码如下:
public abstract class AbstractAutowireCapableBeanFactory {
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware beanNameAware) {
//调用setBeanName()方法
beanNameAware.setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware beanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
//调用setBeanClassLoader()方法
beanClassLoaderAware.setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware beanFactoryAware) {
//调用setBeanFactory()方法
beanFactoryAware.setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
}
class ApplicationContextAwareProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Aware) {
this.invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware environmentAware) {
environmentAware.setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware embeddedValueResolverAware) {
embeddedValueResolverAware.setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware resourceLoaderAware) {
resourceLoaderAware.setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware applicationEventPublisherAware) {
applicationEventPublisherAware.setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware messageSourceAware) {
messageSourceAware.setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationStartupAware applicationStartupAware) {
applicationStartupAware.setApplicationStartup(this.applicationContext.getApplicationStartup());
}
if (bean instanceof ApplicationContextAware applicationContextAware) {
//这里调用的setApplicationContext()方法
applicationContextAware.setApplicationContext(this.applicationContext);
}
}
}
applyBeanPostProcessorsBeforeInitialization() 方法流程
在该方法中主要就是查找所有实现了 BeanPostProcessor
接口的对象,然后循环调用其 postProcessBeforeInitialization()
方法。代码如下:
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;
}
在 Spring 中提供了 CommonAnnotationBeanPostProcessor
(@Resource
注解也是它处理的) 实现了 BeanPostProcessor
接口,在它的构造函数里面初始化了要处理 @PostConstruct
注解。代码如下:
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
// Jakarta EE 9 set of annotations in jakarta.annotation package
addInitAnnotationType(loadAnnotationType("jakarta.annotation.PostConstruct"));
addDestroyAnnotationType(loadAnnotationType("jakarta.annotation.PreDestroy"));
// Tolerate legacy JSR-250 annotations in javax.annotation package
addInitAnnotationType(loadAnnotationType("javax.annotation.PostConstruct"));
addDestroyAnnotationType(loadAnnotationType("javax.annotation.PreDestroy"));
}
然后在它的子类 InitDestroyAnnotationBeanPostProcessor
的 postProcessBeforeInitialization()
实现了查找 @PostConstruct
注解修饰的方法,然后调用的逻辑。代码如下:
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}
invokeInitMethods() 方法流程
在该方法中会先判断 Bean 是否实现了 InitializingBean
接口,如果实现了则调用其 afterPropertiesSet()
方法,然后查看 Bean 定义中是否有自定义的初始化方法,如果有的话,则调用自定义的初始化方法。代码如下:
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
//调用afterPropertiesSet()方法
((InitializingBean) bean).afterPropertiesSet();
}
if (mbd != null && bean.getClass() != NullBean.class) {
String[] initMethodNames = mbd.getInitMethodNames();
if (initMethodNames != null) {
for (String initMethodName : initMethodNames) {
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
//调用自定义初始化方法
invokeCustomInitMethod(beanName, bean, mbd, initMethodName);
}
}
}
}
}
protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd, String initMethodName)
throws Throwable {
Class<?> beanClass = bean.getClass();
MethodDescriptor descriptor = MethodDescriptor.create(beanName, beanClass, initMethodName);
String methodName = descriptor.methodName();
Method initMethod = (mbd.isNonPublicAccessAllowed() ?
BeanUtils.findMethod(descriptor.declaringClass(), methodName) :
ClassUtils.getMethodIfAvailable(beanClass, methodName));
//省略代码
Method methodToInvoke = ClassUtils.getPubliclyAccessibleMethodIfPossible(initMethod, beanClass);
try {
ReflectionUtils.makeAccessible(methodToInvoke);
//这里通过反射的方式调用初始化方法
methodToInvoke.invoke(bean);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
applyBeanPostProcessorsBeforeInitialization() 方法流程
在该方法中主要就是查找所有实现了 BeanPostProcessor
接口的对象,然后循环调用其 postProcessAfterInitialization()
方法。代码如下:
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
基于 TCP 与领域事件的 kaka-rpc 2.0.0 正式发布
基于Netty TCP与kaka-core领域事件的轻量级RPC框架,相比其它使用各种繁杂设计模式的框架,本框架仅10多个类实现,可谓是极简编码。 基础通信 事件驱动 自定义心跳协议或基于rpc调用的心跳处理 断线重连 同步、future异步、callback回调多种rpc调用模式 超时控制 自定义数据协议扩展领域事件 登录鉴权 基础数据类型在满足通信协议的要求下支持任意程序语言调用 集群部署 方案一:Nginx负载均衡 方案二:自定义负载均衡,基于一致性哈希算法,亦可使用心跳轮询基于连接量的动态权重负载均衡 性能强劲 调用远程接口方法,顺序执行1000000次,耗时78秒,平均每秒12800次左右,相比其它基于Http协议的RPC框架,性能提升明显 异步回调模式,每秒60000次左右 以上性能数据均在 win11 i5-12400 16GB内存 IDEA 下运行产生 以下为部分示例程序,具体示例请参看源码test目录 final CountDownLatch latch = new CountDownLatch(1); InetSocketAddress address = n...
-
下一篇
如何用好“对话式编程”?牢记这十二条策略
编者按: 如何有效利用大语言模型(LLMs)生成高质量代码?这是当下开发者们比较关心的一个问题。在生成代码的过程中,提示词的设计是否精确,直接决定了模型输出的质量。 本文深入探讨了提示词优化的 12 条策略,给出了清晰的操作指南和示范案例,读者可以了解到如何通过精准编写提示词引导模型生成性能优越、符合实际需求的代码。 作者 | Ayush Thakur@Potpie (https://github.com/potpie-ai/potpie) 编译 | 岳扬 大语言模型(LLMs)已经彻底改变了代码生成领域,但要想获得高质量、有用的输出结果,编写有效的提示词至关重要。LLMs 生成代码的质量高度依赖于所提供提示词的质量。一句表述不当的提示词可能导致不完整、不正确或虽然正确但不具备针对性的响应,而逻辑性、完整性和易读性良好的提示词则能最大化发挥模型的潜力。 本文将探讨编写有效提示词的高级策略,以便使用 LLMs 生成高质量的代码。 01 提供详细的上下文 在与 LLMs 进行交互生成代码时,所提供上下文的深度和质量直接影响模型输出的相关程度和准确程度。 上下文需要包含的关键要素有: Spe...
相关文章
文章评论
共有0条评论来说两句吧...