@Autowired注解 -【Spring底层原理】

blog54

1、概述

【1】注解用法java

根据@Autowired注解的源码,能够看到该注解能够做用在构造器、参数、方法、属性,都是从容器中获取参数组件的值数组

  • 标注在方法上:@Bean+方法参数,参数从容器中获取,默认不写@Autowired效果是同样的,都能自动装配
  • 标注在构造器上:若是组件上只有一个有参构造,这个有参构造的@Autowired能够省略,参数位置的组件仍是能够自动从容器中获取
  • 标注在参数位置
  • 标注在属性位置
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}
复制代码

相关注解:markdown

  • @Autowired:默认按类型装配,若是咱们想使用按名称装配,能够结合@Qualifier注解一块儿使用(Spring提供)
  • @Qualifier():指定装配的bean,多个实例时能够结合@Autowired一块儿使用
  • @Primary:自动装配时当出现多个bean候选者时,被注解为@Primary的bean将做为首选者
  • @Resource:默认按名称装配,当找不到与名称匹配的bean才会按类型装配(不支持@Primary@Autowired(required = false)功能,JDK提供)
  • @Inject:须要导入javax.inject的包,和Autowired功能同样,可是没有required=false功能(JDK提供)

【2】自动装配app

Spring利用依赖注入(DI)完成对IOC容器中各个组件的依赖关系赋值ide

@Autowired自动注入(Spring提供的):oop

  • 默认优先按照去容器中找对应的组件:applicationContext.getBean()
  • 若是找到多个相同类型的组件,再将属性的名称做为组件的ID去容器中查找
  • @Qualifier()注解:该注解指定须要装配的组件ID,而不是使用属性名
  • 自动装配默认必需要对属性赋值,没有就会报错,可使用@Autowired(required = false)指定非必须就不会报错
  • @Primary注解:自动装配时当出现多个bean候选者时,被注解为@Primary的bean将做为首选者,不然将抛出异常,若是使用了@Qualifier()指定装配的bean,则仍是使用明确指定装配的bean

@Resource(JSR250)和@Inject(JSR330)(JDK提供的)源码分析

@Resource:post

  • 默认按照组件名称进行装配,也能够指定名称进行装配
  • 当找不到与名称匹配的bean会按类型装配
  • 不支持@Primary@Autowired(required = false)功能
  • 若是同时指定了name和type,则从Spring上下文中找到惟一匹配的bean进行装配,找不到则抛出异常。
  • 若是指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
  • 若是指定了type,则从上下文中找到相似匹配的惟一bean进行装配,找不到或是找到多个,都会抛出异常。
  • 若是既没有指定name,又没有指定type,则自动按照byName方式进行装配;若是没有匹配,则回退为一个原始类型进行匹配,若是匹配则自动装配。

@Inject:ui

  • 须要导入javax.inject的包,和Autowired功能同样,可是没有required=false功能

【3】@Autowired和@Resource注解的区别this

  • @Autowired由Spring提供,只按照byType注入;@Resource由J2EE提供,默认按照byName自动注入,当找不到与名称匹配的bean会按类型装配
  • @Autowired默认按类型装配,默认状况下必需要求依赖对象存在,若是要容许null值,能够设置它的required属性为false。若是想使用名称装配能够结合@Qualifier注解进行使用。
  • @Resource,默认按照名称进行装配,名称能够经过name属性进行指定,若是没有指定name属性,当注解写在字段上时,默认取字段名进行名称查找。若是注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。可是须要注意的是,若是name属性一旦指定,就只会按照名称进行装配。

2、实例分析

这里只对@Autowired注解标注在属性位置进行实例分析

【1】@Autowired注解

// 启动类
@Test
public void TestMain() {
    // 建立IOC容器
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    UserService userService = applicationContext.getBean(UserService.class);
    System.out.println("userService:" + userService);
}

// Service
@Service
public class UserService {
    @Autowired(required = false)	// 指定非必须
    @Qualifier("userDao2")		// 指定装配bean
    private UserDao userDao;
    @Override
    public String toString() {
        return "UserService{" +
                "userDao=" + userDao +
                '}';
    }
}

// Dao
@Repository
public class UserDao {
    private String label = "1";
    public void setLabel(String label) {
        this.label = label;
    }
    @Override
    public String toString() {
        return "UserDao{" +
                "label='" + label + '\'' +
                '}';
    }
}

// 配置类
@Configuration
@ComponentScan({"dao","service","controller"})
public class AppConfig {
    @Primary		// 首选装配bean
    @Bean("userDao2")
    public UserDao userDao(){
        UserDao userDao = new UserDao();
        userDao.setLabel("2");
        return userDao;
    }
}
复制代码

输出结果以下,因为上面使用@Qualifier("userDao2")指定了要装配的bean,因此这里输出的是label=’2‘:

image-20210313102341875

  • 若是将@Qualifier("userDao2")改成@Qualifier("userDao"),则装配的是label=’1‘

【2】@Resource注解

@Service
public class UserService {
    @Resource(name = "userDao2",type = UserDao.class)
    private UserDao userDao;
    @Override
    public String toString() {
        return "UserService{" +
                "userDao=" + userDao +
                '}';
    }
}
复制代码
  • 默认按照组件名称进行装配,也能够指定名称进行装配
  • 当找不到与名称匹配的bean会按类型装配
  • 不支持@Primary@Autowired(required = false)功能
  • 若是同时指定了name和type,则从Spring上下文中找到惟一匹配的bean进行装配,找不到则抛出异常。
  • 若是指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
  • 若是指定了type,则从上下文中找到相似匹配的惟一bean进行装配,找不到或是找到多个,都会抛出异常。
  • 若是既没有指定name,又没有指定type,则自动按照byName方式进行装配;若是没有匹配,则回退为一个原始类型进行匹配,若是匹配则自动装配。

3、源码追踪

这里对@Autowired注解底层进行源码分析

参考:blog.csdn.net/topdevelope…

@Autowired是用来装配bean的,确定和bean的实例化有关,先通过了refresh方法,在finishBeanFactoryInitialization方法中getBean,而后走getObject的时候触发bean的初始化。bean的初始化是一个很复杂地方,在AbstractAutowireCapableBeanFactory#doCreateBean方法中,先建立一个BeanWrapper,它的内部成员变量wrappedObject中存放的就是实例化的MyService对象,Spring Bean的生命周期源码详解 - 【Spring底层原理】,再日后进入populateBean方法进行属性注入

Spring对autowire注解的实现逻辑位于类:AutowiredAnnotationBeanPostProcessor#postProcessProperties之中,——>findAutowiringMetadata——>buildAutowiringMetadata,核心代码就在buildAutowiringMetadata方法里面

private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
    if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
        return InjectionMetadata.EMPTY;
    } else {
        List<InjectedElement> elements = new ArrayList();
        // 须要处理的目标类
        Class targetClass = clazz;

        do {
            List<InjectedElement> currElements = new ArrayList();
            // 经过反射获取该类全部的字段,并遍历每个字段,并经过方法findAutowiredAnnotation遍历每个字段的所用注解,并若是用autowired修饰了,则返回auotowired相关属性
            ReflectionUtils.doWithLocalFields(targetClass, (field) -> {
                MergedAnnotation<?> ann = this.findAutowiredAnnotation(field);
                if (ann != null) {
                    // 校验autowired注解是否用在了static方法上
                    if (Modifier.isStatic(field.getModifiers())) {
                        if (this.logger.isInfoEnabled()) {
                            this.logger.info("Autowired annotation is not supported on static fields: " + field);
                        }

                        return;
                    }

                    // 判断是否指定了required
                    boolean required = this.determineRequiredStatus(ann);
                    currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
                }

            });
            // 和上面同样的逻辑,可是是经过反射处理类的method
            ReflectionUtils.doWithLocalMethods(targetClass, (method) -> {
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                if (BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                    MergedAnnotation<?> ann = this.findAutowiredAnnotation(bridgedMethod);
                    if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                        if (Modifier.isStatic(method.getModifiers())) {
                            if (this.logger.isInfoEnabled()) {
                                this.logger.info("Autowired annotation is not supported on static methods: " + method);
                            }

                            return;
                        }

                        if (method.getParameterCount() == 0 && this.logger.isInfoEnabled()) {
                            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));
                    }

                }
            });
            // 用@Autowired修饰的注解可能不止一个,所以都加在currElements这个容器里面,一块儿处理
            elements.addAll(0, currElements);
            targetClass = targetClass.getSuperclass();
        } while(targetClass != null && targetClass != Object.class);

        return InjectionMetadata.forElements(elements, clazz);
    }
}
复制代码
  • 获取须要处理的目标类
  • 经过doWithLocalFields方法传入目标类参数,经过反射获取该类全部的字段,并遍历每个字段,并经过方法findAutowiredAnnotation遍历每个字段的所用注解,并若是用autowired修饰了,则返回auotowired相关属性
  • 判断autowired注解是否用在了static方法上
  • 若有多个@Autowired修饰的注解,都加在currElements这个容器里面,一块儿处理

最后返回包含全部带有autowire注解修饰的一个InjectionMetadata集合,以下

  • targetClass:要处理的目标类
  • elements:上述方法获取到的因此elements集合
public InjectionMetadata(Class<?> targetClass, Collection<InjectionMetadata.InjectedElement> elements) {
    this.targetClass = targetClass;
    this.injectedElements = 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);
            // 注入的bean的名字,这个方法的功能就是根据这个bean的名字去拿到它
            method.invoke(target, this.getResourceToInject(target, requestingBeanName));
        } catch (InvocationTargetException var5) {
            throw var5.getTargetException();
        }
    }
}
复制代码
  • 使用了反射技术,分红字段和方法去处理的。
  • makeAccessible这样的能够称之为暴力破解的方法,经过反射技术对对象进行实例化和赋值
  • getResourceToInject方法的参数就是要注入的bean的名字,这个方法的功能就是根据这个bean的名字去拿到它

4、总结

@AutoWired自动注入过程图:

image-20210313173345709

相关文章
相关标签/搜索