@Autowired寻找注入点
- 本文源码基于spring-framework-5.3.10。
- 在属性注入的时候,spring需要找到那些属性需要注入!
- 源码位于:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(RootBeanDefinition, Class<?>, String)
- 他在Bean的生命周期的合并BeanDefinition中调用。
@Autowired注解可以写的位置
- 属性上:先根据属性类型去找Bean,如果找到多个再根据属性名确定一个
- 构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
- set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
@Autowired寻找注入点原理
- 遍历当前类的所有的属性字段Field
- 查看字段上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该字段是一个注入点
如果字段是static的,则不进行注入
- 获取@Autowired中的required属性的值
- 将字段信息构造成一个AutowiredFieldElement对象,作为一个注入点对象添加到currElements集合中。
遍历当前类的所有方法Method
- 判断当前Method是否是桥接方法,如果是找到原方法
- 查看方法上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该方法是一个注入点
- 如果方法是static的,则不进行注入
- 获取@Autowired中的required属性的值
- 将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到currElements集合中。
- 遍历完当前类的字段和方法后,将遍历父类的,直到没有父类。
- 最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对于的注入点集合对象,并缓存。
@Autowired寻找注入点:postProcessMergedBeanDefinition源码分析
/**
* 在合并BeanDefinition的时候调用
*/
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 寻找注入点
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
// 检查配置
metadata.checkConfigMembers(beanDefinition);
}
/**
* 寻找注入点的具体逻辑
*/
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
// 得到当前Bean的名字
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
// metadata:可以简单理解为一个注入点的集合
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
// 读取到了类的元数据之后,进行对字段、普通方法、构造方法的处理
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
// metadata:可以简单理解为一个注入点的集合
metadata = this.injectionMetadataCache.get(cacheKey);
// 双重检测在不在缓存,没有变化
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
// 清理一些要跳过的自己设置的值?已经注入的值
if (metadata != null) {
metadata.clear(pvs);
}
// 解析注入点
metadata = buildAutowiringMetadata(clazz);
// 将当前Bean的注入信息缓存
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
/**
* 寻找解析注入点
*/
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
// 如果一个Bean的是java.开头的类,那么则根本不需要进行依赖注入
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
// 定义一个存放注入信息的集合:包含每一个注入点
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
// 得到当前Bean的class对象
Class<?> targetClass = clazz;
// 这里的循环主要作用是子类找完找父类,直到没有父类!
do {
// 定义一个存放注入信息的集合:包含当前一个字段活方法的信息
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
// 遍历targetClass中的所有Field,执行第二个参数的内容
ReflectionUtils.doWithLocalFields(targetClass, field -> {
// 得到field上是否存在@Autowired、@Value、@Inject中的其中一个
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
// static filed不是注入点,不会进行自动注入。static属于类,原型Bean在创建的时候会有属性值覆盖问题
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// 判断是否是必须的,简单理解就是@autowired(required = true)
boolean required = determineRequiredStatus(ann);
// 注入点加入到上面的集合中
currElements.add(new AutowiredFieldElement(field, required));
}
});
// 遍历targetClass中的所有Method
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// 桥接方法,找到真正的方法
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
// method上是否存在@Autowired、@Value、@Inject中的其中一个
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// static method不是注入点,不会进行自动注入。static属于类,原型Bean在创建的时候会有属性值覆盖问题
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
// set方法最好有入参,无参数会打印日志。这里也就是说,提供一个s方法,标记位@Autowired,他会在依赖注入的时候(初始化前)执行!
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
// 判断是否是必须的,简单理解就是@autowired(required = true)
boolean required = determineRequiredStatus(ann);
// 属性描述器中存在当前的属性(set方法或者get方法的名称)
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
// 注入点加入到上面的集合中
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
// 头插法的方式将当前类的所有注入点放入集合中
elements.addAll(0, currElements);
// 得到他的父类,继续循环,直到没有父类!
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
// 生成InjectionMetadata对象
return InjectionMetadata.forElements(elements, clazz);
}
一个字段能不能成为注入点
private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
// 得到当前字段上所有的注解
MergedAnnotations annotations = MergedAnnotations.from(ao);
// 包含符合注入点的条件直接返回(@Autowired、@Value、@Inject),否则返回null
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
MergedAnnotation<?> annotation = annotations.get(type);
if (annotation.isPresent()) {
return annotation;
}
}
return null;
}
有什么样的标记会成为注入点?
public AutowiredAnnotationBeanPostProcessor() {
// @Autowired会成为注入点
this.autowiredAnnotationTypes.add(Autowired.class);
// @Value会成为注入点
this.autowiredAnnotationTypes.add(Value.class);
try {
// @Inject也会成为注入点
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.
}
}
结束语
- 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
- 关注公众号,可以让你对MySQL、并发编程、spring源码有深入的了解!
- 关注公众号,后续持续高效的学习JVM!
- 这个公众号,无广告!!!每日更新!!!
