Spring依赖注入(上)

导读:本篇文章讲解 Spring依赖注入(上),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

一、依赖注入方式

在创建Bean过程中,当完成了Bean的实例化以后,会对Bean进行属性填充,属性填充的过程中完成依赖注入

依赖注入主要有两种方式

  • 手动注入
  • 自动注入

1.1 手动注入

手动注入通过xml配置的方式来完成,包含set方法注入和构造方法注入两种方式:

  • set方法注入

    通过set方法注入时,对应的属性必须要有set方法,同时set方法的方法名必须满足JavaBean的规范

    <!--set注入-->
    <bean id="userService" class="com.lizhi.service.UserService" >
       <property name="orderService" ref="orderService"/>
    </bean>
    
  • 构造方法注入

    xml中使用构造方法注入时,需要指定构造方法的参数下标

    <bean id="userService" class="com.lizhi.service.UserService" >
       <constructor-arg index="0" ref="orderService"/>
    </bean>
    

1.2 自动注入

自动注入主要通过@Autowired和autowire属性来实现,其中autowire属性的使用场景有如下两种方式

  • xml的方式

    xml方式中,autowire属性定义了五种注入方式:no、byName、byType、construct、default

    <bean id="userService" class="com.lizhi.service.UserService" autowire="byName"/>
    
  • @Bean方式

    @Bean注解中,autowire属性只定义了三种注入方式:NO、BY_NAME、BY_TYPE,默认为NO

    这种方式现在已经不推荐使用了

    @Bean(autowire = BY_NAME)
    public UserService userService(){
       return new UserService();
    }
    

二、依赖注入的基本过程

2.1 autowire属性注入

处理依赖注入的时候,最先解决的就是通过autowire属性指定注入方式的这种依赖注入,源码如下:

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

    int resolvedAutowireMode = mbd.getResolvedAutowireMode();
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
        // MutablePropertyValues是PropertyValues具体的实现类
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        // Add property values based on autowire by name if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
            autowireByName(beanName, mbd, bw, newPvs);
        }
        // Add property values based on autowire by type if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            autowireByType(beanName, mbd, bw, newPvs);
        }
        pvs = newPvs;
    }
  • 获取自定义属性值

    首先从RootBeanDefinition中去拿由开发人员自己添加的属性值,开发人员可以在MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition方法中添加属性值

    BeanDefinition中的属性值存放在MutablePropertyValues缓存中,MutablePropertyValues实例的propertyValueList存放了具体的属性相关信息,包括属性名、属性值等等

    public class LzBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
        @Override
        public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
            beanDefinition.getPropertyValues().add("user",new User());
        }
    }
    

    MutablePropertyValues类定义如下:

    public class MutablePropertyValues implements PropertyValues, Serializable {
    
    	private final List<PropertyValue> propertyValueList;
        ……
    }
    

    PropertyValue类定义如下

    public class PropertyValue extends BeanMetadataAttributeAccessor implements Serializable {
    
        private final String name;
    
        @Nullable
        private final Object value;
        ……
    }
    
  • 判断注入方式

    调用getResolvedAutowireMode()方法的获取BeanDefinition中指定的注入方式,如果是通过BY_NAME或BY_TYPE方式注入,则会调用对应的方法,获取对应的参数与值,但并不是直接对属性赋值,而是先将其缓存到PropertyValues的属性列表中

  • 按属性名称注入

    如果是按名称进行注入,则调用autowireByName()解析属性

    protected void autowireByName(
          String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
    
       // 当前Bean中能进行自动注入的属性名
       String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
       // 遍历每个属性名,并去获取Bean对象,并设置到pvs中
       for (String propertyName : propertyNames) {
          if (containsBean(propertyName)) {
             Object bean = getBean(propertyName);
             pvs.add(propertyName, bean);
             // 记录一下propertyName对应的Bean被beanName给依赖了
             registerDependentBean(propertyName, beanName);
          }
       }
    }
    

    首先会调用unsatisfiedNonSimpleProperties()判断哪些属性可以进行注入,从当前实例获取所有的属性描述实例PropertyDescriptor对象,该对象记录了属性的get和set方法等信息

    如果当前属性有set方法,并且已经定义的属性中没有该属性,并且属性类型不是简单类型,isSimpleProperty()判断是否是简单类型,简单类型如:Enum、Number、CharSequence等

    protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
       Set<String> result = new TreeSet<>();
       PropertyValues pvs = mbd.getPropertyValues();
       PropertyDescriptor[] pds = bw.getPropertyDescriptors();
    
       for (PropertyDescriptor pd : pds) {
          if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
                !BeanUtils.isSimpleProperty(pd.getPropertyType())) {
             result.add(pd.getName());
          }
       }
       return StringUtils.toStringArray(result);
    }
    

    得到需要注入的属性的属性名之后,如上面代码所示,遍历属性名,调用getBean()去获取属性名实例作为属性值,这个时候并没有对属性进行赋值,只是把属性名与值缓存到了MutablePropertyValues的实例pvs中

  • 按属性类型注入

    如果是按类型注入,则调用autowireByType(),判断哪些属性需要注入的逻辑同上面按属性名称注入,然后调用resolveDependency()方法根据类型获取Bean实例,然后进行缓存,该方法的具体实现,会在依赖注入的其他章节详细讲到

    protected void autowireByType(
        String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
    
        Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
    
        // 当前Bean中能进行自动注入的属性名
        String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        for (String propertyName : propertyNames) {
            PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
            if (Object.class != pd.getPropertyType()) {
                MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
                boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
                DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
                // 根据类型找到的结果
                Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, 
                if (autowiredArgument != null) {
                    pvs.add(propertyName, autowiredArgument);
                }
                ……
            }
        }
    }
    

2.2 @Autowired注入

执行完前面autowired属性注入的逻辑之后,接下来将处理@Autowired、@Value、@Inject、@Resourec这些注解,并直接给属性进行赋值

获取实现了InstantiationAwareBeanPostProcessor接口的所有Bean,该接口中提供了postProcessProperties()方法,用于给属性赋值,遍历所有该类Bean,调用postProcessProperties实现属性赋值

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    ……
    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

    PropertyDescriptor[] filteredPds = null;
    if (hasInstAwareBpps) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }
        for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
            // 这里会调用AutowiredAnnotationBeanPostProcessor的postProcessProperties()方法,会直接给对象中的属性赋值
            // AutowiredAnnotationBeanPostProcessor内部并不会处理pvs,直接返回了
            PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
                if (filteredPds == null) {
                    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                }
                pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
                    return;
                }
            }
            pvs = pvsToUse;
        }
    }
    ……
}

下面以AutowiredAnnotationBeanPostProcessor为例,详细介绍属性赋值

AutowiredAnnotationBeanPostProcessor不仅实现了InstantiationAwareBeanPostProcessor接口,还实现了MergedBeanDefinitionPostProcessor,并且在MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition方法的实现中,做了非常重要的工作

  • 寻找注入点

    postProcessMergedBeanDefinition()的调用是在实例化后,属性填充前调用的,该方法中主要实现了寻找注入点的逻辑,即判断哪些方法或属性需要注入

    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
       InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
       metadata.checkConfigMembers(beanDefinition);
    }
    

    调用findAutowiringMetadata()寻找注入点,首先从缓存中取出所有注入点,然后将pvs中的属性从注入点中移除,pvs中属性的赋值会有其他逻辑去做,然后调用buildAutowiringMetadata()方法去真正解析注入点

    private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
       // Fall back to class name as cache key, for backwards compatibility with custom callers.
       String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
       // Quick check on the concurrent map first, with minimal locking.
       InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
       if (InjectionMetadata.needsRefresh(metadata, clazz)) {
          synchronized (this.injectionMetadataCache) {
             metadata = this.injectionMetadataCache.get(cacheKey);
             if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                if (metadata != null) {
                   metadata.clear(pvs);
                }
                // 解析注入点并缓存
                metadata = buildAutowiringMetadata(clazz);
                this.injectionMetadataCache.put(cacheKey, metadata);
             }
          }
       }
       return metadata;
    }
    

    解析注入点,从当前类开始,遍历类中所有属性和方法,调用findAutowiredAnnotation()是否有注解,但是对于static修饰的属性和方法不是注入点,在遍历方法的时候,需要调用findBridgedMethod()判断当前方法是否是桥接方法,桥接方法不会生成注入点;然后将生成的注入点全部缓存起来,后面注入属性的时候会用到

    private InjectionMetadata buildAutowiringMetadata(final Class<?> 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不是注入点,不会进行自动注入
                    if (Modifier.isStatic(field.getModifiers())) {
                        return;
                    }
    
                    // 构造注入点
                    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不是注入点,不会进行自动注入
                    if (Modifier.isStatic(method.getModifiers())) {
                        return;
                    }
                    boolean required = determineRequiredStatus(ann);
                    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);
    
        return InjectionMetadata.forElements(elements, clazz);
    }
    

    findAutowiredAnnotation判断是否有@Autowired、@Value、@Inject中的其中一个,autowiredAnnotationTypes是一个注解缓存,在AutowiredAnnotationBeanPostProcessor实例化的时候,会给该缓存添加Autowired、Value和Inject这三个注解

    private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
       MergedAnnotations annotations = MergedAnnotations.from(ao);
       for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
          MergedAnnotation<?> annotation = annotations.get(type);
          if (annotation.isPresent()) {
             return annotation;
          }
       }
       return null;
    }
    
    private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
    // 构造方法
    public AutowiredAnnotationBeanPostProcessor() {
        this.autowiredAnnotationTypes.add(Autowired.class);
        this.autowiredAnnotationTypes.add(Value.class);
        this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
                                              ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
    }
    

    findBridgedMethod()判断是否为桥接方法,对于某个类,如果它实现了泛型接口的某个方法,则在它的字节码文件中,会存在两个方法,一个真正的方法,一个是桥接方法

    public interface BridgeInterface <T>{
    	void test(T t);
    }
    
    @Component
    public class BridgeInterfaceImpl implements BridgeInterface<UserService>{
    	@Override
    	@Autowired
    	public void test(UserService userService) {
    
    	}
    }
    

    查看它的反编译后的字节码文件,发现有两个test方法,其中一个是桥接方法,对于桥接方法,不会生成注入点

    public test(Lcom/lizhi/service/UserService;)V
    public synthetic bridge test(Ljava/lang/Object;)V
    
  • 属性注入

    属性注入是在AutowiredAnnotationBeanPostProcessor.postProcessProperties()中完成的

    同样的调用findAutowiringMetadata()去获取注入点,因为前面已经寻找过注入点且缓存了,那么这一次可以直接从缓存中取出所有注入点,然后进行注入

    	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    		// 找注入点(所有被@Autowired注解了的Field或Method)
    		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    		try {
    			metadata.inject(bean, beanName, pvs);
    		}
    		return pvs;
    	}
    

    注入方法的实现如下,遍历每个注入点,然后调用注入点的inject()进行注入

    如果是属性注入点,则调用AutowiredFieldElement.inject()方法类进行注入,主要通过resolveFieldValue()从BeanFactory中找到匹配的Bean实例,resolveFieldValue()中的核心方法是beanFactory.resolveDependency()

    protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    
        Field field = (Field) this.member;
        Object value;
        if (this.cached) {
            // 对于原型Bean,第一次创建的时候,也找注入点,然后进行注入,此时cached为false,注入完了之后cached为true
            // 第二次创建的时候,先找注入点(此时会拿到缓存好的注入点),也就是AutowiredFieldElement对象,此时cache为true,也就进到此处了
            // 注入点内并没有缓存被注入的具体Bean对象,而是beanName,这样就能保证注入到不同的原型Bean对象
            try {
                value = resolvedCachedArgument(beanName, this.cachedFieldValue);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Unexpected removal of target bean for cached argument -> re-resolve
                value = resolveFieldValue(field, bean, beanName);
            }
        }
        else {
            // 根据filed从BeanFactory中查到的匹配的Bean对象
            value = resolveFieldValue(field, bean, beanName);
        }
    
        // 反射给filed赋值
        ……
    }
    

    如果是方法注入点,则调用AutowiredMethodElement.inject()进行注入,方法注入的逻辑要比属性注入复杂很多,resolveMethodArguments()中的核心方法也是beanFactory.resolveDependency()

    protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
       // 如果pvs中已经有当前注入点的值了,则跳过注入
       if (checkPropertySkipping(pvs)) {
          return;
       }
       Method method = (Method) this.member;
       Object[] arguments;
       if (this.cached) {
          try {
             arguments = resolveCachedArguments(beanName);
          }
          catch (NoSuchBeanDefinitionException ex) {
             // Unexpected removal of target bean for cached argument -> re-resolve
             arguments = resolveMethodArguments(method, bean, beanName);
          }
       }
       else {
          arguments = resolveMethodArguments(method, bean, beanName);
       }
       if (arguments != null) {
          ReflectionUtils.makeAccessible(method);
             method.invoke(bean, arguments);
       }
    }
    

    无论是属性注入点还是方法注入点,在进行注入的时候,它们最核心的代码都是BeanFactory的resolveDependency(),调用该方法来获取注入的属性值,即Bean实例对象,下一章节会对依赖注入的核心方法,如果获取依赖对象作详细介绍

    @Resource的注入是在CommonAnnotationBeanPostProcessor类中,寻找注入点以及属性注入同上

三、设置BeanDefinition中的属性值

在前面的属性注入中,对于RootBeanDefinition中的定义的属性MutablePropertyValues却没有赋值,而对这部分属性的赋值操作,是在完成@Autowired和@@Resource这类注解的属性注入后进行

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    ……
    if (pvs != null) {
        applyPropertyValues(beanName, mbd, bw, pvs);
    }
}

调用applyPropertyValues()方法来对进行属性赋值

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/112163.html

(0)
Java光头强的头像Java光头强

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!