• 这篇文章,我们来谈一谈Spring中的属性注入


    本系列文章:

    读源码,我们可以从第一行读起

    你知道Spring是怎么解析配置类的吗?

    配置类为什么要添加@Configuration注解?

    谈谈Spring中的对象跟Bean,你知道Spring怎么创建对象的吗?

    推荐阅读:

    Spring官网阅读 | 总结篇

    Spring杂谈

    本系列文章将会带你一行行的将Spring的源码吃透,推荐阅读的文章是阅读源码的基础!

    前言

    在前面的文章中已经知道了Spring是如何将一个对象创建出来的,那么紧接着,Spring就需要将这个对象变成一个真正的Bean了,这个过程主要分为两步

    1. 属性注入
    2. 初始化

    在这两个过程中,Bean的后置处理器会穿插执行,其中有些后置处理器是为了帮助完成属性注入或者初始化的,而有些后置处理器是Spring提供给程序员进行扩展的,当然,这二者并不冲突。整个Spring创建对象并将对象变成Bean的过程就是我们经常提到了Spring中Bean的生命周期。当然,本系列源码分析的文章不会再对生命周期的概念做过多阐述了,如果大家有这方面的需求的话可以参考我之前的文章,或者关注我的公众号:程序员DMZ

    Spring官网阅读(九)Spring中Bean的生命周期(上)

    Spring官网阅读(十)Spring中Bean的生命周期(下)

    源码分析

    闲话不再多说,我们正式进入源码分析阶段,本文重点要分析的方法就是org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean,其源码如下:

    doCreateBean

    	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
    			throws BeanCreationException {
    
    		// 创建对象的过程在上篇文章中我们已经介绍过了,这里不再赘述
    		BeanWrapper instanceWrapper = null;
    		if (mbd.isSingleton()) {
    			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    		}
    		if (instanceWrapper == null) {
    			instanceWrapper = createBeanInstance(beanName, mbd, args);
    		}
            
            // 获取到创建的这个对象
    		final Object bean = instanceWrapper.getWrappedInstance();
    		Class<?> beanType = instanceWrapper.getWrappedClass();
    		if (beanType != NullBean.class) {
    			mbd.resolvedTargetType = beanType;
    		}
    
    		// Allow post-processors to modify the merged bean definition.
            // 按照官方的注释来说,这个地方是Spring提供的一个扩展点,对程序员而言,我们可以通过一个实现了MergedBeanDefinitionPostProcessor的后置处理器来修改bd中的属性,从而影响到后续的Bean的生命周期
            // 不过官方自己实现的后置处理器并没有去修改bd,而是调用了applyMergedBeanDefinitionPostProcessors方法
            // 这个方法名直译过来就是-应用合并后的bd,也就是说它这里只是对bd做了进一步的使用而没有真正的修改
    		synchronized (mbd.postProcessingLock) {
               // bd只允许被处理一次
    			if (!mbd.postProcessed) {
    				try {
                        // 应用合并后的bd
    					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
    				}
    				catch (Throwable ex) {
    					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
    							"Post-processing of merged bean definition failed", ex);
    				}
                    // 标注这个bd已经被MergedBeanDefinitionPostProcessor的后置处理器处理过
                    // 那么在第二次创建Bean的时候,不会再次调用applyMergedBeanDefinitionPostProcessors
    				mbd.postProcessed = true;
    			}
    		}
    
    		// 这里是用来出来循环依赖的,关于循环以来,在介绍完正常的Bean的创建后,单独用一篇文章说明
            // 这里不做过多解释
    		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
    				isSingletonCurrentlyInCreation(beanName));
    		if (earlySingletonExposure) {
    			if (logger.isTraceEnabled()) {
    				logger.trace("Eagerly caching bean '" + beanName +
    						"' to allow for resolving potential circular references");
    			}
    			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    		}
    
    
    		Object exposedObject = bean;
    		try {
                // 我们这篇文章重点要分析的就是populateBean方法,在这个方法中完成了属性注入
    			populateBean(beanName, mbd, instanceWrapper);
                // 初始化
    			exposedObject = initializeBean(beanName, exposedObject, mbd);
    		}
    		catch (Throwable ex) {
    			// 省略异常代码
    		}
    
    		// 后续代码不在本文探讨范围内了,暂不考虑
    
    		return exposedObject;
    	}
    

    applyMergedBeanDefinitionPostProcessors

    源码如下:

    // 可以看到这个方法的代码还是很简单的,就是调用了MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法
    protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof MergedBeanDefinitionPostProcessor) {
                MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
                bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
            }
        }
    }
    

    这个时候我们就要思考一个问题,容器中现在有哪些后置处理器是MergedBeanDefinitionPostProcessor呢?

    image-20200613200058693

    查看这个方法的实现类我们会发现总共就这么几个类实现了MergedBeanDefinitionPostProcessor接口。实际上除了ApplicationListenerDetector之外,其余的后置处理器的逻辑都差不多。我们在这里我们主要就分析两个后置处理

    1. ApplicationListenerDetector
    2. AutowiredAnnotationBeanPostProcessor

    ApplicationListenerDetector

    首先,我们来ApplicationListenerDetector,这个类在之前的文章中也多次提到过了,它的作用是用来处理嵌套Bean的情况,主要是保证能将嵌套在Bean标签中的ApplicationListener也能添加到容器的监听器集合中去。我们先通过一个例子来感受下这个后置处理器的作用吧

    配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    	<bean class="com.dmz.source.populate.service.DmzService" id="dmzService">
    		<constructor-arg name="orderService">
    			<bean class="com.dmz.source.populate.service.OrderService"/>
    		</constructor-arg>
    	</bean>
    </beans>
    

    示例代码:

    // 事件
    public class DmzEvent extends ApplicationEvent {
    	public DmzEvent(Object source) {
    		super(source);
    	}
    }
    
    public class DmzService {
    
    	OrderService orderService;
    
    	public DmzService(OrderService orderService) {
    		this.orderService = orderService;
    	}
    }
    // 实现ApplicationListener接口
    public class OrderService implements ApplicationListener<DmzEvent> {
    	@Override
    	public void onApplicationEvent(DmzEvent event) {
    		System.out.println(event.getSource());
    	}
    }
    
    public class Main {
    	public static void main(String[] args) {
    		ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("application-populate.xml");
    		cc.publishEvent(new DmzEvent("my name is dmz"));
    	}
    }
    
    // 程序运行结果,控制台打印:my name is dmz
    

    说明OrderService已经被添加到了容器的监听器集合中。但是请注意,在这种情况下,如果要使OrderService能够执行监听的逻辑,必须要满足下面这两个条件

    • 外部的Bean要是单例的,对于我们的例子而言就是dmzService
    • 内嵌的Bean也必须是单例的,在上面的例子中也就是orderService必须是单例

    另外需要注意的是,这种嵌套的Bean比较特殊,它虽然由Spring创建,但是确不存在于容器中,就是说我们不能将其作为依赖注入到别的Bean中。

    AutowiredAnnotationBeanPostProcessor

    对应源码如下:

    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        // 找到注入的元数据,第一次是构建,后续可以直接从缓存中拿
        // 注解元数据其实就是当前这个类中的所有需要进行注入的“点”的集合,
        // 注入点(InjectedElement)包含两种,字段/方法
        // 对应的就是AutowiredFieldElement/AutowiredMethodElement
        InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
        // 排除掉被外部管理的注入点
        metadata.checkConfigMembers(beanDefinition);
    }
    

    上面代码的核心逻辑就是

    • 找到所有的注入点,其实就是被@Autowired注解修饰的方法以及字段,同时静态的方法以及字段也会被排除
    • 排除掉被外部管理的注入点,在后续的源码分析中我们再细说

    findAutowiringMetadata

    // 这个方法的核心逻辑就是先从缓存中获取已经解析好的注入点信息,很明显,在原型情况下才会使用缓存
    // 创建注入点的核心逻辑在buildAutowiringMetadata方法中
    private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
        String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
        InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
        // 可能我们会修改bd中的class属性,那么InjectionMetadata中的注入点信息也需要刷新
        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;
    }
    

    buildAutowiringMetadata

    // 我们应用中使用@Autowired注解标注在字段上或者setter方法能够完成属性注入
    // 就是因为这个方法将@Autowired注解标注的方法以及字段封装成InjectionMetadata
    // 在后续阶段会调用InjectionMetadata的inject方法进行注入
    private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
        List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
        Class<?> targetClass = clazz;
    
        do {
            final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
    		// 处理所有的被@AutoWired/@Value注解标注的字段
            ReflectionUtils.doWithLocalFields(targetClass, field -> {
                AnnotationAttributes ann = findAutowiredAnnotation(field);
                if (ann != null) {
                    // 静态字段会直接跳过
                    if (Modifier.isStatic(field.getModifiers())) {
                        // 省略日志打印
                        return;
                    }
                    // 得到@AutoWired注解中的required属性
                    boolean required = determineRequiredStatus(ann);
                    currElements.add(new AutowiredFieldElement(field, required));
                }
            });
    		// 处理所有的被@AutoWired注解标注的方法,相对于字段而言,这里需要对桥接方法进行特殊处理
            ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                // 只处理一种特殊的桥接场景,其余的桥接方法都会被忽略
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                    return;
                }
                AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
                // 处理方法时需要注意,当父类中的方法被子类重写时,如果子父类中的方法都加了@Autowired
                // 那么此时父类方法不能被处理,即不能被封装成一个AutowiredMethodElement
                if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                    if (Modifier.isStatic(method.getModifiers())) {
                        // 省略日志打印
                        return;
                    }
                    if (method.getParameterCount() == 0) {
                        // 当方法的参数数量为0时,虽然不需要进行注入,但是还是会把这个方法作为注入点使用
                        // 这个方法最终还是会被调用
                        if (logger.isInfoEnabled()) {
                            logger.info("Autowired annotation should only be used on methods with parameters: " +
                                        method);
                        }
                    }
                    boolean required = determineRequiredStatus(ann);
                    // PropertyDescriptor: 属性描述符
                    // 就是通过解析getter/setter方法,例如void getA()会解析得到一个属性名称为a
                    // readMethod为getA的PropertyDescriptor,
                    // 在《Spring官网阅读(十四)Spring中的BeanWrapper及类型转换》文中已经做过解释
                    // 这里不再赘述,这里之所以来这么一次查找是因为当XML中对这个属性进行了配置后,
                    // 那么就不会进行自动注入了,XML中显示指定的属性优先级高于注解
                    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);		   // 构造一个对应的AutowiredMethodElement,后续这个方法会被执行
                    // 方法的参数会被自动注入,这里不限于setter方法
                    currElements.add(new AutowiredMethodElement(method, required, pd));
                }
            });
    		// 会处理父类中字段上及方法上的@AutoWired注解,并且父类的优先级比子类高
            elements.addAll(0, currElements);
            targetClass = targetClass.getSuperclass();
        }
        while (targetClass != null && targetClass != Object.class);
    
        return new InjectionMetadata(clazz, elements);
    }
    
    难点代码分析

    上面的代码整体来说应该很简单,就如我们之前所说的,处理带有@Autowired注解的字段及方法,同时会过滤掉所有的静态字段及方法。上面复杂的地方在于对桥接方法的处理,可能大部分人都没办法理解这几行代码:

    // 第一行
    Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
    
    // 第二行
    if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
        return;
    }
    
    // 第三行
    if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
    
    }
    

    要理解这些代码,首先你得知道什么是桥接,为此我已经写好了一篇文章:

    Spring杂谈 | 从桥接方法到JVM方法调用

    除了在上面的文章中提到的桥接方法外,还有一种特殊的情况

    // A类跟B类在同一个包下,A不是public的
    class A {
    	public void test(){
    
    	}
    }
    
    // 在B中会生成一个跟A中的方法描述符(参数+返回值)一模一样的桥接方法
    // 这个桥接方法实际上就是调用父类中的方法
    // 具体可以参考:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=63424113
    public class B extends A {
    }
    

    在理解了什么是桥接之后,那么上边的第一行代码你应该就能看懂了,就以上面的代码为例,B中会生成一个桥接方法,对应的被桥接的方法就是A中的test方法。

    接着,我们看看第二行代码

    public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) {
        // 说明这个方法本身就不是桥接方法,直接返回true
        if (bridgeMethod == bridgedMethod) {
            return true;
        }
        // 说明是桥接方法,并且方法描述符一致
        // 当且仅当是上面例子中描述的这种桥接的时候这个判断才会满足
        // 正常来说桥接方法跟被桥接方法的返回值+参数类型肯定不一致
        // 所以这个判断会过滤掉其余的所有类型的桥接方法
        // 只会保留本文提及这种特殊情况下产生的桥接方法
        return (bridgeMethod.getReturnType().equals(bridgedMethod.getReturnType()) &&
                Arrays.equals(bridgeMethod.getParameterTypes(), bridgedMethod.getParameterTypes()));
    }
    

    最后,再来看看第三行代码,核心就是这句 method.equals(ClassUtils.getMostSpecificMethod(method, clazz)。这句代码的主要目的就是为了处理下面这种情况

    @Component
    public class D extends C {
    
    	@Autowired
    	@Override
    	public void setDmzService(DmzService dmzService) {
    		dmzService.init();
    		this.dmzService = dmzService;
    	}
    }
    
    // C不是Spring中的组件
    public class C {
    	DmzService dmzService;
        @Autowired
    	public void setDmzService(DmzService dmzService) {
    		this.dmzService = dmzService;
    	}
    }
    
    

    这种情况下,在处理D中的@Autowired注解时,虽然我们要处理父类中的@Autowired注解,但是因为子类中的方法已经复写了父类中的方法,所以此时应该要跳过父类中的这个被复写的方法,这就是第三行代码的作用。

    小结

    到这里我们主要分析了applyMergedBeanDefinitionPostProcessors这段代码的作用,它的执行时机是在创建对象之后,属性注入之前。按照官方的定义来说,到这里我们仍然可以使用这个方法来修改bd的定义,那么相对于通过BeanFactoryPostProcessor的方式修改bd,applyMergedBeanDefinitionPostProcessors这个方法影响的范围更小,BeanFactoryPostProcessor影响的是整个Bean的生命周期,而applyMergedBeanDefinitionPostProcessors只会影响属性注入之后的生命周期。

    其次,我们分析了Spring中内置的MergedBeanDefinitionPostProcessor,选取了其中两个特殊的后置处理器进行分析,其中ApplicationListenerDetector主要处理内嵌的事件监听器,而AutowiredAnnotationBeanPostProcessor主要用于处理@Autowired注解,实际上我们会发现,到这里还只是完成了@Autowired注解的解析,还没有真正开始进行注入,真正注入的逻辑在后面我们要分析的populateBean方法中,在这个方法中会使用解析好的注入元信息完成真正的属性注入,那么接下来我们就开始分析populateBean这个方法的源码。

    populateBean

    循环依赖的代码我们暂且跳过,后续出一篇专门文章解读循环依赖,我们直接看看populateBean到底做了什么。

    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    
        // 处理空实例
        if (bw == null) {
            // 如果创建的对象为空,但是在XML中又配置了需要注入的属性的话,那么直接报错
            if (mbd.hasPropertyValues()) {
                throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
            }
            else {
                // 空对象,不进行属性注入
                return;
            }
        }
    
        // 满足两个条件,不是合成类 && 存在InstantiationAwareBeanPostProcessor
        // 其中InstantiationAwareBeanPostProcessor主要作用就是作为Bean的实例化前后的钩子
        // 外加完成属性注入,对于三个方法就是
        // postProcessBeforeInstantiation  创建对象前调用
        // postProcessAfterInstantiation   对象创建完成,@AutoWired注解解析后调用   
        // postProcessPropertyValues(已过期,被postProcessProperties替代) 进行属性注入
        // 下面这段代码的主要作用就是我们可以提供一个InstantiationAwareBeanPostProcessor
        // 提供的这个后置处理如果实现了postProcessAfterInstantiation方法并且返回false
        // 那么可以跳过Spring默认的属性注入,但是这也意味着我们要自己去实现属性注入的逻辑
        // 所以一般情况下,我们也不会这么去扩展
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                        return;
                    }
                }
            }
        }
    	
        // 这里其实就是判断XML是否提供了属性相关配置
        PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
    	
        // 确认注入模型
        int resolvedAutowireMode = mbd.getResolvedAutowireMode();
        
        // 主要处理byName跟byType两种注入模型,byConstructor这种注入模型在创建对象的时候已经处理过了
        // 这里都是对自动注入进行处理,byName跟byType两种注入模型均是依赖setter方法
        // byName,根据setter方法的名字来查找对应的依赖,例如setA,那么就是去容器中查找名字为a的Bean
        // byType,根据setter方法的参数类型来查找对应的依赖,例如setXx(A a),就是去容器中查询类型为A的bean
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
            if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }
            if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }
            // pvs是XML定义的属性
            // 自动注入后,bean实际用到的属性就应该要替换成自动注入后的属性
            pvs = newPvs;
        }
    	// 检查是否有InstantiationAwareBeanPostProcessor
        // 前面说过了,这个后置处理器就是来完成属性注入的
        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        
        //  是否需要依赖检查,默认是不会进行依赖检查的
        boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
    	
        // 下面这段代码有点麻烦了,因为涉及到版本问题
        // 其核心代码就是调用了postProcessProperties完成了属性注入
       
        PropertyDescriptor[] filteredPds = null;
        
        // 存在InstantiationAwareBeanPostProcessor,我们需要调用这类后置处理器的方法进行注入
    		if (hasInstAwareBpps) {
    			if (pvs == null) {
    				pvs = mbd.getPropertyValues();
    			}
    			for (BeanPostProcessor bp : getBeanPostProcessors()) {
    				if (bp instanceof InstantiationAwareBeanPostProcessor) {
    					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                        // 这句就是核心
    					PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
    					if (pvsToUse == null) {
    						if (filteredPds == null) {
                                // 得到需要进行依赖检查的属性的集合
    							filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
    						}
                            //  这个方法已经过时了,放到这里就是为了兼容老版本
    						pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
    						if (pvsToUse == null) {
    							return;
    						}
    					}
    					pvs = pvsToUse;
    				}
    			}
    		}
        // 需要进行依赖检查
    		if (needsDepCheck) {
    			if (filteredPds == null) {
                    // 得到需要进行依赖检查的属性的集合
    				filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
    			}
                // 对需要进行依赖检查的属性进行依赖检查
    			checkDependencies(beanName, mbd, filteredPds, pvs);
    		}
        // 将XML中的配置属性应用到Bean上
    		if (pvs != null) {
    			applyPropertyValues(beanName, mbd, bw, pvs);
    		}
    }
    

    上面这段代码主要可以拆分为三个部分

    1. 处理自动注入
    2. 处理属性注入(主要指处理@Autowired注解),最重要
    3. 处理依赖检查

    处理自动注入

    autowireByName

    对应源码如下:

    protected void autowireByName(
        String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
        // 得到符合下面条件的属性名称
        // 1.有setter方法
        // 2.需要进行依赖检查
        // 3.不包含在XML配置中
        // 4.不是简单类型(基本数据类型,枚举,日期等)
        // 这里可以看到XML配置优先级高于自动注入的优先级
        // 不进行依赖检查的属性,也不会进行属性注入
        String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        for (String propertyName : propertyNames) {
            if (containsBean(propertyName)) {
                Object bean = getBean(propertyName);
                // 将自动注入的属性添加到pvs中去
                pvs.add(propertyName, bean);
                // 注册bean之间的依赖关系
                registerDependentBean(propertyName, beanName);
                // 忽略日志
            }
            // 忽略日志
        }
    }
    

    看到了吗?代码就是这么的简单,不是要通过名称注入吗?直接通过beanName调用getBean,完事儿

    autowireByType

    	protected void autowireByType(
    			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
    		// 这个类型转换器,主要是在处理@Value时需要使用
    		TypeConverter converter = getCustomTypeConverter();
    		if (converter == null) {
    			converter = bw;
    		}
    
    		Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
    		// 得到符合下面条件的属性名称
    		// 1.有setter方法
    		// 2.需要进行依赖检查
    		// 3.不包含在XML配置中
    		// 4.不是简单类型(基本数据类型,枚举,日期等)
    		// 这里可以看到XML配置优先级高于自动注入的优先级
    		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    		for (String propertyName : propertyNames) {
    			try {
    				PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
    				if (Object.class != pd.getPropertyType()) {
    					// 这里获取到的就是setter方法的参数,因为我们需要按照类型进行注入嘛
    					MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
    					
                        // 如果是PriorityOrdered在进行类型匹配时不会去匹配factoryBean
    					// 如果不是PriorityOrdered,那么在查找对应类型的依赖的时候会会去匹factoryBean
    				 	// 这就是Spring的一种设计理念,实现了PriorityOrdered接口的Bean被认为是一种
                        // 最高优先级的Bean,这一类的Bean在进行为了完成装配而去检查类型时,
                        // 不去检查factoryBean
                        // 具体可以参考PriorityOrdered接口上的注释文档
    					boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
    					// 将参数封装成为一个依赖描述符
    					// 依赖描述符会通过:依赖所在的类,字段名/方法名,依赖的具体类型等来描述这个依赖
    					DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
    					// 解析依赖,这里会处理@Value注解
                        // 另外,通过指定的类型到容器中查找对应的bean
    					Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
    					if (autowiredArgument != null) {
    						// 将查找出来的依赖属性添加到pvs中,后面会将这个pvs应用到bean上
    						pvs.add(propertyName, autowiredArgument);
    					}
    					// 注册bean直接的依赖关系
    					for (String autowiredBeanName : autowiredBeanNames) {
    						registerDependentBean(autowiredBeanName, beanName);
    						if (logger.isDebugEnabled()) {
    							logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
    									propertyName + "' to bean named '" + autowiredBeanName + "'");
    						}
    					}
    					autowiredBeanNames.clear();
    				}
    			}
    			catch (BeansException ex) {
    				throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
    			}
    		}
    	}
    
    
    resolveDependency

    这个方法在Spring杂谈 | 什么是ObjectFactory?什么是ObjectProvider?已经做过分析了,本文不再赘述。

    可以看到,真正做事的方法是doResolveDependency

    @Override
    public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    	// descriptor代表当前需要注入的那个字段,或者方法的参数,也就是注入点
        // ParameterNameDiscovery用于解析方法参数名称
        descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
        // 1. Optional<T>
        if (Optional.class == descriptor.getDependencyType()) {
            return createOptionalDependency(descriptor, requestingBeanName);
        // 2. ObjectFactory<T>、ObjectProvider<T>
        } else if (ObjectFactory.class == descriptor.getDependencyType() ||
                 ObjectProvider.class == descriptor.getDependencyType()) {
            return new DependencyObjectProvider(descriptor, requestingBeanName);
        // 3. javax.inject.Provider<T>
        } else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
            return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
        } else {
            // 4. @Lazy
            Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
                descriptor, requestingBeanName);
            // 5. 正常情况
            if (result == null) {
                result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
            }
            return result;
        }
    }
    
    doResolveDependency
    	public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
    			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    
    		InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    		try {
    			Object shortcut = descriptor.resolveShortcut(this);
    			if (shortcut != null) {
    				return shortcut;
    			}
    			// 依赖的具体类型
    			Class<?> type = descriptor.getDependencyType();
    			// 处理@Value注解,这里得到的时候@Value中的值
    			Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
    			if (value != null) {
    				if (value instanceof String) {
    					// 解析@Value中的占位符
    					String strVal = resolveEmbeddedValue((String) value);
    					// 获取到对应的bd
    					BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
    					// 处理EL表达式
    					value = evaluateBeanDefinitionString(strVal, bd);
    				}
    				// 通过解析el表达式可能还需要进行类型转换
    				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
    				return (descriptor.getField() != null ?
    						converter.convertIfNecessary(value, type, descriptor.getField()) :
    						converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
    			}
    			
                // 对map,collection,数组类型的依赖进行处理
    			// 最终会根据集合中的元素类型,调用findAutowireCandidates方法
    			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
    			if (multipleBeans != null) {
    				return multipleBeans;
    			}
    			
                // 根据指定类型可能会找到多个bean
                // 这里返回的既有可能是对象,也有可能是对象的类型
                // 这是因为到这里还不能明确的确定当前bean到底依赖的是哪一个bean
                // 所以如果只会返回这个依赖的类型以及对应名称,最后还需要调用getBean(beanName)
                // 去创建这个Bean
    			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
    			// 一个都没找到,直接抛出异常
    			if (matchingBeans.isEmpty()) {
    				if (isRequired(descriptor)) {
    					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
    				}
    				return null;
    			}
    
    			String autowiredBeanName;
    			Object instanceCandidate;
    			// 通过类型找到了多个
    			if (matchingBeans.size() > 1) {
    				// 根据是否是主Bean
    				// 是否是最高优先级的Bean
    				// 是否是名称匹配的Bean
    				// 来确定具体的需要注入的Bean的名称
                    // 到这里可以知道,Spring在查找依赖的时候遵循先类型再名称的原则(没有@Qualifier注解情况下)
    				autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
    				if (autowiredBeanName == null) {
    					// 无法推断出具体的名称
    					// 如果依赖是必须的,直接抛出异常
    					// 如果依赖不是必须的,但是这个依赖类型不是集合或者数组,那么也抛出异常
    					if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
    						return descriptor.resolveNotUnique(type, matchingBeans);
    					}
    					// 依赖不是必须的,但是依赖类型是集合或者数组,那么返回一个null
    					else {
    						return null;
    					}
    				}
    				instanceCandidate = matchingBeans.get(autowiredBeanName);
    			}
    			else {
    				// 直接找到了一个对应的Bean
    				Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
    				autowiredBeanName = entry.getKey();
    				instanceCandidate = entry.getValue();
    			}
    			if (autowiredBeanNames != null) {
    				autowiredBeanNames.add(autowiredBeanName);
    			}
                
                // 前面已经说过了,这里可能返回的是Bean的类型,所以需要进一步调用getBean
    			if (instanceCandidate instanceof Class) {
    				instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
    			}
                
                // 做一些检查,如果依赖是必须的,查找出来的依赖是一个null,那么报错
                // 查询处理的依赖类型不符合,也报错
    			Object result = instanceCandidate;
    			if (result instanceof NullBean) {
    				if (isRequired(descriptor)) {
    					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
    				}
    				result = null;
    			}
    			if (!ClassUtils.isAssignableValue(type, result)) {
    				throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
    			}
    			return result;
    		}
    		finally {
    			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
    		}
    	}
    
    findAutowireCandidates
    protected Map<String, Object> findAutowireCandidates(
        @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
    	
        // 简单来说,这里就是到容器中查询requiredType类型的所有bean的名称的集合
        // 这里会根据descriptor.isEager()来决定是否要匹配factoryBean类型的Bean
        // 如果isEager()为true,那么会匹配factoryBean,反之,不会
        String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this, requiredType, true, descriptor.isEager());
       
        Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
       
        // 第一步会到resolvableDependencies这个集合中查询是否已经存在了解析好的依赖
        // 像我们之所以能够直接在Bean中注入applicationContext对象
        // 就是因为Spring之前就将这个对象放入了resolvableDependencies集合中
        for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
            if (autowiringType.isAssignableFrom(requiredType)) {
                Object autowiringValue = this.resolvableDependencies.get(autowiringType);
                
                // 如果resolvableDependencies放入的是一个ObjectFactory类型的依赖
                // 那么在这里会生成一个代理对象
                // 例如,我们可以在controller中直接注入request对象
                // 就是因为,容器启动时就在resolvableDependencies放入了一个键值对
                // 其中key为:Request.class,value为:ObjectFactory
                // 在实际注入时放入的是一个代理对象
                autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
                if (requiredType.isInstance(autowiringValue)) {
                    // 这里放入的key不是Bean的名称
                    // value是实际依赖的对象
                    result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                    break;
                }
            }
        }
        
        // 接下来开始对之前查找出来的类型匹配的所有BeanName进行处理
        for (String candidate : candidateNames) {
            // 不是自引用,什么是自引用?
            // 1.候选的Bean的名称跟需要进行注入的Bean名称相同,意味着,自己注入自己
            // 2.或者候选的Bean对应的factoryBean的名称跟需要注入的Bean名称相同,
            // 也就是说A依赖了B但是B的创建又需要依赖A
            // 要符合注入的条件
            if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
                // 调用addCandidateEntry,加入到返回集合中,后文有对这个方法的分析
                addCandidateEntry(result, candidate, descriptor, requiredType);
            }
        }
        
        // 排除自引用的情况下,没有找到一个合适的依赖
        if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
            // 1.先走fallback逻辑,Spring提供的一个扩展吧,感觉没什么卵用
            // 默认情况下fallback的依赖描述符就是自身
            DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
            for (String candidate : candidateNames) {
                if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) {
                    addCandidateEntry(result, candidate, descriptor, requiredType);
                }
            }
            // fallback还是失败
            if (result.isEmpty()) {
                // 处理自引用
                // 从这里可以看出,自引用的优先级是很低的,只有在容器中真正的只有这个Bean能作为
                // 候选者的时候,才会去处理,否则自引用是被排除掉的
                for (String candidate : candidateNames) {
                    if (isSelfReference(beanName, candidate) &&
                        // 不是一个集合或者
                        // 是一个集合,但是beanName跟candidate的factoryBeanName相同
                        (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
                        isAutowireCandidate(candidate, fallbackDescriptor)) {
                        addCandidateEntry(result, candidate, descriptor, requiredType);
                    }
                }
            }
        }
        return result;
    }
    
    
    // candidates:就是findAutowireCandidates方法要返回的候选集合
    // candidateName:当前的这个候选Bean的名称
    // descriptor:依赖描述符
    // requiredType:依赖的类型
    private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
                                   DependencyDescriptor descriptor, Class<?> requiredType) {
    	
        // 如果依赖是一个集合,或者容器中已经包含这个单例了
        // 那么直接调用getBean方法创建或者获取这个Bean
        if (descriptor instanceof MultiElementDescriptor || containsSingleton(candidateName)) {
            Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
            candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
        }
        // 如果依赖的类型不是一个集合,这个时候还不能确定到底要使用哪个依赖,
        // 所以不能将这些Bean创建出来,所以这个时候,放入candidates是Bean的名称以及类型
        else {
            candidates.put(candidateName, getType(candidateName));
        }
    }
    

    处理属性注入(@Autowired)

    postProcessProperties

    // 在applyMergedBeanDefinitionPostProcessors方法执行的时候,
    // 已经解析过了@Autowired注解(buildAutowiringMetadata方法)
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        // 这里获取到的是解析过的缓存好的注入元数据
        InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
        try {
            // 直接调用inject方法
            // 存在两种InjectionMetadata
            // 1.AutowiredFieldElement
            // 2.AutowiredMethodElement
            // 分别对应字段的属性注入以及方法的属性注入
            metadata.inject(bean, beanName, pvs);
        }
        catch (BeanCreationException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
        }
        return pvs;
    }
    
    字段的属性注入
    // 最终反射调用filed.set方法
    protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
        Field field = (Field) this.member;
        Object value;
        if (this.cached) {
            // 第一次注入的时候肯定没有缓存
            // 这里也是对原型情况的处理
            value = resolvedCachedArgument(beanName, this.cachedFieldValue);
        } else {
            DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
            desc.setContainingClass(bean.getClass());
            Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
            Assert.state(beanFactory != null, "No BeanFactory available");
            TypeConverter typeConverter = beanFactory.getTypeConverter();
            try {
                // 这里可以看到,对@Autowired注解在字段上的处理
                // 跟byType下自动注入的处理是一样的,就是调用resolveDependency方法
                value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
            } catch (BeansException ex) {
                throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
            }
            synchronized (this) {
                // 没有缓存过的话,这里需要进行缓存
                if (!this.cached) {
                    if (value != null || this.required) {
                        this.cachedFieldValue = desc;
                        // 注册Bean之间的依赖关系
                        registerDependentBeans(beanName, autowiredBeanNames);
                        // 如果这个类型的依赖只存在一个的话,我们就能确定这个Bean的名称
                        // 那么直接将这个名称缓存到ShortcutDependencyDescriptor中
                        // 第二次进行注入的时候就可以直接调用getBean(beanName)得到这个依赖了
                        // 实际上正常也只有一个,多个就报错了
                        // 另外这里会过滤掉@Vlaue得到的依赖
                        if (autowiredBeanNames.size() == 1) {
                            String autowiredBeanName = autowiredBeanNames.iterator().next();
                            // 通过resolvableDependencies这个集合找的依赖不满足containsBean条件
                            // 不会进行缓存,因为缓存实际还是要调用getBean,而resolvableDependencies
                            // 是没法通过getBean获取的
                            if (beanFactory.containsBean(autowiredBeanName) &&
                                beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {							 // 依赖描述符封装成ShortcutDependencyDescriptor进行缓存
                                this.cachedFieldValue = new ShortcutDependencyDescriptor(
                                    desc, autowiredBeanName, field.getType());
                            }
                        }
                    } else {
                        this.cachedFieldValue = null;
                    }
                    this.cached = true;
                }
            }
        }
        if (value != null) {
            // 反射调用Field.set方法
            ReflectionUtils.makeAccessible(field);
            field.set(bean, value);
        }
    }
    
    方法的属性注入
    // 代码看着很长,实际上逻辑跟字段注入基本一样
    protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
        // 判断XML中是否配置了这个属性,如果配置了直接跳过
        // 换而言之,XML配置的属性优先级高于@Autowired注解
        if (checkPropertySkipping(pvs)) {
            return;
        }
        Method method = (Method) this.member;
        Object[] arguments;
        if (this.cached) {
            arguments = resolveCachedArguments(beanName);
        } else {
            // 通过方法参数类型构造依赖描述符
            // 逻辑基本一样的,最终也是调用beanFactory.resolveDependency方法
            Class<?>[] paramTypes = method.getParameterTypes();
            arguments = new Object[paramTypes.length];
            DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length];
            Set<String> autowiredBeans = new LinkedHashSet<>(paramTypes.length);
            Assert.state(beanFactory != null, "No BeanFactory available");
            TypeConverter typeConverter = beanFactory.getTypeConverter();
            
            // 遍历方法的每个参数
            for (int i = 0; i < arguments.length; i++) {
                MethodParameter methodParam = new MethodParameter(method, i);
                DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
                currDesc.setContainingClass(bean.getClass());
                descriptors[i] = currDesc;
                try {
                    // 还是要调用这个方法
                    Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
                    if (arg == null && !this.required) {
                        arguments = null;
                        break;
                    }
                    arguments[i] = arg;
                } catch (BeansException ex) {
                    throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
                }
            }
            synchronized (this) {
                if (!this.cached) {
                    if (arguments != null) {
                        Object[] cachedMethodArguments = new Object[paramTypes.length];
                        System.arraycopy(descriptors, 0, cachedMethodArguments, 0, arguments.length);  
                        // 注册bean之间的依赖关系
                        registerDependentBeans(beanName, autowiredBeans);
                        
                        // 跟字段注入差不多,存在@Value注解,不进行缓存
                        if (autowiredBeans.size() == paramTypes.length) {
                            Iterator<String> it = autowiredBeans.iterator();
                            for (int i = 0; i < paramTypes.length; i++) {
                                String autowiredBeanName = it.next();
                                if (beanFactory.containsBean(autowiredBeanName) &&
                                    beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
                                    cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
                                        descriptors[i], autowiredBeanName, paramTypes[i]);
                                }
                            }
                        }
                        this.cachedMethodArguments = cachedMethodArguments;
                    } else {
                        this.cachedMethodArguments = null;
                    }
                    this.cached = true;
                }
            }
        }
        if (arguments != null) {
            try {
                // 反射调用方法
                // 像我们的setter方法就是在这里调用的
                ReflectionUtils.makeAccessible(method);
                method.invoke(bean, arguments);
            } catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }
    }
    

    处理依赖检查

    protected void checkDependencies(
        String beanName, AbstractBeanDefinition mbd, PropertyDescriptor[] pds, PropertyValues pvs)
        throws UnsatisfiedDependencyException {
    
        int dependencyCheck = mbd.getDependencyCheck();
        for (PropertyDescriptor pd : pds) {
            
            // 有set方法但是在pvs中没有对应属性,那么需要判断这个属性是否要进行依赖检查
            // 如果需要进行依赖检查的话,就需要报错了
            // pvs中保存的是自动注入以及XML配置的属性
            if (pd.getWriteMethod() != null && !pvs.contains(pd.getName())) {
               
                // 是否是基本属性,枚举/日期等也包括在内
                boolean isSimple = BeanUtils.isSimpleProperty(pd.getPropertyType());
               	
                // 如果DEPENDENCY_CHECK_ALL,对任意属性都开启了依赖检查,报错
                // DEPENDENCY_CHECK_SIMPLE,对基本属性开启了依赖检查并且是基本属性,报错
                // DEPENDENCY_CHECK_OBJECTS,对非基本属性开启了依赖检查并且不是非基本属性,报错
                boolean unsatisfied = (dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_ALL) ||
                    (isSimple && dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_SIMPLE) ||
                    (!isSimple && dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_OBJECTS);
                
                if (unsatisfied) {
                    throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, pd.getName(),
                                                             "Set this property value or disable dependency checking for this bean.");
                }
            }
        }
    }
    

    将解析出来的属性应用到Bean上

    到这一步解析出来的属性主要有三个来源

    1. XML中配置的
    2. 通过byName的方式自动注入的
    3. 通过byType的方式自动注入的

    但是在应用到Bean前还需要做一步类型转换,这一部分代码实际上跟我们之前在Spring官网阅读(十四)Spring中的BeanWrapper及类型转换介绍的差不多,而且因为XML跟自动注入的方式都不常见,正常@Autowired的方式进行注入的话,这个方法没有什么用,所以本文就不再赘述。

    总结

    本文我们主要分析了Spring在属性注入过程中的相关代码,整个属性注入可以分为两个部分

    1. @Autowired/@Vale的方式完成属性注入
    2. 自动注入(byType/byName

    完成属性注入的核心方法其实就是doResolveDependencydoResolveDependency这个方法的逻辑简单来说分为两步:

    1. 通过依赖类型查询到所有的类型匹配的bean的名称
    2. 如果找到了多个的话,再根据依赖的名称匹配对应的Bean的名称
    3. 调用getBean得到这个需要被注入的Bean
    4. 最后反射调用字段的set方法完成属性注入

    从上面也可以知道,其实整个属性注入的逻辑是很简单的。

    如果本文对你有帮助的话,记得点个赞吧!也欢迎关注我的公众号,微信搜索:程序员DMZ,或者扫描下方二维码,跟着我一起认认真真学Java,踏踏实实做一个coder。

    公众号

  • 相关阅读:
    [每日一讲] Python系列:浅拷贝与深拷贝
    [每日一讲] Python系列:变量、内存管理与传递
    [每日一讲] Python系列:字典
    [每日一讲] Python系列:列表与元组
    [已开源/文章教程]独立开发 一个社交 APP 的源码/架构分享 (已上架)
    从选择到上传,可能是最贴心的高仿朋友圈编辑了
    完整的社交app源码android+laravel
    基于Laravel+Swoole开发智能家居后端
    APP架子迁移指南(三)
    Laravel如何优雅的使用Swoole
  • 原文地址:https://www.cnblogs.com/daimzh/p/13174252.html
Copyright © 2020-2023  润新知