您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

Spring的注入模型

时间:2022-06-08 11:13:23  来源:  作者:程序员阿龙

一、如何手动设置注入模型

Spring的注入模型属于beanDefifinition的一个属性(默认为0),可以手动设置

xml设置

<bean id="n" class="xxxx" autowire="byType"> </bean>

 

JAVA代码设置

public class ModelBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition("bean"); //获取注入模型 
        //手动设置注入模型 
        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    }
}

二、Spring的四种注入模型

public interface AutowireCapableBeanFactory extends BeanFactory {

	/**
	 * Constant that indicates no externally defined autowiring. Note that
	 * BeanFactoryAware etc and annotation-driven injection will still be Applied.
	 * @see #createBean
	 * @see #autowire
	 * @see #autowireBeanProperties
	 */
    /**
     * 默认的装配模式,如果注入indexBean,没有在属性上加@Autowired或者@Resource,这时候,indexBean是无法注入的
     */
	int AUTOWIRE_NO = 0;

	/**
	 * Constant that indicates autowiring bean properties by name
	 * (applying to all bean property setters).
	 * @see #createBean
	 * @see #autowire
	 * @see #autowireBeanProperties
	 */
    /**
     * 通过属性名自动装配。Spring寻找与需要自动连接的属性同名的bean。
     * 例如,如果一个bean定义按名称设置为自动装配,并且它包含一个master属性(也就是说,它有一个setMaster(…)方法),
     * Spring就会查找一个名为master的bean定义,并使用它来设置属性。
     */
	int AUTOWIRE_BY_NAME = 1;

	/**
	 * Constant that indicates autowiring bean properties by type
	 * (applying to all bean property setters).
	 * @see #createBean
	 * @see #autowire
	 * @see #autowireBeanProperties
	 */
    /**
     * 如果容器中恰好存在该属性类型的一个bean,则自动连接该属性。
     * 如果存在多个,将抛出一个致命异常,这表明您不能对该bean使用byType自动装配。
     * 如果没有匹配的bean,则什么也不会发生(属性没有设置)。
     */
	int AUTOWIRE_BY_TYPE = 2;

	/**
	 * Constant that indicates autowiring the greediest constructor that
	 * can be satisfied (involves resolving the appropriate constructor).
	 * @see #createBean
	 * @see #autowire
	 */
    /**
     * 类似于byType,但适用于构造函数参数。
     * 如果容器中没有确切的构造函数参数类型的bean,则会引发致命错误。
     */
	int AUTOWIRE_CONSTRUCTOR = 3;

	/**
	 * Constant that indicates determining an appropriate autowire strategy
	 * through introspection of the bean class.
	 * @see #createBean
	 * @see #autowire
	 * @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
	 * prefer annotation-based autowiring for clearer demarcation of autowiring needs.
	 */
    /**
     * 通过bean的内省来选择合适的自动装配策略(根据英文翻译,就是spring会选择最适合装备的策略)
     */
	@Deprecated
	int AUTOWIRE_AUTODETECT = 4;
  
}

三、Autowired注解注入源码分析

注:此处只分析注入的核心流程,细节不在此展开

通过
AutowiredAnnotationBeanPostProcessor的postProcessProperties方法完成对@Autowired注解的处理和解析

Spring的注入模型

 


Spring的注入模型

 

这里最终会将所有打了Autowiring注解的字段和方法全部构建成
InjectionMetadata.InjectedElement放入一个set集合中进行处理

private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
   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<>();

           /**
            * 遍历类中的所有属性字段
            */
      ReflectionUtils.doWithLocalFields(targetClass, field -> {
          
          // 查询是否有 Autowired 注解
         MergedAnnotation<?> ann = findAutowiredAnnotation(field);
         if (ann != null) {
            if (Modifier.isStatic(field.getModifiers())) {
               if (logger.isInfoEnabled()) {
                  logger.info("Autowired annotation is not supported on static fields: " + field);
               }
               return;
            }
            
            // 判断注解中是否有 Required
            boolean required = determineRequiredStatus(ann);

            // 封装成 AutowiredFieldElement 放入 currElements 集合
            currElements.add(new AutowiredFieldElement(field, required));
         }
      });

           /**
            * 遍历类中所有方法
            */
      ReflectionUtils.doWithLocalMethods(targetClass, method -> {
         Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
         if (!BridgeMethodResolver.isVisibilityBridgeMethodPAIr(method, bridgedMethod)) {
            return;
         }
         
               // 查询是否有 Autowired 注解
         MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
         if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
            if (Modifier.isStatic(method.getModifiers())) {
               if (logger.isInfoEnabled()) {
                  logger.info("Autowired annotation is not supported on static methods: " + method);
               }
               return;
            }
            if (method.getParameterCount() == 0) {
               if (logger.isInfoEnabled()) {
                  logger.info("Autowired annotation should only be used on methods with parameters: " +
                        method);
               }
            }
            
                   // 判断注解中是否有 Required
            boolean required = determineRequiredStatus(ann);
            PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
            
                   // 封装成 AutowiredMethodElement 放入 currElements 集合
            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);
}

之后在调用
org.springframework.beans.factory.annotation.InjectionMetadata#inject方法进行属性填充

Spring的注入模型

 

其中
AutowiredAnnotationBeanPostProcessor类里面有3个内部类,其中2个类为AutowiredFieldElement和AutowiredMethodElement,这2个类封装了对打在字段上或者方法上的@Autowired注解进行处理的逻辑;

Spring的注入模型

 

我们就根据AutowiredFieldElement字段上的@Autowired注解进行分析,这个也是我们最常用的方式;

入口方法为inject方法,就是上面InjectionMetadata遍历调用的inject方法;

Spring的注入模型

 


Spring的注入模型

 

org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency

Spring的注入模型

 

org.springframework.beans.factory.support.DefaultListableBeanFactory#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();
      Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
      if (value != null) {
         if (value instanceof String) {
            String strVal = resolveEmbeddedValue((String) value);
            BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                  getMergedBeanDefinition(beanName) : null);
            value = evaluateBeanDefinitionString(strVal, bd);
         }
         TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
         try {
            return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
         }
         catch (UnsupportedOperationException ex) {
            // A custom TypeConverter which does not support TypeDescriptor resolution...
            return (descriptor.getField() != null ?
                  converter.convertIfNecessary(value, type, descriptor.getField()) :
                  converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
         }
      }

      /**
            * 解析当前依赖是否支持多个bean注入,如:list、set、map类型等
            * 如果支持多个bean注入,则在内部就完成bean的查找,不为空直接返回
            */
      Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
      if (multipleBeans != null) {
         return multipleBeans;
      }

           /**
            * 根据类型查询bean实例,可能会有多个,key为需要注入对象的名字
            */
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      if (matchingBeans.isEmpty()) {
          // 判断在依赖上面是否加了必须的条件,默认为true
         if (isRequired(descriptor)) {
             // 抛出异常
            raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
         }
         // 如果非必须则返回null,不注入任何对象
         return null;
      }

      String autowiredBeanName;
      Object instanceCandidate;

      // 如果找出的实例大于1个
      if (matchingBeans.size() > 1) {

               // 通过依赖描述组件来推断需要注入对象的名字
         autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
         if (autowiredBeanName == null) {
            if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
               return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
            }
            else {
               // In case of an optional Collection/Map, silently ignore a non-unique case:
               // possibly it was meant to be an empty collection of multiple regular beans
               // (before 4.3 in particular when we didn't even look for collection beans).
               return null;
            }
         }
         // 通过名字获取对象
         instanceCandidate = matchingBeans.get(autowiredBeanName);
      }
      else {
         // We have exactly one match.
               // 如果找出来的实例为1个,则直接获取出来,赋值给instanceCandidate
         Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
         autowiredBeanName = entry.getKey();
         instanceCandidate = entry.getValue();
      }

      if (autowiredBeanNames != null) {
         autowiredBeanNames.add(autowiredBeanName);
      }
      // 判断是否为Class,如果是则说明没有实例化
      if (instanceCandidate instanceof Class) {
          // 实例化
         instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
      }
      // 将获取到的实例候选赋值给result
      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);
   }
}

四、Autowired分析总结

先根据类型查找bean;如果没有找到则报错(默认情况下@Autowired是一定需要注入一个bean的);如果查找到一个则用找到的这一个完成注入;如果查找到多个,先把这个多个放到map当中,继而根据属性的名字从map当中去获取;能获取到则使用获取的这个;如果map当中通过名字还是无法获取则报错;

@Autowired不会改变bean的注入模型(默认情况下bean的注入模型还是0);@Autowired算是一种半自动注入;因为他只需要程序员告诉spring需要注入的属性或者方法,而不需要程序员告诉spring需要注入的属性或者方法他的值到底是哪个bean;@Autowired会根据自己的规则去查找这个bean,所以只能算作半自动注入;

五、Resource注解注入源码分析

通过
CommonAnnotationBeanPostProcessor这个类的postProcessorProperties方

法来完成对@Resource注解的解析和处理

Spring的注入模型

 

后续的流程和Autowired差不多,就是扫描出来类所有加了Resource注解的属性,封装成对应的
InjectionMetadata.InjectedElement

Spring的注入模型

 


Spring的注入模型

 

在封装成ResourceElement时会读取注解中的值进行参数的赋值

Spring的注入模型

 

在调用ResourceElement的inject方法进行属性注入,但我们发现ResourceElement没有inject方法,我们就可以猜想出,这个方法肯定继承至父类

Spring的注入模型

 

我们最终在
InjectionMetadata.InjectedElement中找到了inject方法

Spring的注入模型

 

在调用子类ResourceElement的getResourceToInject方法

Spring的注入模型

 


Spring的注入模型

 

判断Resource是否自定义了名字,如果自定义了,则不会走类型查询并注入,而是会走else因为自定义了名字说明我们想根据名字注入对象;

如果没有自定义会先根据名字判断是否在单例池中存在或者在beanDefinitionMap中存在,存在则通过名字获取,没有则在通过类型去获取,获取到一个则进行注入,没有获取到或者获取到多个则抛异常;

Spring的注入模型

 


Spring的注入模型

 

六、Resource分析总结

 

@Resource在没有配置name的情况下首先根据名字查找;

如果名字能查找到则返回这个查找到的(spring容器的原则是name唯一的,所以不存在通过名字能查找到多个的情况);

如果通过名字查找不到(需要注意的是这里的前提是没有配置name的情况,spring觉得名字无所谓);因为对名字无要求,所以会再根据类型查找;那么走的就是@Autowired这一套;

如果配置了名字,spring觉得对名字有严格要求,所以只能根据你配置的名字查找;如果查找不到则报错,找到了则用;



Tags:Spring   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  Search: Spring  点击:(61)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  Search: Spring  点击:(41)  评论:(0)  加入收藏
详解基于SpringBoot的WebSocket应用开发
在现代Web应用中,实时交互和数据推送的需求日益增长。WebSocket协议作为一种全双工通信协议,允许服务端与客户端之间建立持久性的连接,实现实时、双向的数据传输,极大地提升了用...【详细内容】
2024-01-30  Search: Spring  点击:(23)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  Search: Spring  点击:(94)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19  Search: Spring  点击:(90)  评论:(0)  加入收藏
Spring Boot2.0深度实践 核心原理拆解+源码分析
Spring Boot2.0深度实践:核心原理拆解与源码分析一、引言Spring Boot是一个基于Java的轻量级框架,它简化了Spring应用程序的创建过程,使得开发者能够快速搭建一个可运行的应用...【详细内容】
2024-01-15  Search: Spring  点击:(99)  评论:(0)  加入收藏
SpringBoot3+Vue3 开发高并发秒杀抢购系统
开发高并发秒杀抢购系统:使用SpringBoot3+Vue3的实践之旅随着互联网技术的发展,电商行业对秒杀抢购系统的需求越来越高。为了满足这种高并发、高流量的场景,我们决定使用Spring...【详细内容】
2024-01-14  Search: Spring  点击:(93)  评论:(0)  加入收藏
Spring Boot 3.0是什么?
Spring Boot 3.0是一款基于Java的开源框架,用于简化Spring应用程序的构建和开发过程。与之前的版本相比,Spring Boot 3.0在多个方面进行了改进和增强,使其更加易用、高效和灵活...【详细内容】
2024-01-11  Search: Spring  点击:(140)  评论:(0)  加入收藏
GraalVM与Spring Boot 3.0:加速应用性能的完美融合
在2023年,SpringBoot3.0的发布标志着Spring框架对GraalVM的全面支持,这一支持是对Spring技术栈的重要补充。GraalVM是一个高性能的多语言虚拟机,它提供了Ahead-of-Time(AOT)编...【详细内容】
2024-01-11  Search: Spring  点击:(135)  评论:(0)  加入收藏
Spring Boot虚拟线程的性能还不如Webflux?
早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章,觉得还不错。内容较长,抓重点给大家介绍一下这篇文章的核心内容,方便大家快速阅读。测试场景作者采用了一个尽可...【详细内容】
2024-01-10  Search: Spring  点击:(131)  评论:(0)  加入收藏
▌简易百科推荐
Qt与Flutter:在跨平台UI框架中哪个更受欢迎?
在跨平台UI框架领域,Qt和Flutter是两个备受瞩目的选择。它们各自具有独特的优势,也各自有着广泛的应用场景。本文将对Qt和Flutter进行详细的比较,以探讨在跨平台UI框架中哪个更...【详细内容】
2024-04-12  刘长伟    Tags:UI框架   点击:(7)  评论:(0)  加入收藏
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  京东云开发者    Tags:Web Components   点击:(11)  评论:(0)  加入收藏
Kubernetes 集群 CPU 使用率只有 13% :这下大家该知道如何省钱了
作者 | THE STACK译者 | 刘雅梦策划 | Tina根据 CAST AI 对 4000 个 Kubernetes 集群的分析,Kubernetes 集群通常只使用 13% 的 CPU 和平均 20% 的内存,这表明存在严重的过度...【详细内容】
2024-03-08  InfoQ    Tags:Kubernetes   点击:(23)  评论:(0)  加入收藏
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  风舞凋零叶    Tags:Spring Security   点击:(61)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  贝格前端工场    Tags:框架   点击:(51)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  程序员wayn  微信公众号  Tags:Spring   点击:(41)  评论:(0)  加入收藏
开发者的Kubernetes懒人指南
你可以将本文作为开发者快速了解 Kubernetes 的指南。从基础知识到更高级的主题,如 Helm Chart,以及所有这些如何影响你作为开发者。译自Kubernetes for Lazy Developers。作...【详细内容】
2024-02-01  云云众生s  微信公众号  Tags:Kubernetes   点击:(58)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  大噬元兽  微信公众号  Tags:框架   点击:(71)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  HELLO程序员  微信公众号  Tags:Spring   点击:(94)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19   Java中文社群  微信公众号  Tags:SpringBoot   点击:(90)  评论:(0)  加入收藏
站内最新
站内热门
站内头条