亲宝软件园·资讯

展开

Spring @Autowired

飘飘~ 人气:0

前言

说明:我们今天要分享的是依赖注入(DI)通过自动装配的注解方式@Autowird注解方法的作用和实现原理以及实现机制。

在分享之前,我们先来回顾一下说明是依赖注入,依赖注入中手动注入和自动装配的几种方式,以便于提高大家对本篇文章的理解。

一、依赖注入的方式

对于spring配置一个bean时,如果需要给该bean提供一些初始化参数,则需要通过依赖注入方式,所谓的依赖注入就是通过spring将bean所需要的一些参数传递到bean实例对象的过程(将依赖关系注入到对象中)

手动注入

①使用属性的setter方法注入 ,这是最常用的方式:

要求:属性注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的Setter方法。Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值。

②构造器注入

使用方式:

第一,在类中,不用为属性设置setter方法(但是可以有),但是需要生成该类带参的构造方法。

第二,在配置文件中配置该类的bean,并配置构造器,在配置构造器中用到了<constructor-arg>节点,该节点有四个属性:

自动装配

XML方式进行自动装配

大家可以看到用xml装配bean是一件很繁琐的事情,而且我们还要找到对应类型的bean才能装配。

创建应用对象之间协作关系的行为称为装配。也就是说当一个对象的属性是另一个对象时,实例化时,需要为这个对象属性进行实例化,这就是装配。

如果一个对象只通过接口来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行切换。但是这样会存在一个问题,在传统的依赖注入配置中,我们必须要明确要给属性装配哪一个bean的引用,一旦bean很多,就不好维护了。基于这样的场景,spring使用注解来进行自动装配,解决这个问题。自动装配就是开发人员不必知道具体要装配哪个bean的引用,这个识别的工作会由spring来完成。

在sping框架中共有5种自动装配 :

上面所有方式的applicationContext.xml配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 使用注解前的配置 -->
    <context:annotation-config></context:annotation-config>
    <!-- 扫描 -->
    <context:component-scan base-package="com.ape.pojo"></context:component-scan>
    <!-- 手动注入 -->
    <!-- set注入 -->
    <bean id="Student" class="com.ape.pojo.Student">
        <property name="sid" value="1"></property>
        <property name="sname" value="张三"></property>
        <property name="smoney" value="100.00"></property>
        <property name="cat" ref="cat"></property>
    </bean>
    <!-- 构造器注入construct -->
    <bean id="Student" class="com.ape.pojo.Student">
        <constructor-arg name="sid" value="1"></constructor-arg>
        <constructor-arg name="sname" value="张三"></constructor-arg>
        <constructor-arg name="smoney" value="100.00"></constructor-arg>
        <constructor-arg name="cat" ref="cat"></constructor-arg>
    </bean>
    <bean id="Student" class="com.ape.pojo.Student">
        <constructor-arg type="int" value="1"></constructor-arg>
        <constructor-arg type="java.lang.String" value="张三"></constructor-arg>
        <constructor-arg type="double" value="100.00"></constructor-arg>
        <constructor-arg type="com.ape.pojo.Cat" ref="cat"></constructor-arg>
    </bean>
    <bean id="Student" class="com.ape.pojo.Student">
        <constructor-arg index="0" value="1"></constructor-arg>
        <constructor-arg index="1" value="张三"></constructor-arg>
        <constructor-arg index="2" value="100.00"></constructor-arg>
        <constructor-arg index="3" ref="cat"></constructor-arg>
    </bean>
    <!-- 自动装配 -->
    <!-- xml -->
    <bean id="Student" class="com.ape.pojo.Student" autowire="no">
        <property name="sid" value="1"></property>
        <property name="sname" value="张三"></property>
        <property name="smoney" value="100.00"></property>
        <property name="cat" ref="cat"></property>
    </bean>
    <bean id="Student" class="com.ape.pojo.Student" autowire="byName">
    </bean>
    <bean id="Student" class="com.ape.pojo.Student" autowire="byType">
    </bean>
    <bean id="Student" class="com.ape.pojo.Student" autowire="constructor">
    </bean>
    <bean id="Student" class="com.ape.pojo.Student" autowire="default">
    </bean>
</beans>

二、注解@Autowired的自动装配原理

@Autowired自动装配过程

使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置<context:annotation-config/>。

在启动springIoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IOC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;

如果查询的结果不止一个,那么@Autowired会根据名称来查找;

如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

实现原理

①首先看看spring的源代码定义

阅读代码我们可以看到,Autowired注解可以应用在构造方法,普通方法,参数,字段,以及注解这五种类型的地方,它的保留策略是在运行时。在Spring源代码当中,Autowired注解位于包org.springframework.beans.factory.annotation之中,如上图。

②核心逻辑在buildAutowiringMetadata中

通过反射获取该类的每一个字段和方法,并且分别用findAutowiredAnnotation方法遍历每一个字段和方法,如果有@Autowired注解修饰,则返回注解相关属性。最后这个方法返回的就是包含所有带有autowire注解修饰的一个InjectionMetadata集合。

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();
                ReflectionUtils.doWithLocalFields(targetClass, (field) -> {
                    MergedAnnotation<?> ann = this.findAutowiredAnnotation(field);
                    if (ann != null) {
                        if (Modifier.isStatic(field.getModifiers())) {
                            if (this.logger.isInfoEnabled()) {
                                this.logger.info("Autowired annotation is not supported on static fields: " + field);
                            }
                            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);
                        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));
                        }
                    }
                });
                elements.addAll(0, currElements);
                targetClass = targetClass.getSuperclass();
            } while(targetClass != null && targetClass != Object.class);
            return InjectionMetadata.forElements(elements, clazz);
        }
    }

③InjectionMetadata类

这个类由两部分组成: targetClass目标类和我们要获得的injectedElements集合。

 public InjectionMetadata(Class<?> targetClass, Collection<InjectionMetadata.InjectedElement> elements) {
        this.targetClass = targetClass;
        this.injectedElements = elements;
    }

④ 实现注入逻辑

调用InjectionMetadata中的公共inject方法遍历调用protect的inject方法

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);
        try {
            metadata.inject(bean, beanName, pvs);
            return pvs;
        } catch (BeanCreationException var6) {
            throw var6;
        } catch (Throwable var7) {
            throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var7);
        }
    }

⑤调用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);
            }
        }
    }

⑥遍历调用protect的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();
                }
            }
        }

实质就是inject也使用了反射技术并且依然是分成字段和方法去处理的。

加载全部内容

相关教程
猜你喜欢
用户评论