概述
概述
本文主要介绍生命周期中属性填充,涉及到spring 的自动注入,@Autowired注解,以及属性填充相关后置处理器的实现
Bean的生命周期再笔者《spring中bean的生命周期总览》一文中已有介绍,这里不再过多介绍
属性填充
直接进入属性填充的相关方法 :populateBean(beanName, mbd, instanceWrapper)
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
return;
}
}
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
}
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
在这个方法里面有三个参数
String beanName : bean的name
RootBeanDefinition mbd : 合并后的RootBeanDefintion
BeanWrapper bw : 实例化后的Bean的包装类
这个方法比较长,可以分为三个模块
模块一: 提供了一个扩展点,再这个扩展点,可以通过继承BeanPostProcessor对BeanDefinition进行操作
模块二:ByName和ByType自动注入的实现
模块三:注解的自动注入的实现
模块一
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
}
这里可以算是spring提供的一个扩展点,在这里可以通过继承InstantiationAwareBeanPostProcessor这个后置处理器对BeanDefinition进行修改
但是这不免有个疑问,进入这个方法的时候,spring已经完成了类的加载已经相关bean的实例化,那么修改BeanDefinition又有什么意义?
首先按照笔者的理解,spring的Bean和java的Bean是不一样的,一个完整的spring的bean,笔者认为是需要经过一个完整的spring的bean的生命周期的,所以笔者认为这个阶段的Bean不是一个springBean,或者说是一个不完整的springBean,而再后序阶段的生命周期中也的的确确使用到了BeanDefinition里面的信息,例如自动注入,例如生命周期回调,所以这里对BeanDefintion的修改一定是有意义的
模块二
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
这里会对byType 和 byName的自动注入模型进行处理,那么@Autowired是在这里处理的么?
@Autowired并不是在这里进行处理的,这里一般是处理xml配置的自动注入,这里不免更有疑问了,同样是自动注入,为什么注解的自动配置不是在这里进行配置的呢?
spring的发展是有一个过程的,我们都知道在以往的spring中,都是采用xml配置的方法,而注解功能是后来加入到spring中的,对于以往的xml配置而言,在进行对xml解析的时候,就会将标签中的autowire 这个属性封装到BeanDefinition中,但是对于注解而言,是没有autowire这个属性的,所以根本不会进入到这个判断中。同时注解的自动注入支持更加强大的功能,例如@Autowired会先byType再byName,所以对于注解的自动注入需要有单独的一套现实
还是回到当前的代码,来看看对于这一块的自动注入,spring是如何实现的
1)在当前创建的BeanDefintion中去获取PropertyValues 这个数据结构,这个里面存储的是我们当前生成Bean中的属性以及对应的值,也
就是说,如果我们使用xml配置手动的设置了注入的属性,那么在对xml解析的时候,就会存放在这个数据结构中,如果没有手动配置,
那么当前这个属性对应的就是空,这里的自动注入,是先将要注入的值找到放入PropertyValues这个数据结构中,然后统一的对当前真正
创建的Bean进行赋值
2) 判断具体的自动注入模型是 byName 还是 byType,然后调用对应的方法
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
// 找到有对应set方法的属性
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
if (containsBean(propertyName)) {
// 根据属性名去找bean,这就是byName
Object bean = getBean(propertyName);
// 给属性赋值
pvs.add(propertyName, bean);
registerDependentBean(propertyName, beanName);
...
}
else {
...
}
}
}
1))通过unsatisfiedNonSimpleProperties 方法 找到所有的set方法所对应的名字,例如setAAA,那么找到的名字就是AAA
这里也就解释了为什么set方法的方法名要与待填充的属性名匹配
protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
Set<String> result = new TreeSet<>();
PropertyValues pvs = mbd.getPropertyValues(); // 在BeanDefinition中添加的属性和值
PropertyDescriptor[] pds = bw.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
result.add(pd.getName());
}
}
return StringUtils.toStringArray(result);
}
在这个方法里面又出现了一个新的数据结构,PropertyDescriptor翻译过来就是属性的描述,对于这个数据结构,其实如果一个类里面一个属性也没有也可能会生成一个属性的描述,因为这个属性的描述不是去找属性,而是找set方法,例如setAAA,那么找到的属性描述就是AAA这个类了,会将set方法看成一个对象
在这个数据结构里面又有五个重要的属性:
beanClass:这个属性所属的class
readMethod:存储的是对应的get方法
writeMethod:存储的是对应的set方法
writeMethodParameter:保存着writeMethod(也就是set方法的)参数和参数的类型
name:set方法所对应的名字,例如setAAA,解析出来的名字就是aAA
如果没有提供对应的set/get方法,那么这里就会是null
最后进过一些校验,将这些方法的描述的名字转为数组然后返回
2))然后遍历这些名字,在遍历的时候会进行一个判断 containsBean(propertyName)
进入这个判断里面
public boolean containsBean(String name) {
String beanName = transformedBeanName(name);
if (containsSingleton(beanName) || containsBeanDefinition(beanName)) {
return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(name));
}
// Not found -> check parent.
BeanFactory parentBeanFactory = getParentBeanFactory();
return (parentBeanFactory != null && parentBeanFactory.containsBean(originalBeanName(name)));
}
containsSingleton(beanName) || containsBeanDefinition(beanName)
这两个判断的意思是,判断当前单例池里面是否有这个Bean,或者当前的BeanDefinitionMap中是否有这个Bean对应的BeanDefinition
为什么要执行这个判断?
例如AAA里面有一个属性BBB,当我们对AAA进行属性填充的时候,BBB不一定是已经完成了实例化,所以这里是确保BBB这个Bean一定是可以出现在spring容器中的
3))接下来一个属性的方法就出现了-----getBean ,接着上面的AAA和BBB的例子,如果说单例池里面有bean,那么就会直接从单例池里面拿,如果说单例池里面没有那就会去创建这个bean,然后将其加入PropertyValues 这个数据结构中
以上就是byName的逻辑,接下来看看byType的逻辑
protected void autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
// 找到有对应set方法的属性
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
try {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
if (Object.class != pd.getPropertyType()) {
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
// 根据类型找bean,这就是byType
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
registerDependentBean(autowiredBeanName, beanName);
...
}
autowiredBeanNames.clear();
}
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
}
}
}
-
byType同样是先unsatisfiedNonSimpleProperties找到set方法所对应的名字
-
将获取到的set方法的名字遍历,然后获得其PropertyDescriptor 属性的描述符
-
然后根据方法的描述信息生成了另外一个Descriptor叫做DependencyDescriptor
这个数据结构翻译为中文就是依赖描述,也就是依赖的描述,这里指的依赖其实可以认为是set方法的参数类型
也就是对set方法参数的类型进行一些描述
首先清除一个概念,在java中所谓的依赖,例如类AAA中有BBB属性,则说明AAA依赖BBB,
但是对于spring而言判定依赖关系,不是基于是否有这个属性,而是基于set方法
对于byType的自动注入而言 ,spring关注的是set方法的参数类型
对于byName的自动注入而言,spring关注的是set方法后面的字符串的(开头字母小写)并且这个依赖描述DependencyDescriptor 接收的是其子类 AutowireByTypeDependencyDescriptor
根据名字也能判断出来,这个依赖描述,是基于byType来描述bean之间的依赖关系的 -
根据新生成的描述信息找要注入的Bean
这里首先会先根据前面传过来的依赖描述DependencyDescriptor 先获取类型,然后在spring容器里面去找Bean
这根据类型找bean可能会找到多个bean,然后就会在这多个 bean 里面进行筛选,先会筛选出标记了@Primary的类
如果没有再会去通过byName找,但是我们知道,这里的依赖注入是处理xml配置的,如果xml只配置了byType,这里是不允许byName进
行查找的。
所以对于AutowireByTypeDependencyDescriptor这个子类而言,重写了其父类的getDependencyName方法,使其永远都返回null,从而
阻止了byName -
将找到的Bean对象加入到 PropertyValues
模块三
提到这部分内容,就不得不先提到MergedBeanDefinitionPostProcessor这个后置处理器
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
...
// 原始对象
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {...}
mbd.postProcessed = true;
}
}
......
}
再doCreateBean这个方法中可以看到,MergedBeanDefinitionPostProcessor这个后置处理器的执行是再bean的实例化之后的
在applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName) 里会调用postProcessMergedBeanDefinition
在这里会有一个后置处理器与@Autowired注解相关,就是AutowiredAnnotationBeanPostProcessor,这个类继承自
MergedBeanDefinitionPostProcessor,所以这里会调用AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)
{
// 获取beanType中的注入点
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
先找到当前创建的Bean的注入点,注入点就是需要注入的点,例如加了@Autowired的字段就是一个注入点,InjectionMetadata 是一个集合,因为一个类可能会依赖多个其他的类
点入InjectionMetadata 这个对象里
public abstract static class InjectedElement {
protected final Member member;
protected final boolean isField;
@Nullable
protected final PropertyDescriptor pd;
@Nullable
protected volatile Boolean skip;
...
可以发现每一个注入点的类型是InjectedElement,里面还有之前介绍过的PropertyDescriptor (属性描述)
Member是一个接口,是java反射包下的类,其中有两个关键的子类:一个是Method,另一个是Field,这两个类用来存储有关于方法和字段的信息
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
先去缓存里面找,如果缓存里面没有就会调用buildAutowiringMetadata
在这个方法里面有一个非常关键的do-while
在循环里则回去找到相关注解:
1)获取到当前类里面的所有的字段
2)判断当前这个字段上有没有标有@Autowired ,@Value,@Inject这三个注解的任意一个,如果有,则表示可以注入
这三个注解是在构造AutowiredAnnotationBeanPostProcessor这个对象时添加的
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject",
AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
3)判断当前的属性是不是静态的,spring不支持静态属性的注入
4)判断当前的注入点 (required == ture) ,是不是必须的,如果是必须的在后续的注入是一定要被赋值的,不然会抛出异常,如果不是必须的,则不会报错
5)new 一个AutowiredFieldElement添加到集合中,(AutowiredFieldElement 是 InjectedElement的子类)
6)对于当前类的注入点寻找完后,如果有父类还会去找父类的注入点
以上所说的都是对属性注入的查找
在这个do-while中还会对方法的注入进行查找,过程类似于对属性的查找,
只是在最后添加集合的时候不是使用AutowiredFieldElement,而是使用AutowiredMethodElement
(静态方法也不支持注入)
现在回到模块三的主体内容
这一块主要的做的事情就是调用AutowiredAnnotationBeanPostProcessor的postProcessProperties
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// InjectionMetadata中保存了所有被@Autowired注解标注的属性/方法并封装成一个个的InjectedElement
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 按注入点进行注入
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
可以看到在这个方法里面也调用了findAutowiringMetadata去寻找注入点,但是前面我们已经找过了注入点了,所以这里的findAutowiringMetadata可以直接从缓存里面拿到之前找到的注入点,然后调用 inject 进行注入
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
// 遍历每个能够注入的属性,进行注入
for (InjectedElement element : elementsToIterate) {
if (logger.isTraceEnabled()) {
logger.trace("Processing injected element of bean '" + beanName + "': " + element);
}
// element可能是Method,也可能是Field
element.inject(target, beanName, pvs);
}
}
}
然后就遍历这个bean中所有的注入点进行注入,在 进行注入的时候可能是属性的注入,也可能是方法的注入
属性的注入
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
// 对得到的对象进行缓存
this.cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
// 反射设值
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
}
1)根据当前的字段生成一个依赖描述
2)对这个依赖描述的对象进行解析得到一个需要注入的对象
3)最后通过反射将这个对象进行注入到字段里面
方法注入
...
for (int i = 0; i < arguments.length; i++) {
// 方法参数对象
MethodParameter methodParam = new MethodParameter(method, i);
DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
currDesc.setContainingClass(bean.getClass());
descriptors[i] = currDesc;
try {
// 寻找bean
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
if (arg == null && !this.required) {
arguments = null;
break;
}
arguments[i] = arg;
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
}
}
...
相比属性注入最大的不同就是,对于方法的注入免会遍历方法的每一个参数,然后进行注入
最后
以上就是等待水池为你收集整理的spring中Bean的生命周期之属性填充(1)的全部内容,希望文章能够帮你解决spring中Bean的生命周期之属性填充(1)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复