一、引子
疑问:String如何处理带有@Autowired的方法或者带有@Autowired的属性
在Spring实例化Bean的过程中有个填充属性的操作,这里就包含依赖注入,也就是@Autowired注解。以下是Spring在实例化过程中的一段代码,该代码主要是处理Bean里面有属性或者方法带有@Autowired注解的处理逻辑。
//BeanWrapper instanceWrapper = xxx
//RootBeanDefinition mbd = xx
populateBean(beanName, mbd, instanceWrapper);
//BeanWrapper是做什么的?因为此时Bean对象已经生成,但是没有完全实例化完,因为Bean里面的属性还没有赋值,那么就需要去修改这个对象里面的属性,BeanWrapper就是做这个事情的。关于BeanWrapper介绍可以看我的另一偏文章
上面代码主要对只是实例化部分的Bean进一步处理,对他里面的属性进行赋值的过程,主要是对Bean里面的属性或者方法标有@Autowired进行处理
看下populateBean方法,如何对Bean里面的属性和方法进行处理,主要是包含两块
第一块是关于通过ByType或者ByName的处理方式
比如我们在开发中使用xml配置
<bean id="user" class="com.kuang.pojo.User" autowire="byName">
</bean>
<bean id="user" class="com.kuang.pojo.User" autowire="byType">
。。。。。User里面很多属性,那么这些属性赋值就和autowire配的值有关系了。
</bean>
DependencyDescriptor来给属性赋值或者方法赋值,关于DependencyDescriptor可以看我的另一偏文章:
第二块是通过AutowiredAnnotationBeanPostProcessor
关于AutowiredAnnotationBeanPostProcessor我们重点说下处理流程
1、拿到Bean里面带有@Autowired注解的属性和带有@Autowired注解的方法。
2、通过反射去设置属性的值,或者通过反射调用目标方法
这种方式就好比Spring在实例化Mapper类的时候,里面就有带@Autowired注解的属性和带有@Autowired注解的方法,比如setSqlSessionFactory方法,处理逻辑都是一样的。下面说下针对使用了@Autowired修饰方法或者属性,Spring是怎么赋值的。
//这是对populateBean精简后的伪代码,里面包含2个重要类PropertyValues、BeanWrapper
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
//遍历所有的BeanPostProcessors
Iterator var9 = this.getBeanPostProcessors().iterator();
while (var9.hasNext()) {
BeanPostProcessor bp = (BeanPostProcessor) var9.next();
if (bp instanceof InstantiationAwareBeanPostProcessor) {
//找到AutowiredAnnotationBeanPostProcessor
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
//从Bean里面找到带有@Autowired的属性和方法,然后给其赋值,最后得到PropertyValues,PropertyValues就包含了属性所需要赋值的value。关于PropertyValues我下文会介绍
PropertyValues pvs = ibp.postProcessProperties((PropertyValues) pvs, bw.getWrappedInstance(), beanName);
//这里主要是把上面得到的PropertyValues设置到BeanWrapper里面去。
this.applyPropertyValues(beanName, mbd, bw, (PropertyValues) pvs);
}
}
}
//主要是把得到的PropertyValues设置到BeanWrapper里面去。
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
bw.setPropertyValues(pvs);
}
上面2个重要的类这里说下,PropertyValues、BeanWrapper
PropertyValues是什么?
PropertyValues的实现类是MutablePropertyValues,可以理解他是一个集合,里面包含如下几个属性
id:Bean 唯一标识名称。
beanClass:类全限定名(包名+类名)。
MultablePropertyValues:用于封装类属性的集合,里面是一个List容器,包装了很多 PropertyValue。一个PropertyValue封装了一个属性及其对应的值,可以说一个属性及其值就是一个 PropertyValue,当我们需要再 BeanDefinition 中修改某个类里面的属性时就可以使用该类。
…省略属性
那么这个类有什么作用呢?当一个Bean里面有很多个属性需要设置值的时候,这些属性的name和value就会被封装成PropertyValue对象。多个属性组和成一个PropertyValue集合。
BeanWrapper 是什么?
Spring在对Bean封装打包得到BeanWrapper对象之后,我们就可以通过BeanWrapper访问Bean的属性和方法,在Spring需要反射获取(设置)Bean的字段或者方法时(比如@Autowired注入字段时),就可以直接通过BeanWrapper来操作。
BeanWrapper实现了很多接口,每个接口都有特殊的功能
1、属性编辑器PropertyEditor,有了它,你可以用String给一个Bean非字符串类型属性设置值(当然只支持几种内置的类型)
2、类型转换服务ConversionService
3、类型转换逻辑TypeConverter
接下来看上面postProcessProperties方法做了什么操作
//通过类名生成InjectionMetadata,关于InjectionMetadata我下面会介绍这个做什么用的,很重要。
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
//通过类型生成InjectionMetadata
InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);
//给InjectionMetadata里面的属性设置值,InjectionMetadata主要是Method、Field这2种东西,然后通过反射给其设置值。
metadata.inject(bean, beanName, pvs);
return pvs;
}
//通过缓存获取InjectionMetadata,如果没有就通过类名转成InjectionMetadata,
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
//先从缓存里面拿
InjectionMetadata metadata = (InjectionMetadata)this.injectionMetadataCache.get(cacheKey);
if(metadata==null){
InjectionMetadata metadata = this.buildAutowiringMetadata(clazz);//没拿到就通过类来生成新的InjectionMetadata,在存到缓存里面去。
this.injectionMetadataCache.put(cacheKey, metadata);
}
return metadata;
}
InjectionMetadata介绍
public class InjectionMetadata {
private final Class<?> targetClass;
//当一个方法有多个属性有@Autowired时,那么这里面就是一个集合,@Autowired修饰的方法也是一样
private final Collection<InjectionMetadata.InjectedElement> injectedElements;
}
//伪代码
public abstract static class InjectedElement {
protected final Method或者Field类型 member;
protected final boolean isField;
}
通过上面的结构我们不难看出带有@Autowired修饰的字段或者@Autowired修饰的方法,
都是通过member来完成赋值和调用。
//一层层解析Bean里面带有@Autowired属性和方法,包括其父类。最终得到InjectionMetadata对象。
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
List<InjectedElement> elements = new ArrayList();
Class targetClass = clazz;
do {
List<InjectedElement> currElements = new ArrayList();
ReflectionUtils.doWithLocalFields(targetClass, (field) -> {
MergedAnnotation<?> ann = this.findAutowiredAnnotation(field);//判断字段是否带了@Autowired注解
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
return;
}
boolean required = this.determineRequiredStatus(ann);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
}
});
ReflectionUtils.doWithLocalMethods(targetClass, (method) -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
MergedAnnotation<?> ann = this.findAutowiredAnnotation(bridgedMethod);//判断方法是否带了@Autowired注解
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
return;
}
if (method.getParameterCount() == 0 && this.logger.isInfoEnabled()) {//参数个数是0个
this.logger.info("Autowired annotation should only be used on methods with parameters: " + method);
}
boolean required = this.determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement(method, required, pd));
}
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();//一层层往父类里面找。
} while(targetClass != null && targetClass != Object.class);
//最终拿到所有的带有@Autowired注解的属性和方法生成InjectionMetadata对象。
return InjectionMetadata.forElements(elements, clazz);
}
最后通过metadata.inject(bean, beanName, pvs)来完成赋值,下面看下metadata.inject做了什么
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
if (!((Collection)elementsToIterate).isEmpty()) {
//遍历所有的InjectedElement出发inject方法
Iterator var6 = ((Collection)injectedElements).iterator();
while(var6.hasNext()) {
InjectionMetadata.InjectedElement element = (InjectionMetadata.InjectedElement)var6.next();
element.inject(target, beanName, pvs);//完成赋值或者调用。
}
}
}
//根据element的类型,是Field还是Method
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field)this.member;
Object value = AutowiredAnnotationBeanPostProcessor.this.resolvedCachedArgument(beanName, this.cachedFieldValue);
field.set(bean, value);//给字段赋值,到这带有@Autowired注解的字段值已经填充好了。
}
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Method method = (Method)this.member;
Object[] arguments = this.resolveCachedArguments(beanName);//拿到参数
method.invoke(bean, arguments);//触发目标方法,到这带有@Autowired的方法已经得到执行了。
}
二、@Resource
其实@Resource和@Autowired处理有点类似,下面从源码上介绍下处理的区别,关于@Autowired的处理使用的后置处理器是AutowiredAnnotationBeanPostProcessor,那么@Resource的后置处理器是CommonAnnotationBeanPostProcessor,下面看下处理的区别。
CommonAnnotationBeanPostProcessor
1、区别一、CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor
同样也是在populateBean方法里面处理,下面精简了一下代码
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
Iterator var9 = this.getBeanPostProcessors().iterator();
while(var9.hasNext()) {
BeanPostProcessor bp = (BeanPostProcessor)var9.next();
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)bp;
//区别在于当前的后置处理器ibp=CommonAnnotationBeanPostProcessor,只不过处理@Autowired时,ibp=AutowiredAnnotationBeanPostProcessor
PropertyValues pvsToUse = ibp.postProcessProperties((PropertyValues)pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = this.filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues((PropertyValues)pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs);
}
2、区别二:InjectedElement的实现类不同
首先是通过目标类Bean找到里面带有@Resource注解的属性或者方法,来生成元数据也就是InjectionMetadata。
此时的元数据的实现类型是ResourceElement,而我们在分析@Autowired时实现的类型是AutowiredFieldElement、AutowiredMethodElement
private InjectionMetadata buildResourceMetadata(Class<?> clazz) {
List<InjectedElement> elements = new ArrayList();
Class targetClass = clazz;
//通过循环,一层层遍历,通过父类往上找
do {
//查找带有@Resource的字段
List<InjectedElement> currElements = new ArrayList();
ReflectionUtils.doWithLocalFields(targetClass, (field) -> {
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
}
currElements.add(new CommonAnnotationBeanPostProcessor.WebServiceRefElement(field, field, (PropertyDescriptor)null));
} else if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static fields");
}
currElements.add(new CommonAnnotationBeanPostProcessor.EjbRefElement(field, field, (PropertyDescriptor)null));
} else if (field.isAnnotationPresent(Resource.class)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
//注意currElements添加进去的数据类型是ResourceElement
if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
currElements.add(new CommonAnnotationBeanPostProcessor.ResourceElement(field, field, (PropertyDescriptor)null));
}
}
});
//查找带有@Resource的方法
ReflectionUtils.doWithLocalMethods(targetClass, (method) -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
PropertyDescriptor pdx;
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
}
pdx = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new CommonAnnotationBeanPostProcessor.WebServiceRefElement(method, bridgedMethod, pdx));
} else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
}
pdx = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new CommonAnnotationBeanPostProcessor.EjbRefElement(method, bridgedMethod, pdx));
} else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static methods");
}
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 1) {
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
}
//实现类型用的是ResourceElement
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new CommonAnnotationBeanPostProcessor.ResourceElement(method, bridgedMethod, pd));
}
}
}
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
} while(targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
基于上面的InjectedElement类型不同,必然导致inject方法的处理逻辑不一样
![](https://i-blog.csdnimg.cn/blog_migrate/3b7a0456c90d1b1017be95fee9b8df63.png)
//下面看下ResourceElement的处理逻辑
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs) throws Throwable {
if (this.isField) {//判断是否是字段
Field field = (Field)this.member;
ReflectionUtils.makeAccessible(field);
field.set(target, this.getResourceToInject(target, requestingBeanName));
} else {
//走到说明@Resource修饰的方法
try {
Method method = (Method)this.member;
ReflectionUtils.makeAccessible(method);
method.invoke(target, this.getResourceToInject(target, requestingBeanName));
} catch (InvocationTargetException var5) {
throw var5.getTargetException();
}
}
}
3、区别点三:获取Bean的方法不同,resolveDependency和resolveBeanByName
Autowired是调用resolveDependency方法,来获取需要注入的属性,底层是通过Type来拿到BeanName,在通过Name来获取实例,如果Name存在多个,在通过BeanName进行匹配。
Resource是调用resolveBeanByName方法,在获取Bean的时候,优先通过factory里面是否包含name判断,如果没有则通过Type尝试去获取,通过Type去获取也就变成了调用resolveDependency方法一个意思,这里需要注意在@Autowired获取Bean的时候,也是用resolveDependency方法
protected Object autowireResource(BeanFactory factory, CommonAnnotationBeanPostProcessor.LookupElement element, @Nullable String requestingBeanName){
String name = element.name;
Object resource;
Object autowiredBeanNames;
if (factory instanceof AutowireCapableBeanFactory) {
AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory)factory;
DependencyDescriptor descriptor = element.getDependencyDescriptor();
//最终进到这里,先尝试判断factory中是否包含该name,如果不包含则通过Type场景去获取。
if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
autowiredBeanNames = new LinkedHashSet();
//流程1
resource = beanFactory.resolveDependency(descriptor, requestingBeanName, (Set)autowiredBeanNames, (TypeConverter)null);
if (resource == null) {
throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
}
} else {
//流程2、进到这里说明Bean中包含name,则通过name来获取
resource = beanFactory.resolveBeanByName(name, descriptor);
autowiredBeanNames = Collections.singleton(name);
}
}
return resource;
}
注:DependencyDescriptor对象存的就是被@Autowired或者@Resource修饰的属性或者方法信息如下
@Nullable
protected Field field; 字段Field,这也包含了字段名称字段类型,通过这个我们也可以知道需要注入什么类型进去。
@Nullable
protected MethodParameter methodParameter; 方法参数
@Nullable
private volatile Annotation[] fieldAnnotations; 注解
@Nullable
private String fieldName; 被修饰的字段名称
@Nullable
private String methodName; 被修饰的方法名称