Bean 实例化的基本流程
Spring容器在进行初始化时,会将xml配置的<bean>的信息封装成一个BeanDefinition对象,所有的BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去,Spring框架在对该Map进行遍历,使用反射创建Bean实例对象,创建好的Bean对象存储在一个名为singletonObjects的Map集合中,当调用getBean方法时则最终从该Map集合中取出Bean实例对象返回。
Bean信息定义对象-BeanDefinition
<bean id=""
class=""
name=""
lazyinit=""
scope=""
init-method=""
destroy-method=""
factory-bean=""
factory-method=""
abstract=""
depends-on=""
parent="">
<property name="" ref=""/>
<property name="" ref=""/>
<property name="" value=""/>
</bean>
public interface BeanDefinition {
String SCOPE_SINGLETON = "singleton";
String SCOPE_PROTOTYPE = "prototype";
void setBeanClassName(@Nullable String var1);
String getBeanClassName();
void setScope(@Nullable String var1);
String getScope();
void setLazyInit(boolean var1);
boolean isLazyInit();
void setFactoryBeanName(@Nullable String var1);
String getFactoryBeanName();
void setFactoryMethodName(@Nullable String var1);
String getFactoryMethodName();
void setInitMethodName(@Nullable String var1);
String getInitMethodName();
//..... 省略部分属性和方法
}
DefaultListableBeanFactory对象内部维护着一个Map用于存储封装好的BeanDefinitionMap
public class DefaultListableBeanFactory extends ... implements ... {
//存储<bean>标签对应的BeanDefinition对象
//key:是Bean的beanName,value:是Bean定义对象BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap;
}
Spring框架会取出beanDefinitionMap中的每个BeanDefinition信息,反射构造方法或调用指定的工厂方法生成Bean实例对象,所以只要将BeanDefinition注册到beanDefinitionMap这个Map中,Spring就会进行对应的Bean的实例化操作
Bean实例及单例池singletonObjects, beanDefinitionMap中的BeanDefinition会被转化成对应的Bean实例对象 ,存储到单例池singletonObjects中去,在DefaultListableBeanFactory的上四级父类 DefaultSingletonBeanRegistry中,维护着singletonObjects,源码如下:
public class DefaultSingletonBeanRegistry extends ... implements ... {
//存储Bean实例的单例池
////key:是Bean的beanName,value:是Bean的实例对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
}
- 加载
xml,解析获取配置中的每个<bean>的信息,封装成一个个的BeanDefinition对象; - 将
BeanDefinition存储在一个名为beanDefinitionMap的Map<String,BeanDefinition>中; ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象;- 创建好的
Bean实例对象,被存储到一个名为singletonObjects的Map<String,Object>中; - 当执行
applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回。
Spring的后处理器
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
BeanFactoryPostProcessor
Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;
BeanPostProcessor
Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。
BeanFactoryPostProcessor
BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}
编写BeanFactoryPostProcessor
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException{
System.out.println("MyBeanFactoryPostProcessor执行了...");
}
}
配置BeanFactoryPostProcessor
<bean class="com.itheima.processor.MyBeanFactoryPostProcessor"/>
BeanDefinition修改操作
postProcessBeanFactory参数本质就是 DefaultListableBeanFactory,拿到BeanFactory的引用,自然就可以对beanDefinitionMap中的BeanDefinition进行操作了 ,例如对UserDaoImpl的BeanDefinition进行修改操作
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
BeanDefinition userDaoBD = beanFactory.getBeanDefinition(“userDao”);//获得UserDao定义对象
userDaoBD.setBeanClassName("com.itheima.dao.impl.UserDaoImpl2"); //修改class
//userDaoBD.setInitMethodName(methodName); //修改初始化方法
//userDaoBD.setLazyInit(true); //修改是否懒加载
//... 省略其他的设置方式 ...
}
}
BeanDefinition注册操作
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory
(ConfigurableListableBeanFactory configurableListableBeanFactory)
throws BeansException {
//强转成子类DefaultListableBeanFactory
if(configurableListableBeanFactory instanceof DefaultListableBeanFactory){
DefaultListableBeanFactory beanFactory =
(DefaultListableBeanFactory)configurableListableBeanFactory;
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName("com.itheima.dao.UserDaoImpl2");
//进行注册操作
beanFactory.registerBeanDefinition("userDao2",beanDefinition);
}
}
}
BeanDefinitionRegistryPostProcessor 注册
Spring提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作
public class MyBeanFactoryPostProcessor2 implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory
(ConfigurableListableBeanFactory configurableListableBeanFactory)
throws BeansException {}
@Override
public void postProcessBeanDefinitionRegistry
(BeanDefinitionRegistry beanDefinitionRegistry)
throws BeansException {
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName("com.itheima.dao.UserDaoImpl2");
beanDefinitionRegistry.registerBeanDefinition("userDao2",beanDefinition);
}
}
使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描
自定义@MyComponent注解,使用在类上;
使用资料中提供好的包扫描器工具BaseClassScanUtils完成指定包的类扫描;
自定义BeanFactoryPostProcessor完成注解@MyComponent的解析,解析后最终被Spring管理。
public class BaseClassScanUtils {
//设置资源规则
private static final String RESOURCE_PATTERN = "/**/*.class";
public static Map<String, Class> scanMyComponentAnnotation(String basePackage) {
//创建容器存储使用了指定注解的Bean字节码对象
Map<String, Class> annotationClassMap = new HashMap<String, Class>();
//spring工具类,可以获取指定路径下的全部类
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
try {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;
Resource[] resources = resourcePatternResolver.getResources(pattern);
//MetadataReader 的工厂类
MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
//用于读取类信息
MetadataReader reader = refractory.getMetadataReader(resource);
//扫描到的class
String classname = reader.getClassMetadata().getClassName();
Class<?> clazz = Class.forName(classname);
//判断是否属于指定的注解类型
if(clazz.isAnnotationPresent(MyComponent.class)){
//获得注解对象
MyComponent annotation = clazz.getAnnotation(MyComponent.class);
//获得属value属性值
String beanName = annotation.value();
//判断是否为""
if(beanName!=null&&!beanName.equals("")){
//存储到Map中去
annotationClassMap.put(beanName,clazz);
continue;
}
//如果没有为"",那就把当前类的类名作为beanName
annotationClassMap.put(clazz.getSimpleName(),clazz);
}
}
} catch (Exception exception) {
}
return annotationClassMap;
}
}
public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
//通过扫描工具去扫描指定包及其子包下的所有类,收集使用@MyComponents的注解的类
Map<String, Class> map = BaseClassScanUtils.scanMyComponentAnnotation("com.chen");
map.forEach((beanName, clazz) -> {
String beanClassName = clazz.getName();
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName(beanClassName);
beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
});
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
BeanPostProcessor
Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程,例如:属性的填充、初始方法init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。
配置MyBeanPostProcessor
<bean class="com.itheima.processors.MyBeanPostProcessor"></bean>
测试台打印结果
userDao实例化
userDao:postProcessBeforeInitialization
属性设置之后执行:afterPropertiesSet
init初始化方法执行
userDao:postProcessAfterInitialization
对Bean方法进行执行时间日志增强
编写BeanPostProcessor,增强逻辑编写在 after方法中
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//对Bean进行动态代理,返回的是Proxy代理对象
Object proxyBean = Proxy.newProxyInstance(
bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//输出开始时间
System.out.println("method:" + method.getName() + " start time:" + new Date());
//执行目标方法
Object result = method.invoke(bean, args);
//输出结束时间
System.out.println("method:" + method.getName() + " end time" + new Date());
return result;
}
}
});
//返回代理对象
return proxyBean;
}
Bean后处理器 – BeanPostProcessor
Spring Bean的生命周期
Spring Bean的生命周期是从 Bean实例化之后,即通过反射创建出对象之后,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期。Spring Bean的生命周期大体上分为三个阶段:
Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化;
Bean的初始化阶段:Bean创建之后还仅仅是个"半成品",还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,后面要学习的Spring的注解功能等、spring高频面试题Bean的循环引用问题都是在这个阶段体现的;
Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池 singletonObjects中去了,即完成了Spring Bean的整个生命周期。
于Bean的初始化阶段的步骤比较复杂,所以着重研究Bean的初始化阶段
Spring Bean的初始化过程涉及如下几个过程: Bean实例的属性填充 Aware接口属性注入 BeanPostProcessor的before()方法回调 InitializingBean接口的初始化方法回调 自定义初始化方法init回调 BeanPostProcessor的after()方法回调
Bean实例属性填充
BeanDefinition 中有对当前Bean实体的注入信息通过属性propertyValues进行了存储,例如UserService的属性信息如下
Spring在进行属性注入时,会分为如下几种情况:
注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;
注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;
注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题,下面会详细阐述解决方案。
循环引用
public class UserServiceImpl implements UserService{
public void setUserDao(UserDao userDao) {}
}
public class UserDaoImpl implements UserDao{
public void setUserService(UserService userService){}
}
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="userService" ref="userService"/>
</bean>
Spring提供了三级缓存存储 完整Bean实例 和 半成品Bean实例 ,用于解决循环引用问题 在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:
public class DefaultSingletonBeanRegistry ... {
//1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"
Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"
Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存"
Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
UserService和UserDao循环依赖的过程结合上述三级缓存描述一下
UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存;
UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao;
UserDao实例化对象,但尚未初始化,将UserDao存储到到三级缓存;
UserDao属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移入二级缓存;
UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;
UserService 注入UserDao;
UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。
常用的Aware接口
Aware接口是一种框架辅助属性注入的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接触到的一般都是业务代码,一个底层功能API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了,就可以使用框架提供的类似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 对象 |