当前位置:网站首页>@Autowired注解的底层原理
@Autowired注解的底层原理
2022-07-25 23:53:00 【崇令】
Spring框架的便利让我们很容易地使用@Autowired注解实现依赖注入,本篇将深入Spring源码解析@Autowired注解的工作原理。
一、@Autowired注解的作用
1. @Autowired 是一个注释,它可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。
2. @Autowired 默认是按照类去匹配,配合 @Qualifier 指定按照名称去装配 bean。
二、@Autowired注解的用法
标注在方法上:@Bean+方法参数,参数从容器中获取,默认不写@Autowired效果是一样的,都能自动装配
标注在构造器上:如果组件上只有一个有参构造,这个有参构造的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.beans.factory.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
三、@Autowired注解的自动装配
@Autowired自动注入:
1. 默认优先按照类型去容器中找对应的组件:applicationContext.getBean()
2. 如果找到多个相同类型的组件,再将属性的名称作为组件的ID去容器中查找
四、@Autowired注解的源码分析
@Autowired注解的作用是由AutowiredAnnotationBeanPostProcessor实现的,查看该类的源码会发现它实现了MergedBeanDefinitionPostProcessor接口,进而实现了接口中的postProcessMergedBeanDefinition方法,其中的核心处理代码如下:
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
// 需要处理的目标类
Class<?> targetClass = clazz;
do {
final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>();
// 通过反射获取该类所有的字段,并遍历每一个字段,并通过方法findAutowiredAnnotation遍历每一个字段的所用注解,并如果用autowired修饰了,则返回auotowired相关属性
ReflectionUtils.doWithLocalFields(targetClass, field -> {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
//有多个@Autowired修饰的注解,都加在currElements这个容器里面,一起处理
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}
@Autowired注解正是通过postProcessMergedBeanDefinition这个方法实现注入类型的预解析,将需要依赖注入的属性信息封装到InjectionMetadata类中,最后这个方法返回的就是包含所有带有autowire注解修饰的一个InjectionMetadata集合。这个类由两部分组成:
public InjectionMetadata(Class<?> targetClass, Collection<InjectionMetadata.InjectedElement> elements) {
this.targetClass = targetClass;
this.injectedElements = elements;
}
一是我们处理的目标类,二就是上述方法获取到的所以elements集合。
有了目标类,与所有需要注入的元素集合之后,我们就可以实现autowired的依赖注入逻辑了,实现的方法如下:
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
if (!this.validatedBeanNames.contains(beanName)) {
if (!this.shouldSkip(this.beanFactory, beanName)) {
List<String> invalidProperties = new ArrayList();
PropertyDescriptor[] var6 = pds;
int var7 = pds.length;
for(int var8 = 0; var8 < var7; ++var8) {
PropertyDescriptor pd = var6[var8];
if (this.isRequiredProperty(pd) && !pvs.contains(pd.getName())) {
invalidProperties.add(pd.getName());
}
}
if (!invalidProperties.isEmpty()) {
throw new BeanInitializationException(this.buildExceptionMessage(invalidProperties, beanName));
}
}
this.validatedBeanNames.add(beanName);
}
return pvs;
}
它调用的方法是InjectionMetadata中定义的inject方法,如下:
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectionMetadata.InjectedElement> checkedElements = this.checkedElements;
Collection<InjectionMetadata.InjectedElement> elementsToIterate = checkedElements != null ? checkedElements : this.injectedElements;
if (!((Collection)elementsToIterate).isEmpty()) {
Iterator var6 = ((Collection)elementsToIterate).iterator();
while(var6.hasNext()) {
InjectionMetadata.InjectedElement element = (InjectionMetadata.InjectedElement)var6.next();
element.inject(target, beanName, pvs);
}
}
}
其逻辑就是遍历,然后调用inject方法,inject方法其实现逻辑如下:
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 {
if (this.checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method)this.member;
ReflectionUtils.makeAccessible(method);
method.invoke(target, this.getResourceToInject(target, requestingBeanName));
} catch (InvocationTargetException var5) {
throw var5.getTargetException();
}
}
}
这里使用了反射技术,是分成字段和方法去处理的。调用了makeAccessible这样的可以称之为暴力破解的方法,通过反射技术对对象进行实例化和赋值。getResourceToInject方法的参数就是要注入的bean的名字,这个方法就是根据这个bean的名字去拿到它。
边栏推荐
- About the foundation of fetch
- Optimize the browsing experience of yandere/konachan site with user scripts
- Part 66: monocular 3D reconstruction point cloud
- Lua脚本编写Wireshark插件解析第三方私有协议
- The difference between SFTP and FTP
- [Database Foundation] summary of MySQL Foundation
- Docker 安装 Redis-5.0.12(远程访问)
- [nodejs] nodejs create a simple server
- String functions and memory operation functions
- 十大排序之快速排序
猜你喜欢

A brief introduction to OWASP
![[Muduo] EventLoop event cycle](/img/80/824c7061d58796d454be0c438e257c.png)
[Muduo] EventLoop event cycle

String functions and memory operation functions

Ten threats to open API ecosystem

Leetcode 0135. distribute candy

Read the field status of account in ABAP code (hidden, optional, required)

获取马蜂窝酒店数据

赋值时'1和'b1有什么区别

下一代终端安全管理的关键特征与应用趋势

Shardingsphere data slicing
随机推荐
A brief introduction to OWASP
ShardingSphere数据分片
Leetcode 0136. numbers that appear only once: XOR
Native JS perfectly realizes deep copy
什么是奇偶校验?如何用C语言实现?
死信队列 和消息TTL过期代码
Program environment and pretreatment
[Muduo] EventLoop event cycle
[debug bug] JS: getFullYear is not a function
About the foundation of fetch
[nodejs] nodejs create a simple server
Programmer interview Golden Classic 4.12 summation path
firewall 命令简单操作
Same origin strategy and cross domain
redis-扩展数据类型(跳跃表/BitMaps/HyperLogLog/GeoSpatial)
ES6 syntax (difference between let, const, VaR, deconstruction assignment, arrow function, residual parameters, extension method of array)
[learning notes] unreal 4 engine introduction (III)
从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?
【MUDUO】打包EventLoop和Thread
S4/HANA ME21N创建PO 输出控制消息按钮丢失解决方法(切换EDI 输出模式BRF+至NAST模式)