大家好,又见面了,我是你们的朋友全栈君。
首先需要说明的是,Bean的生命周期主要指的是singleton bean,标签scope默认就是singleton。对prototype bean来说,当用户getBean获得prototype bean的实例后,IOC容器就不再对当前实例进行管理,而是把管理权交由用户,此后再getBean生成的是新的实例。
普通Java Bean和Spring Bean
- 普通java对象就是new出来,然后不再使用的时候通过垃圾回收机制进行回收;
- 而spring Bean是由spring容器来控制的,并且在创建的时候,赋予了一些特殊处理;
有关 Java Bean, Spring Bean 和 Spring IoC 容器有一个很形象的比喻:小学生 (Java Bean)通过提交资料申请(元数据配置)加入了少先队(Spring Ioc 容器),学习了一些精神与规定之后,变成了少先队员(Spring Bean)。
从这里可以看出,Java Bean 和 Spring Bean 都是具有特定功能的对象,小学生还是那个小学生,只不过加入了少先队之后有了新的身份,新的身份要按照组织 (Spring Ioc)的规定履行特定义务。
bean的生命周期 经典四步
由源码可知,最核心的逻辑就是四步:实例化 –> 填充属性 –> 初始化 –> 销毁
代码语言:javascript复制protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
if (instanceWrapper == null) {
//实例化
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
…………………………忽略其他代码
try {
//属性赋值
this.populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
//初始化
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
}
} catch (Throwable var18) {
if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
throw (BeanCreationException)var18;
}
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
}
…………………………………………忽略其他代码
try {
//销毁
this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
return exposedObject;
} catch (BeanDefinitionValidationException var16) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
}
}
实例化
实例化是bean生命周期中至关重要的一环,如何实例化呢?spring基于类,根据类的无参构造函数,反射得到一对象,这时候的对象,可以称为原始对象,因为最后得到bean,其实就是这个时候得对象,只不过是经过了一系列的处理。
属性赋值
对象实例化出来了,但是对象里边的属性,也得需要spring容器来自动赋值。
比如AServiceImpl依赖了BService,那么这时候就用到@Autowired或@Resource注解,所以属性赋值的原理就是依赖注入,也就是这两个注解的原理。
这两注解都可以用在属性和方法上,@Autowired是属于spring的,@Resource是数据java规范的;@Autowired是按byType注入,@Resource是默认按byName注入;
上一副图看看这两注解的区别:
初始化
属性赋值完了就是初始化了,但是初始化该如何理解呢?就是我们在调用前,对象的某些属性有我们想要的值。以下是三种初始化方式:
1. @PostConstruct
代码语言:javascript复制public class UserService implements InitializingBean {
@Autowired
private OrderService orderService;
//这种方式是 Spring 非常提倡的一种方式,我们通常将其标记在方法上即可,通常习惯将这个方法起名为 init()
private final Map<String, String> map = new HashMap<>();
@PostConstruct
public void init(){
this.map.put("支付", "1");
this.map.put("部分支付", "2");
this.map.put("未支付", "3");
}
}
2、InitializingBean.afterPropertiesSet()
代码语言:javascript复制public class UserService implements InitializingBean {
@Autowired
private OrderService orderService;
//我们可以通过Spring 为我们提供的 InitializingBean 接口中的方法 afterPropertiesSet 内完成实例化的工作,
//但是 Spring Framework 官方并不建议我们通过这种方法来完成 Bean 的实例化,这是一种强耦合的方式,我们看到框架层面才会用到这个方法。
@Override
public void afterPropertiesSet() throws Exception {
this.map.put("支付", "1");
this.map.put("部分支付", "2");
this.map.put("未支付", "3");
}
}
3、xml配置
<bean id=”myClass” class=”com.demo.MyClass” init-method=”init”/>
以上就是三种初始化 Spring Beans 的方式,我们在框架中看到过三种方式在组合使用,那么组合使用的调用顺序是什么呢?
1、首先@PostConstruct 会被最先调用
2、其次 InitializingBean.afterPropertiesSet()
方法将会被调用
3、最后调用通过 XML 配置的 init-method 方法或通过设置 @Bean 注解 设置 initMethod 属性的方法
销毁
- 销毁和初始化的方法一样,方法一:使用注解@PreDestroy;方法二:实现接口DisposableBean,重写destroy()方法;方法三:xml配置指定destroy-method=”destroyMethod”
- 容器销毁的时候,会销毁单例bean,但是容器什么时候销毁呢???
实例化前的处理
class –> BeanDefinition –> 推断构造方法 –> 实例化
BeanDefinition
bean的定义,spring扫描到类,将类的信息或者xml的信息保存到BeanDefinition结构,生成BeanDefinition;这个结构包括beanClass:bean的类型是什么、scope:bean的作用域、isLaze:),将类的属性放入一个map,后边有获取这个bean的时候,就可以通过这个map来获取bean的一些定义(比如scope就可以判断作用域,如果是单例的,直接获取,如果是原型,直接创建)
推断构造方法
后边的实例化需要根据构造函数来生成对象,如果类里边只有一个无参构造函数,那就一切soEasy了,,但是如果,有自定义的有参构造函数,那spring实例化的的时候就麻烦了,到底该使用哪个构造函数嘞。。所以就有了推断构造函数,先bytype后byname,选择可用的构造方法。
初始化后的处理
初始化 –> AOP –> userService代理对象 –> bean
AOP:我们常说的aop就是在初始化之后,aop后得到的对象是一个代理对象。
如何知道需要aop呢?spring会也会扫描所有的切面bean,拿到切面bean中的@Before等注解中的切点,和当前类,进行匹配,如果有匹配的切点,就进行aop,得到一个代理对象,最后bean就是代理对象。aop的实现是基于动态代理:参考博客
初始化
接下来就是重头戏初始化,
借用一张图,5、6是真实的初始化,在前边我们已经做过说明;3、4是在初始化之前;7是初始化之后;接下来我们就说说Aware和BeanPostProcessor。
我们先点进初始化代码initializeBean()瞅瞅
代码语言:javascript复制protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
// 3. 检查 Aware 相关接口并设置相关依赖
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
AbstractAutowireCapableBeanFactory.this.invokeAwareMethods(beanName, bean);
return null;
}
}, this.getAccessControlContext());
} else {
this.invokeAwareMethods(beanName, bean);
}
// 4. BeanPostProcessor 前置处理
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
}
// 5. 若实现 InitializingBean 接口,调用 afterPropertiesSet() 方法
// 6. 若配置自定义的 init-method方法,则执行
try {
this.invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable var6) {
throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
}
// 7. BeanPostProceesor 后置处理
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
Aware
Aware类型的接口的作用就是让我们能够拿到Spring容器中的一些资源。基本都能够见名知意,Aware之前的名字就是可以拿到什么资源,例如BeanNameAware
可以拿到BeanName,以此类推。调用时机需要注意:所有的Aware方法都是在初始化阶段之前调用的!
若 Spring 检测到 bean 实现了 Aware 接口,则会为其注入相应的依赖。所以通过让bean 实现 Aware 接口,则能在 bean 中获得相应的 Spring 容器资源,类似这样:
代码语言:javascript复制public class UserService implements InitializingBean, BeanNameAware, BeanClassLoaderAware {
@Autowired
private OrderService orderService;
@Override
public void afterPropertiesSet() throws Exception {
this.map.put("支付", "1");
this.map.put("部分支付", "2");
this.map.put("未支付", "3");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
}
@Override
public void setBeanName(String name) {
}
}
spring提供的Aware接口有很多:
BeanFactory 类型的容器提供的aware接口:
- BeanNameAware:注入当前 bean 对应 beanName;
- BeanClassLoaderAware:注入加载当前 bean 的 ClassLoader;
- BeanFactoryAware:注入 当前BeanFactory容器 的引用。
ApplicationContext 类型的容器提供的aware接口:
- EnvironmentAware:注入 Enviroment,一般用于获取配置属性;
- EmbeddedValueResolverAware:注入 EmbeddedValueResolver(Spring EL解析器),一般用于参数解析;
- ApplicationContextAware(ResourceLoader、ApplicationEventPublisherAware、MessageSourceAware):注入 ApplicationContext 容器本身。
BeanPostProcessor
BeanPostProcessor 接口,大家也应该有印象,里面只有两个方法:
代码语言:javascript复制public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object var1, String var2) throws BeansException;
Object postProcessAfterInitialization(Object var1, String var2) throws BeansException;
}
看方法名,BeforeInitialization 和 AfterInitialization,我们应该猜得出,这是在上述三种方式的前和后,算是一种全局的切面思想,我们经常会使用 postProcessAfterInitialization
方法,通过读取 Bean 的注解完成一些后续逻辑编写与属性的设定。
整合
所以比较完整的生命周期应该是:
扫描class –> BeanDefinition –> 推断构造方法 –> 实例化 –> 原始对象 –> 填充属性 –> Aware相关接口处理 –> BeanPostProcessor前置处理 –> 初始化 –> BeanPostProcessor后置处理 –> (AOP –> userService代理对象) –> bean
先记住四个主要步骤,然后每个步骤在细化理解,这样就可以得到一个比较完整的bean的生命周期。有不完整或错误的地方,感谢各位指教。
参考博客:参考一 参考二 参考三 参考四
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/192870.html原文链接:https://javaforall.cn