Spring依赖注入

打印 上一主题 下一主题

主题 1809|帖子 1809|积分 5429

Spring注入方式

Spring有以下两种注入方式:

  • 手动注入
  • 主动注入
手动注入

在XML中界说Bean时, 就是手动注入,因为步伐员手动给某个属性指定了值
  1. <bean id="orderService" class="com.fanqiechaodan.service.OrderService"></bean>
  2. <bean id="userService" class="com.fanqiechaodan.service.UserService" >
  3.     <property name="orderService" ref="orderService"/>
  4. </bean>
复制代码
上面这种方式底层就是通过 set方法 举行注入,UserService里面必须要有orderService的set方法.没有会报错
  1. <bean id="orderService" class="com.fanqiechaodan.service.OrderService"></bean>
  2. <bean id="userService" class="com.fanqiechaodan.service.UserService" >
  3.     <constructor-arg index="0" ref="orderService"/>
  4. </bean>
复制代码
上面这种底层通过 构造方法 举行注入,UserService里面要有对应的构造方法,没有会报错
以是手动注入的底层也就是分为两种:

  • set方法注入
  • 构造方法注入
主动注入

主动注入分为以下两种:

  • XML的autowire主动注入
  • @Autowired注解的主动注入
XML的autowire主动注入

在XML中,可以界说一个Bean时去指定这个Bean的主动注入模式:

  • byType
  • byName
  • constructor
  • default
  • no
  1. <bean id="orderService" class="com.fanqiechaodan.service.OrderService"></bean>
  2. <bean id="userService" class="com.fanqiechaodan.service.UserService" autowire="byType"></bean>
复制代码
这么写,表现Spring会主动给UserService中所有属性主动赋值,不必要这个属性上有@Autowired注解,但必要这个属性有对应的set方法
在创建Bean的过程中,在添补属性时,Spring回去解析当前类,把当前类的所有方法都解析出来,Spring会去解析每个方法得到对应的PropertyDescriptor对象,PropertyDescriptor中有几个属性:

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

    • 如果方法名字以"get"开头,比方"getXXX",name=XXX
    • 如果方法名字以"is"开头,比方"isXXX",name=XXX
    • 如果方法名字以"set"开头,比方"setXXX",name=XXX

  • readMethodRef:表现get方法的Method
  • readMethodName:表现get方法的名字
  • writeMethodRef:表现set方法的Method
  • writeMethodName:表现set方法的名字
  • propertyTypeRef: 如果有get方法对应的就是返回值的范例,如果是set方法对应的就是set方法中唯一参数的范例
get方法的界说是:方法参数个数为0,并且方法名字以get开头或者方法名字以is开头并且方法返回值范例为boolean
set方法的界说是:方法参数个数为1,并且方法名字以set开头方法返回范例为void
以是,Spring在通过byName的主动添补属性时流程是:

  • 找到所有set方法所对应的XXX部分的名字
  • 根据名字去获取Bean
Spring在通过byType的主动添补属性时流程是:

  • 获取到set方法中唯一参数的参数范例,并且根据该范例去容器中获取Bean
  • 如果找到多个,会报错
如果是constructor,那么就可以不写set方法了,当某个Bean是通过构造方法来注入时,Spring使用构造方法参数信息从Spring容器中去找Bean,找到Bean之后作为参数传给构造方法,从而实例化得到一个Bean对象,并完成属性赋值,其实构造方法注入相称于byType+byName,平凡的byType是根据set方法中的参数范例去找Bean.找到多个会报错,而constructor就是通过构造方法中的参数范例去找Bean,如果找到多个会根据参数名确定.
别的两个:

  • no:关闭autowire
  • default:默认值,如果在<bean>设置了autowire为default,则会使用<beans>中设置的autowire
@Autowired注解的主动注入

从本质上讲,@Autowired注解提供了与autowire相同的功能,但是拥有 更细粒度的控制 和广泛的实用性
XML中的autowire控制的是整个Bean的所有属性,而@Autowired注解是直接写在某个属性,某个set方法,某个构造方法上的.如果一个类有多个构造方法,XML的autowire=constructor,无法指定用谁人构造方法,用@Autowired注解可以指定构造方法.
同时,使用@Autowired注解,还可以控制,那些属性想被主动注入,那些属性不想,这也是细粒度的控制.
但是@Autowired无法区分byType和byName,@Autowired是先byType,如果找到多个再byName
@Autowired注解可以写在:

  • 属性上(属性注入):先根据 属性范例 去找Bean,如果找到多个再根据 属性名 确定一个
  • set方法上(set方法注入):先根据方法 参数范例 去找Bean,如果找到多个再根据 参数名 确定一个
  • 构造方法上(构造方法注入): 先根据方法 参数范例 去找Bean,如果找到多个再根据 参数名 确定一个
寻找注入点

在创建一个Bean的过程中,Spring会使用org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition找到注入点并缓存,找注入点的流程为:

  • 遍历当前类的所有属性字段Field
  • 检察字段上是否存在@Autowired,@Value中的任意一个存在,则以为该字段是一个注入点
  • 如果字段是static关键字修饰的,则不举行注入
  • 获取@Autowired中required的值
  • 将字段信息构造成一个 AutowiredFieldElement 对象,作为一个 注入点 对象添加到currElements中
  • 遍历当前类所有的方法Method
  • 判定当前Method是否是 桥接方法,如果是找到原方法
  • 检察方法上是否存在@Autowired,@Value中的任意一个存在,则以为该字段是一个注入点
  • 如果方法是static关键字修饰的,则不举行注入
  • 获取@Autowired中的required属性的值
  • 将方法信息构造成一个 AutowiredMethodElement 对象,作为一个 注入点 对象天机道currElements中
  • 遍历完当前类的方法和字段后, 将 遍历父类 的,知道没有父类
  • 末了将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对应的注入点集合对象,并缓存
对应的源码实现
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
  1. @Override
  2. public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
  3.     InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
  4.     metadata.checkConfigMembers(beanDefinition);
  5. }
复制代码
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata
  1. private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
  2.     // Fall back to class name as cache key, for backwards compatibility with custom callers.
  3.     String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
  4.     // Quick check on the concurrent map first, with minimal locking.
  5.     InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
  6.     if (InjectionMetadata.needsRefresh(metadata, clazz)) {
  7.         synchronized (this.injectionMetadataCache) {
  8.             metadata = this.injectionMetadataCache.get(cacheKey);
  9.             if (InjectionMetadata.needsRefresh(metadata, clazz)) {
  10.                 if (metadata != null) {
  11.                     metadata.clear(pvs);
  12.                 }
  13.                 metadata = buildAutowiringMetadata(clazz);
  14.                 this.injectionMetadataCache.put(cacheKey, metadata);
  15.             }
  16.         }
  17.     }
  18.     return metadata;
  19. }
复制代码
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata
  1. private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
  2.     if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
  3.         return InjectionMetadata.EMPTY;
  4.     }
  5.     List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
  6.     Class<?> targetClass = clazz;
  7.     do {
  8.         final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
  9.         ReflectionUtils.doWithLocalFields(targetClass, field -> {
  10.             MergedAnnotation<?> ann = findAutowiredAnnotation(field);
  11.             if (ann != null) {
  12.                 if (Modifier.isStatic(field.getModifiers())) {
  13.                     if (logger.isInfoEnabled()) {
  14.                         logger.info("Autowired annotation is not supported on static fields: " + field);
  15.                     }
  16.                     return;
  17.                 }
  18.                 boolean required = determineRequiredStatus(ann);
  19.                 currElements.add(new AutowiredFieldElement(field, required));
  20.             }
  21.         });
  22.         ReflectionUtils.doWithLocalMethods(targetClass, method -> {
  23.             Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
  24.             if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
  25.                 return;
  26.             }
  27.             MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
  28.             if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
  29.                 if (Modifier.isStatic(method.getModifiers())) {
  30.                     if (logger.isInfoEnabled()) {
  31.                         logger.info("Autowired annotation is not supported on static methods: " + method);
  32.                     }
  33.                     return;
  34.                 }
  35.                 if (method.getParameterCount() == 0) {
  36.                     if (logger.isInfoEnabled()) {
  37.                         logger.info("Autowired annotation should only be used on methods with parameters: " +
  38.                                     method);
  39.                     }
  40.                 }
  41.                 boolean required = determineRequiredStatus(ann);
  42.                 PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
  43.                 currElements.add(new AutowiredMethodElement(method, required, pd));
  44.             }
  45.         });
  46.         elements.addAll(0, currElements);
  47.         targetClass = targetClass.getSuperclass();
  48.     }
  49.     while (targetClass != null && targetClass != Object.class);
  50.     return InjectionMetadata.forElements(elements, clazz);
  51. }
复制代码
static的字段或方法为什么不支持?
再Spring中,依赖注入重要针对对象级别的依赖关系举行管理,即在对象创建时通过构造函数,Setter方法或者字段注入等方式将依赖项注入到对象中.而静态字段属于类级别的属性,通常用于生存类级别的状态或者常量值,而不是对象之间的依赖关系.同样静态方法同样属于类级别的方法,它们可以直接通过类名调用,而不必要创建对象的实例.因此Spring的设计理念重要集中在对象之间的依赖关系管理,而对于静态字段/方法并不是其重要关注点.不支持static的字段/方法举行注入.
注入点举行注入

Spring在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties方法中,遍历所找到的注入点,依次举行注入.
字段注入


  • 遍历所有的 AutowiredFieldElement对象
  • 查找当前是否存在缓存的 cachedFieldValue,存在使用缓存,不存在解析字段
  • 将对应的字段封装成 DependencyDescriptor对象
  • 调用beanFactory.resolveDependency方法,传入 DependencyDescriptor对象,举行依赖查找,找到当前字段匹配的Bean对象
  • DependencyDescriptor对象 和所找到的 效果对象beanName 封装成一个 ShortcutDependencyDescriptor对象 作为缓存,好比如果当前Bean是原型Bean,那么下次再来创建Bean时,就可以直接拿缓存的效果对象beanName去BeanFactory中去拿bean对象了,不消再次举行查找了
  • 使用反射将效果对象赋值给字段






set方法注入


  • 遍历所有的 AutowiredMethodElement
  • 获取方法参数数组,如果缓存有就从缓存中获取,缓存没有就解析方法参数
  • 遍历方法参数,将每个参数封装成 MethodParameter对象
  • MethodParameter对象 封装成 DependencyDescriptor对象
  • 调用beanFactory.resolveDependency,传入 DependencyDescriptor对象,举行依赖查找,找到当火线法参数所匹配的Bean对象
  • DependencyDescriptor对象 和所找到的 效果对象beanName 封装成一个 ShortcutDependencyDescriptor对象 作为缓存,好比如果当前Bean是原型Bean,那么下次再来创建Bean时,就可以直接拿缓存的效果对象beanName去BeanFactory中去拿bean对象了,不消再次举行查找了
  • 使用反射将找到的所有用果对象传给当火线法,并执行









免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

tsx81428

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表