动注入

手动注入就是在XML中定义Bean时,给Bean的某个属性指定了值。

set方法注入

<bean name="orderService" class="com.warrior.service.OrderService" />
<bean name="userService" class="com.warrior.service.UserService"><property name="orderService" ref="orderService"/>
</bean>

构造方法注入

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

自动注入

XML的autowire自动注入

在XML中,可以在定义一个Bean时去指定这个Bean的自动注入模式,主要包含以下自动注入模式:

  • byType

  • byName

  • constructor

  • default

  • no

<bean id="userService" class="com.warrior.service.UserService" autowire="byType"/>

这么写,Spring会给userService中的所有属性自动赋值,不需要在这个属性上有@Autowired注解,但需要这个属性有对应的set方法

Spring源码实现位于AbstractAutowireCapableBeanFactory#populateBean()方法中:

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;}//......// 如果当前Bean中的BeanDefinition中设置了PropertyValues,那么最终将是PropertyValues中的值,覆盖@Autowiredif (pvs != null) {applyPropertyValues(beanName, mbd, bw, pvs);}
}
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);if (logger.isTraceEnabled()) {logger.trace("Added autowiring by name from bean name '" + beanName +"' via property '" + propertyName + "' to bean named '" + propertyName + "'");}}else {if (logger.isTraceEnabled()) {logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +"' by name: no matching bean found");}}}
}
protected void autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {TypeConverter converter = getCustomTypeConverter();if (converter == null) {converter = bw;}Set<String> autowiredBeanNames = new LinkedHashSet<>(4);// 当前Bean中能进行自动注入的属性名String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);for (String propertyName : propertyNames) {try {PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);// Don't try autowiring by type for type Object: never makes sense,// even if it technically is a unsatisfied, non-simple property.if (Object.class != pd.getPropertyType()) {MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);// Do not allow eager init for type matching in case of a prioritized post-processor.// eager表示立即初始化,表示在根据类型查找Bean时,允不允许进行Bean的创建,如果当前bean实现了PriorityOrdered,那么则不允许// 为什么不允许,因为我自己是PriorityOrdered,是优先级最高的,不能有比我创建得更早的boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);// 根据类型找到的结果Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);if (autowiredArgument != null) {pvs.add(propertyName, autowiredArgument);}for (String autowiredBeanName : autowiredBeanNames) {registerDependentBean(autowiredBeanName, beanName);if (logger.isTraceEnabled()) {logger.trace("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);}}
}
protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {Set<String> result = new TreeSet<>();PropertyValues pvs = mbd.getPropertyValues();PropertyDescriptor[] pds = bw.getPropertyDescriptors();// 什么样的属性能进行自动注入?// 1.该属性有对应的set方法// 2.没有在ignoredDependencyTypes中// 3.如果该属性对应的set方法是实现的某个接口中所定义的,那么接口没有在ignoredDependencyInterfaces中// 4.属性类型不是简单类型,比如int、Integer、int[]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);
}

Spring底层在创建Bean的过程中,在填充属性时,会去解析当前类,把当前类的所有方法都解析出来,Spring会去解析每个方法得到对应的PropertyDescriptor对象,PropertyDescriptor中有几个属性:

  • name:这个name并不是方法的名字,而是拿方法名字进行处理后的名字

1、如果方法名字以“get”开头,比如getXXX,那么name=xxx

2、如果方法名字以"is"开头,比如isXXX,那么name=xxx

3、如果方法名字以"set"开头,比如setXXX,那么name=xxx

  • readMethodRef:表示get方法的Method对象引用

  • readMethodName:表示get方法的名字

  • writeMethodRef:表示set方法的Method对象的引用

  • writeMethodName:表示set方法的名字

  • propertyTypeRef:如果有get方法对应的就是返回值的类型,如果是set方法那么对应的就是set方法中唯一参数的类型。

Spring 在通过byName的自动填充属性流程:
找到所有set方法所对应的xxx部分的名字
根据xxx部分名字去获取bean
Spring 在通过byType的自动填充属性流程:
获取到set方法中的唯一参数的参数类型,并且根据该类型去容器中获取bean
如果找到多个,会报错
Spring 通过constructor构造方法来注入,就可以不写set方法,Spring利用构造方法的参数信息从Spring容器中去找bean,找到bean之后作为参数传给构造方法,从而实例化得到一个bean对象,并完成属性赋值。

@Autowired注解的自动注入

@Autowired注解注入对象属性无法区分byType和byName,@Autowired先byType,如果找到了多个则byName。@Autowired注解可以写在:

  • 属性上:先根据属性类型去找Bean,如果找到多个在根据属性名确定一个。

  • 构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个。

  • set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个。

寻找注入点

在创建一个Bean的过程中,Spring会利用AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition()找出注入点并缓存,查找注入点的流程:

  • 遍历当前类的所有属性字段Field

  • 查看字段上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该字段是一个注入点

  • 如果字段是static的,则不进行注入

  • 获取@Autowired中的required属性的值

  • 将字段信息构造成一个AutowiredFieldElement对象,作为一个注入点对象添加到currElements集合中。

  • 遍历当前类的所有方法Method

  • 判断当前Method是否是桥接方法,如果是找到原方法

  • 查看方法上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该方法是一个注入点

  • 如果方法是static的,则不进行注入

  • 获取@Autowired中的required属性的值

  • 将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到currElements集合中。

  • 遍历完当前类的字段和方法后,将遍历父类的,直到没有父类。

  • 最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对于的注入点集合对象,并缓存(Map<String, InjectionMetadata> injectionMetadataCache)。

注入点进行注入

Spring在AutowiredAnnotationBeanPostProcessor#postProcessProperties()方法中,会遍历所有的注入点依次进行注入。

InjectionMetadata#inject()方法中进行注入点注入

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> elementsToIterate =(checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {// 遍历每个注入点进行依赖注入for (InjectedElement element : elementsToIterate) {element.inject(target, beanName, pvs);}}
}
字段注入

字段注入流程:

  • 遍历所有的AuutowireFieldElement对象。

  • 将对应的字段封装为DependencyDescriptor对象。

  • 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前字段所匹配的Bean对象。

  • 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去拿bean对象了,不用再次进行查找了。

  • 利用反射将结果对象赋值给字段。

Spring底层实现字段注入源码入口位于AutowiredFieldElement#inject()方法中

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-resolvevalue = resolveFieldValue(field, bean, beanName);}}else {// 根据filed从BeanFactory中查到的匹配的Bean对象value = resolveFieldValue(field, bean, beanName);}// 反射给filed赋值if (value != null) {ReflectionUtils.makeAccessible(field);field.set(bean, value);}
}
set方法注入

set方法注入流程:

  • 遍历所有的AutowiredMethodElement对象。

  • 遍历将对应的方法的参数,将每个参数封装成MethodParameter对象。

  • 将MethodParameter对象封装为DependencyDescriptor对象。

  • 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前方法参数所匹配的Bean对象。

  • 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了,不用再次进行查找了。

  • 利用反射将找到的所有结果对象传给当前方法,并执行。

Spring底层实现字段注入源码入口位于AutowiredMethodElement#inject()方法中

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-resolvearguments = resolveMethodArguments(method, bean, beanName);}}else {arguments = resolveMethodArguments(method, bean, beanName);}if (arguments != null) {try {ReflectionUtils.makeAccessible(method);method.invoke(bean, arguments);}catch (InvocationTargetException ex) {throw ex.getTargetException();}}
}

实现源码

Spring源码实现位于AbstractAutowireCapableBeanFactory#populateBean()方法中

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;}}if (needsDepCheck) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}checkDependencies(beanName, mbd, filteredPds, pvs);}// 如果当前Bean中的BeanDefinition中设置了PropertyValues,那么最终将是PropertyValues中的值,覆盖@Autowiredif (pvs != null) {applyPropertyValues(beanName, mbd, bw, pvs);}
}AutowiredAnnotationBeanPostProcessor#postProcessProperties()
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);}catch (BeanCreationException ex) {throw ex;}catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);}return pvs;
}

找注入点,所有被@Autowired注解了的Field或Method

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;
}
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {// 如果一个Bean的类型是String...,那么则根本不需要进行依赖注入if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {return InjectionMetadata.EMPTY;}List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;do {final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();// 遍历targetClass中的所有FieldReflectionUtils.doWithLocalFields(targetClass, field -> {// field上是否存在@Autowired、@Value、@Inject中的其中一个MergedAnnotation<?> ann = findAutowiredAnnotation(field);if (ann != null) {// static filed不是注入点,不会进行自动注入if (Modifier.isStatic(field.getModifiers())) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation is not supported on static fields: " + field);}return;}// 构造注入点boolean required = determineRequiredStatus(ann);currElements.add(new AutowiredFieldElement(field, required));}});// 遍历targetClass中的所有MethodReflectionUtils.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())) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation is not supported on static methods: " + method);}return;}// set方法最好有入参if (method.getParameterCount() == 0) {if (logger.isInfoEnabled()) {logger.info("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));}});elements.addAll(0, currElements);targetClass = targetClass.getSuperclass();}while (targetClass != null && targetClass != Object.class);return InjectionMetadata.forElements(elements, clazz);
}

Spring Bean 依赖注入相关推荐

  1. Spring bean依赖注入、bean的装配及相关注解

    依赖注入 Spring主要提供以下两种方法用于依赖注入 基于属性Setter方法注入 基于构造方法注入 Setter方法注入 例子: public class Communication {priva ...

  2. 零配置 之 Spring 注解实现Bean依赖注入

    转载自  [第十二章]零配置 之 12.2 注解实现Bean依赖注入 --跟我学spring3 12.2  注解实现Bean依赖注入 12.2.1  概述 注解实现Bean配置主要用来进行如依赖注入. ...

  3. spring bean依赖_Spring @Configuration并将bean依赖项作为方法参数注入

    spring bean依赖 一个春天建议注射豆从Spring的参考指南复制下面的示例中显示之间的相互依存关系的方式在这里 : @Configuration public class AppConfig ...

  4. factorybean 代理类不能按照类型注入_《Spring入门经典》:使用Spring进行依赖注入

    第二章:使用Spring进行依赖注入 重点:配置并使用Spring容器 使用不同类型的配置元数据来配置Spring容器 理解依赖解析 了解自动装配的优缺点 在容器中执行显式Bean查找 学习不同的Be ...

  5. Spring Setter依赖注入示例

    学习如何编写Spring Setter依赖注入示例 . Setter注入是Spring依赖注入的一种 . Spring支持字段注入,Setter注入以及构造函数注入,以将依赖项注入Spring托管的b ...

  6. Spring字段依赖注入示例

    学习如何编写Spring Field Injection示例 . 字段注入是Spring框架 依赖注入的一种 . 在本教程中,我们将编写几个类,并看一看现场注入工程. 有关Spring依赖注入的更多信 ...

  7. 据说,80%的人没有真正理解了Spring的依赖注入

    前言 提起Spring,大家肯定不陌生,它是每一个Java开发者绕不过去的坎.Spring 框架为基于 java 的企业应用程序提供了一整套解决方案,方便开发人员在框架基础快速进行业务开发. 在官网中 ...

  8. Spring框架----Spring的依赖注入

    1.spring的依赖注入的概念 依赖注入:dependency Injection IOC的作用:降低程序之间的依赖关系,但不是消除. 依赖关系的管理:以后都交给了spring来维护 在当前类中需要 ...

  9. spring(一)依赖注入与 SPEL

    Spring之依赖注入与 SPEL 一.控制反转与依赖注入 二.helloworld 三.构造注入 四.级联注入 五.单例与多例 六.工厂方法创建 Bean 七.包扫描管理 bean 八.SPEL与资 ...

最新文章

  1. 高可用 Prometheus 架构实践中的踩坑集锦
  2. 高效sql性能优化极简教程
  3. 02-15 GUCCI 我喜欢的
  4. Hibernate的事务管理
  5. 整理vim格式回车变成空两格
  6. 有商在线进销存成功案例
  7. python软件是什么作用,python-dotenv的用途是什么?
  8. solr的索引库配置
  9. vscode字体图标
  10. 海康sip服务器地址协议,海康摄像头的GB28181的sip服务器+ZL流媒体+前台测试页面...
  11. 傅里叶级数 画初音 附底层代码
  12. “水晶糖果字体”练习
  13. html中的embed标签属性,html中Embed标签的语法和属性设置
  14. iOS开发UI篇 -- UINagivationController
  15. 【正则表达式】正则表达式在线工具怎么使用?怎么测试自己写的正则表达式是否正确?
  16. 亚马逊经济:从仓库到庞大的零售帝国
  17. mysql和pg数据库表备份及还原
  18. python安装第三方包遇到的一些问题
  19. shell脚本 sh shebang “#!/bin/sh“
  20. iosepub阅读器_iOS苹果手机上最好的3个mobi阅读器

热门文章

  1. 构建iOS持续集成平台(二)——测试框架
  2. 日语助词よ的语法汇总,请牢记
  3. python中魔法方法repr_python中魔法方法__str__与__repr__的区别
  4. Android开发学习Part3
  5. 【毕业设计_课程设计】基于Spark网易云音乐数据分析
  6. 位运算概览与奇巧淫技
  7. PyQt5 组件之QTableView锁定首列
  8. 企业如何进行数据质量管理
  9. 基于Eureka实现服务注册中心
  10. 上海闵行做网站,闵行网站建设-品划告诉您如何选择域名