spring生命周期概述
spring Bean的生命周期是从Bean实例化之后,即通过反射创建对象之后,到Bean成为一个完整对象,最终存储在单例池中,然后在销毁的过程被称为spring Bean的生命周期,这部分不会介绍销毁过程,只要在从无到有的创建过程
Bean的实例化阶段:spring框架会从BeanDefinitionMap中取出BeanDefinition定义信息判断当前Bean的范围是否是singleton,是否是延迟加载,是否是工厂方法实例化等,最终通过反射创建出Bean对象
Bean的初始化阶段:Bean创建之后仅仅只是个“半成品”,还需要对Bean进行DI,也就是popluate属性填充,执行一些Aware接口方法,执行BeanPostProcessor#before方法,执行InitializingBean接口初始化方法,执行自定义初始化方法,BeanPostProcessor#after方法执行,该阶段是spring的重要阶段,Aop功能的实现,注解功能,循环依赖功能均在此阶段
Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的sping Bean,被存储到单例池singletonObjects中去了,到此为止,即完成了springBean的整个生命周期
spirng生命周期图
spring属性填充和三级缓存解决循环依赖
属性填充位置
BeanDefinition中有对当前Bean实体的注入信息通过属性propertyValues进行存储注入
属性注入规则
普通属性注入时,String,int或者存储基本类型的集合时,直接通过set方法的反射注入
注入单项对象引用属性时,先从容器中getBean获取后在通过set方法反射设置进去,如果容器中没有,则先创建对象Bean实例,在注入操作
注入双向对象引用属性时,即涉及到了循环引用问题(解决方案是三级缓存)
spring循环依赖问题
当两个对象互相引用对方对象的时候,就出现了循环引用问题,下面造一个循环引用的问题
package com.tech.test.testbean;
import com.tech.test.service.UserService;
public class DI {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
}
package com.tech.test.service.impl;
import com.tech.test.service.UserService;
import com.tech.test.testbean.DI;
public class UserServiceImpl implements UserService {
private DI di;
public void setDi(DI di) {
this.di = di;
}
@Override
public void getUser() {
System.out.println("get User Success");
}
}
<bean id="userService" name="aaa,bbb" class="com.tech.test.service.impl.UserServiceImpl" >
<property name="di" ref="di"></property>
</bean>
<bean id="di" class="com.tech.test.testbean.DI" autowire="byName">
<property name="userService" ref="userService"></property>
</bean>
在上边定义的循环引用问题是 userService对象引用了di对象,di对象又引用了userService对象,出现了循环引用问题,也就是死循环
解决循环依赖问题(三级缓存)
spring解决循环依赖问题引入了三级缓存
1、实例化对象,没有进行属性注入等初始化操作,会放入三级缓存,这时候的对象是一个半成品,没有被引用
2、当半成品对象被其他对象引用的时候,这个时候将对象放入二级缓存,删除三级缓存,
3、当引用对象的实例化和初始化都完成了进入一级缓存 ,删除二级缓存
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Cache of singleton objects: bean name to bean instance. */
// 存储单例Bean成品的容器,一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name to bean instance. */
// 早期的Bean单例池,缓存半成品对象,且对象已经被引用啦,二级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Cache of singleton factories: bean name to ObjectFactory. */
// 单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}
上述实例填充步骤:
1、userService实例化对象,分配内存地址,但尚未初始化,将userService存储到三级缓存
2、userService属性注入,需要DI对象,从缓存中取,没有DI对象
3、DI实例化对象,分配内存地址,但尚未初始化,将DI存入三级缓存
4、DI属性注入,需要userService,从三级缓存中取userService,userService从三级缓存中移除,放入二级缓存
5、DI执行初始化,最终成为一个完整的bean,存到一级缓存,移除二级缓存
6、userService 注入DI
7、userService执行初始化等流程,最终形成一个bean,放入一级缓存,移除二三级缓存
spring中的Aware接口
Aware接口是一种框架辅助属性注入的一种思想,简单直白的说,就是框架通过Aware接口给我们注入框架底层使用的对象,其他框架也有类似的Aware接口,框架具有高度封装性,底层API不能轻易的获取到,框架通过Aware接口给使用者注入该对象
在属性注入之后执行Aware方法
常见的Aware接口
Aware接口 | 回调方法 | 作用 |
ServletContextAware | setServletContext(ServletContext context) | spring框架回调方法注入servletContext对象,web环境生效 |
BeanFactoryAware | setBeanFactory(BeanFactory factory) | spring框架回调方法注入beanFactory对象 |
BeanNameAware | setBeanName(String beanName) | spring框架回调方法注入当前Bean在容器中的beanName |
ApplicationContextAware | setApplicationContext(ApplicationContext applicationContext) | spring回调方法注入applicationContext对象 |
package com.tech.test.service.impl;
import com.tech.test.service.UserService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class UserServiceImpl implements UserService, ApplicationContextAware, BeanNameAware {
@Override
public void getUser() {
System.out.println("get User Success");
}
@Override
public void setBeanName(String name) {
System.out.println(name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println(applicationContext);
}
}
可以看到 ,spring框架帮我们注入了bean的名称和applicationcontext对象