【小家Spring】细说Spring IOC容器的自动装配(@Autowired),以及Spring4.0新特性之【泛型依赖注入】的源码级解析(上)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: 【小家Spring】细说Spring IOC容器的自动装配(@Autowired),以及Spring4.0新特性之【泛型依赖注入】的源码级解析(上)

前言


前面我发布了Spring IOC容器的刷新(初始化)过程,以及Spring 容器的Bean的实例化、初始化过程。其中有一个步骤小伙伴们比较关心,也提问的比较多,那就是泛型依赖注入。鉴于之前对这一块描述得也不是很详细,鉴于此处还是比较重要的,因此本文专门用篇幅聊聊这个事


看本篇文章之前,建议至少已经了解Spring容器的一个大致过程,最好已经看过博文(或者知道):

【小家Spring】AbstractAutowireCapableBeanFactory#populateBean实现Bean的属性赋值和initializeBean对Bean的初始化


因此本文将直接从依赖注入这一步,分析Spring是怎么样实现控制反转、依赖注入(DI)的~

Demo Show(自动装配的)


在讲解之前,先构造一个例子看看效果(效果很像RedisTemplate):


// 准备一个带泛型的Bean
@Getter
@Setter
@NoArgsConstructor  
@AllArgsConstructor
@ToString
public class GenericBean<T, W> {
    private T t;
    private W w;
}
// config配置文件中注入两个泛型Bean
    @Bean
    public Parent parentOne() {
        return new Parent();
    }
    @Bean
    public Parent parentTwo() {
        return new Parent();
    }
    @Bean
    public GenericBean<String, String> stringGeneric() {
        return new GenericBean<String, String>("str1", "str2");
    }
    @Bean
    public GenericBean<Object, Object> objectGeneric() {
        return new GenericBean<Object, Object>("obj1", 2);
    }
// 使用@Autowired注入,测试一下:
    @Autowired
    private GenericBean<Object, Object> objectGenericBean; //GenericBean(t=obj1, w=2)
    @Autowired
    private GenericBean<String, String> stringGenericBean; //GenericBean(t=st   r1, w=str2)
    // 注意,容器里虽然有两个Parent,这里即使不使用@Qualifier也不会报错。
    // 但是需要注意字段名parentOne,必须是容器里存在的,否则就报错了。
    @Autowired
    private Parent parentOne; //com.fsx.bean.Parent@23c98163
    //Spring4.0后的新特性,这样会注入所有类型为(包括子类)GenericBean的Bean(但是顺序是不确定的,可通过Order接口控制顺序)
    @Autowired
    private List<GenericBean> genericBeans; //[GenericBean(t=st   r1, w=str2), GenericBean(t=obj1, w=2)]
    // 这里的key必须是String类型,把GenericBean类型的都拿出来了,beanName->Bean
    @Autowired
    private Map<String, GenericBean> genericBeanMap; //{stringGenericBean=GenericBean(t=st   r1, w=str2), objectGenericBean=GenericBean(t=obj1, w=2)}
    // 这里面,用上泛型也是好使的,就只会拿指定泛型的了
    @Autowired
    private Map<String, GenericBean<Object, Object>> genericBeanObjMap; //{objectGenericBean=GenericBean(t=obj1, w=2)}
    // 普通类型,容器里面没有的Bean类型,注入是会报错的
    //@Autowired
    //private Integer normerValue;


如果你还不知道泛型依赖注入的话,就应该有这样一个疑问(应该有哈,若没有,看来你对@Autowired还没有个概性的认识):

@Autowired明明是根据类型进行注入的,那我们往容器里放置了两个GenericBean类型的Bean,为何启动没有报错呢???

(最直观的感受:若我们的service有两个serviceImpl,直接仅使用@Autowired注入是会报错的)


那么接下来,我们通过跟踪源码的方式,一层一层剥开内部的缘由,看看葫芦里到底是什么药~

依赖注入源码分析


在上面推荐的博文里已经讲到了,Spring在populateBean这一步为属性赋值的时候,会执行InstantiationAwareBeanPostProcessor处理器的postProcessPropertyValues方法。


这里AutowiredAnnotationBeanPostProcessor该处理器的postProcessPropertyValues方法就是来处理该注解的。(因此,我们的源码解析从此处开始吧~~~~)


  @Override
  public PropertyValues postProcessPropertyValues(
      PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
    // 找到所有的@Autowired元数据-------当前值可以见下图
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
      // 所以我们看InjectionMetadata 的inject即可,见下
      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;
  }


metadata值如下:


image.png


InjectionMetadata#inject


  public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Collection<InjectedElement> checkedElements = this.checkedElements;
    // 这里是待遍历、待处理的属性Element们(备注,本处的InjectedElement实现类为:AutowiredFieldElement 因为我们是Field注入嘛)
    // 所以从下可知,我们直接看AutowiredFieldElement#inject方法吧
    Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements);
    if (!elementsToIterate.isEmpty()) {
      boolean debug = logger.isDebugEnabled();
      for (InjectedElement element : elementsToIterate) {
        if (debug) {
          logger.debug("Processing injected element of bean '" + beanName + "': " + element);
        }
        element.inject(target, beanName, pvs);
      }
    }
  }


AutowiredFieldElement#inject:根据字段注入

AutowiredFieldElement是AutowiredAnnotationBeanPostProcessor的一个私有普

通内部类,高内聚~

    // 这段代码虽然长,其实核心逻辑还并不在这里,而是在beanFactory Bean工厂的resolveDependency处理依赖实现里
    @Override
    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 {
        // 把field和required属性,包装成desc描述类
        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 {
          // 把desc传进去,里面还有注解信息、元信息等等,这个方法是根据注解信息寻找到依赖的Bean的核心逻辑
          // 备注:此部分处理依赖逻辑交给Bean工厂,其实也没毛病。毕竟它处理的相当于是Field
          // 那么接下里,重点分析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;
              registerDependentBeans(beanName, autowiredBeanNames);
              if (autowiredBeanNames.size() == 1) {
                String autowiredBeanName = autowiredBeanNames.iterator().next();
                if (beanFactory.containsBean(autowiredBeanName) &&
                    beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                  this.cachedFieldValue = new ShortcutDependencyDescriptor(
                      desc, autowiredBeanName, field.getType());
                }
              }
            }
            else {
              this.cachedFieldValue = null;
            }
            this.cached = true;
          }
        }
      }
      if (value != null) {
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
      }
    }
  }


DefaultListableBeanFactory#resolveDependency:解决依赖(根据依赖关系找到值)

  @Override
  @Nullable
  public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    // 把当前Bean工厂的名字发现器赋值给传进来DependencyDescriptor 类
    // 这里面注意了:有必要说说名字发现器这个东西,具体看下面吧==========还是比较重要的
    // Bean工厂的默认值为:private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    // 支持到Optional类型的注入,比如我们这样注入:private Optional<GenericBean<Object, Object>> objectGenericBean;
    // 也是能够注入进来的,只是类型变为,Optional[GenericBean(t=obj1, w=2)]
    // 对于Java8中Optional类的处理
    if (Optional.class == descriptor.getDependencyType()) {
      return createOptionalDependency(descriptor, requestingBeanName);
    }
    // 兼容ObjectFactory和ObjectProvider(Spring4.3提供的接口)
    // 关于ObjectFactory和ObjectProvider在依赖注入中的大作用,我觉得是非常有必要再撰文讲解的
    //对于前面讲到的提早曝光的ObjectFactory的特殊处理
    else if (ObjectFactory.class == descriptor.getDependencyType() ||
        ObjectProvider.class == descriptor.getDependencyType()) {
      return new DependencyObjectProvider(descriptor, requestingBeanName);
    }
    // 支持到了javax.inject.Provider这个类的实现
    else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
      return new Jsr330ProviderFactory().createDependencyProvider(descriptor, requestingBeanName);
    }
    // 这个应该是我们觉得部分触及到的,其实不管何种方式,最终都是交给doResolveDependency方法去处理了
    else {
      //getAutowireCandidateResolver()得到ContextAnnotationAutowireCandidateResolver 根据依赖注解信息,找到对应的Bean值信息
      //getLazyResolutionProxyIfNecessary方法,它也是唯一实现。
      //如果字段上带有@Lazy注解,表示进行懒加载 Spring不会立即创建注入属性的实例,而是生成代理对象,来代替实例
      Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
          descriptor, requestingBeanName);
      // 如果在@Autowired上面还有个注解@Lazy,那就是懒加载的,是另外一种处理方式(是一门学问)
      // 这里如果不是懒加载的(绝大部分情况都走这里) 就进入核心方法doResolveDependency 下面有分解
      if (result == null) {
        result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
      }
      return result;
    }
  }


DefaultListableBeanFactory#doResolveDependency 处理属性依赖关系的核心方法:通用的处理逻辑


  @Nullable
  public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    // 相当于打个点,记录下当前的步骤位置  返回值为当前的InjectionPoint 
    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
      // 简单的说就是去Bean工厂的缓存里去看看,有没有名称为此的Bean,有就直接返回,没必要继续往下走了
      // 比如此处的beanName为:objectGenericBean等等
      Object shortcut = descriptor.resolveShortcut(this);
      if (shortcut != null) {
        return shortcut;
      }
      // 此处为:class com.fsx.bean.GenericBean
      Class<?> type = descriptor.getDependencyType();
      // 看看ContextAnnotationAutowireCandidateResolver的getSuggestedValue方法,具体实现在父类 QualifierAnnotationAutowireCandidateResolver中
      //处理@Value注解-------------------------------------
      //获取@Value中的value属性
      Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
      // 若存在value值,那就去解析它。使用到了AbstractBeanFactory#resolveEmbeddedValue
      // 也就是使用StringValueResolver处理器去处理一些表达式~~
      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());
        return (descriptor.getField() != null ?
            converter.convertIfNecessary(value, type, descriptor.getField()) :
            converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
      }
      //对数组、Collection、Map等类型进行处理,也是支持自动注入的。
      //因为是数组或容器,Sprng可以直接把符合类型的bean都注入到数组或容器中,处理逻辑是:
      //1.确定容器或数组的组件类型 if else 分别对待,分别处理
      //2.调用findAutowireCandidates(核心方法)方法,获取与组件类型匹配的Map(beanName -> bean实例)
      //3.将符合beanNames添加到autowiredBeanNames中
      Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
      if (multipleBeans != null) {
        return multipleBeans;
      }
      // 获取所有【类型】匹配的Beans,形成一个Map(此处用Map装,是因为可能不止一个符合条件)
      // 该方法就特别重要了,对泛型类型的匹配、对@Qualifierd的解析都在这里面,下面详情分解
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      // 若没有符合条件的Bean。。。
      if (matchingBeans.isEmpty()) {
          // 并且是必须的,那就抛出没有找到合适的Bean的异常吧
          // 我们非常熟悉的异常信息:expected at least 1 bean which qualifies as autowire candidate...
        if (isRequired(descriptor)) {
          raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
        }
        return null;
      }
      String autowiredBeanName;
      Object instanceCandidate;
      //如果类型匹配的bean不止一个,Spring需要进行筛选,筛选失败的话继续抛出异常
      // 如果只找到一个该类型的,就不用进这里面来帮忙筛选了~~~~~~~~~
      if (matchingBeans.size() > 1) {
        // 该方法作用:从给定的beans里面筛选出一个符合条件的bean,此筛选步骤还是比较重要的,因此看看可以看看下文解释吧
        autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
        if (autowiredBeanName == null) {
          // 如果此Bean是要求的,或者 不是Array、Collection、Map等类型,那就抛出异常NoUniqueBeanDefinitionException
          if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
            // 抛出此异常
            return descriptor.resolveNotUnique(type, matchingBeans);
          }
          // Spring4.3之后才有:表示如果是required=false,或者就是List Map类型之类的,即使没有找到Bean,也让它不抱错,因为最多注入的是空集合嘛
          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.
        // 仅仅只匹配上一个,走这里 很简单  直接拿出来即可
        // 注意这里直接拿出来的技巧:不用遍历,直接用iterator.next()即可
        Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
        autowiredBeanName = entry.getKey();
        instanceCandidate = entry.getValue();
      }
      // 把找到的autowiredBeanName 放进去
      if (autowiredBeanNames != null) {
        autowiredBeanNames.add(autowiredBeanName);
      }
      // 底层就是调用了beanFactory.getBean(beanName);  确保该实例肯定已经被实例化了的
      if (instanceCandidate instanceof Class) {
        instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
      }
      Object result = instanceCandidate;
      if (result instanceof NullBean) {
        if (isRequired(descriptor)) {
          raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
        }
        result = null;
      }
      // 再一次校验,type和result的type类型是否吻合=====
      if (!ClassUtils.isAssignableValue(type, result)) {
        throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
      }
      return result;
    }
    // 最终把节点归还回来
    finally {
      ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
    }
  }


若出现多个Bean,将由下面方法去匹配和决定~


//determineAutowireCandidate 从多个Bean中,筛选出一个符合条件的Bean
  @Nullable
  protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
    Class<?> requiredType = descriptor.getDependencyType();
    // 看看传入的Bean中有没有标注了@Primary注解的
    String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
    // 如果找到了 就直接返回
    // 由此可见,@Primary的优先级还是非常的高的
    if (primaryCandidate != null) {
      return primaryCandidate;
    }
    //找到一个标注了javax.annotation.Priority注解的。(备注:优先级的值不能有相同的,否则报错)
    String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
    if (priorityCandidate != null) { 
      return priorityCandidate;
    }
    // Fallback
    // 这里是最终的处理(相信绝大部分情况下,都会走这里~~~~~~~~~~~~~~~~~~~~)
    // 此处就能看出resolvableDependencies它的效能了,他会把解析过的依赖们缓存起来,不用再重复解析了
    for (Map.Entry<String, Object> entry : candidates.entrySet()) {
      String candidateName = entry.getKey();
      Object beanInstance = entry.getValue();
      // 到这一步就比较简单了,matchesBeanName匹配上Map的key就行。
      // 需要注意的是,bean可能存在很多别名,所以只要有一个别名相同,就认为是能够匹配上的  具体参考AbstractBeanFactory#getAliases方法
      //descriptor.getDependencyName() 这个特别需要注意的是:如果是字段,这里调用的this.field.getName() 直接用的是字段的名称
      // 因此此处我们看到的情况是,我们采用@Autowired虽然匹配到两个类型的Bean了,即使我们没有使用@Qualifier注解,也会根据字段名找到一个合适的(若没找到,就抱错了)
      if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
          matchesBeanName(candidateName, descriptor.getDependencyName())) {
        return candidateName;
      }
    }
    return null;
  }
//determinePrimaryCandidate:顾名思义。它是从给定的Bean中看有木有标注了@Primary注解的Bean,有限选择它
  @Nullable
  protected String determinePrimaryCandidate(Map<String, Object> candidates, Class<?> requiredType) {
    String primaryBeanName = null;
    for (Map.Entry<String, Object> entry : candidates.entrySet()) {
      String candidateBeanName = entry.getKey();
      Object beanInstance = entry.getValue();
      // isPrimary就是去看看容器里(包含父容器)对应的Bean定义信息是否有@Primary标注
      if (isPrimary(candidateBeanName, beanInstance)) {
        if (primaryBeanName != null) {
          boolean candidateLocal = containsBeanDefinition(candidateBeanName);
          boolean primaryLocal = containsBeanDefinition(primaryBeanName);
          // 这个相当于如果已经找到了一个@Primary的,然后又找到了一个 那就抛出异常
          // @Primary只能标注到一个同类型的Bean上
          if (candidateLocal && primaryLocal) {
            throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
                "more than one 'primary' bean found among candidates: " + candidates.keySet());
          }
          else if (candidateLocal) {
            primaryBeanName = candidateBeanName;
          }
        }
        // 把找出来的标注了@Primary的Bean的名称返回出去
        else {
          primaryBeanName = candidateBeanName;
        }
      }
    }
    return primaryBeanName;
  } 
// determineHighestPriorityCandidate:从给定的Bean里面筛选出一个优先级最高的
// 什么叫优先级最高呢?主要为了兼容JDK6提供的注解javax.annotation.Priority
  @Nullable
  protected String determineHighestPriorityCandidate(Map<String, Object> candidates, Class<?> requiredType) {
    String highestPriorityBeanName = null;
    Integer highestPriority = null;
    for (Map.Entry<String, Object> entry : candida         tes.entrySet()) {
      String candidateBeanName = entry.getKey();
      Object beanInstance = entry.getValue();
      if (beanInstance != null) {
        //AnnotationAwareOrderComparator#getPriority
        // 这里就是为了兼容JDK6提供的javax.annotation.Priority这个注解,然后做一个优先级排序
        // 注意注意注意:这里并不是@Order,和它木有任何关系~~~
        // 它有的作用像Spring提供的@Primary注解
        Integer candidatePriority = getPriority(beanInstance);
        // 大部分情况下,我们这里都是null,但是需要注意的是,@Primary只能标注一个,这个虽然可以标注多个,但是里面的优先级值,不能出现相同的(强烈建议不要使用~~~~而使用@Primary)
        if (candidatePriority != null) {
          if (highestPriorityBeanName != null) {
            // 如果优先级的值相等,是不允许的,这里需要引起注意,个人建议一般还是使用@Primary吧
            if (candidatePriority.equals(highestPriority)) {
              throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
                  "Multiple beans found with the same priority ('" + highestPriority +
                  "') among candidates: " + candidates.keySet());
            }
            else if (candidatePriority < highestPriority) {
              highestPriorityBeanName = candidateBeanName;
              highestPriority = candidatePriority;
            }
          }
          else {
            highestPriorityBeanName = candidateBeanName;
            highestPriority = candidatePriority;
          }
        }
      }
    }
    return highestPriorityBeanName;
  }





相关文章
|
30天前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
110 9
|
14天前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
43 0
|
2月前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
93 3
|
3月前
|
XML Java 数据格式
Spring5入门到实战------8、IOC容器-Bean管理注解方式
这篇文章详细介绍了Spring5框架中使用注解进行Bean管理的方法,包括创建Bean的注解、自动装配和属性注入的注解,以及如何用配置类替代XML配置文件实现完全注解开发。
Spring5入门到实战------8、IOC容器-Bean管理注解方式
|
3月前
|
Java Spring 容器
彻底改变你的编程人生!揭秘 Spring 框架依赖注入的神奇魔力,让你的代码瞬间焕然一新!
【8月更文挑战第31天】本文介绍 Spring 框架中的依赖注入(DI),一种降低代码耦合度的设计模式。通过 Spring 的 DI 容器,开发者可专注业务逻辑而非依赖管理。文中详细解释了 DI 的基本概念及其实现方式,如构造器注入、字段注入与 setter 方法注入,并提供示例说明如何在实际项目中应用这些技术。通过 Spring 的 @Configuration 和 @Bean 注解,可轻松定义与管理应用中的组件及其依赖关系,实现更简洁、易维护的代码结构。
49 0
|
3月前
|
自然语言处理 Java 开发者
简单了解下Spring中的各种Aware接口实现依赖注入
【8月更文挑战第21天】在Spring框架中,Aware接口系列是一种特殊的机制,它允许Bean在初始化过程中获取到Spring容器或容器中的特定资源,从而实现了更加灵活和强大的依赖注入方式。本文将围绕Spring中的各种Aware接口,详细探讨它们如何帮助开发者在工作和学习中更好地实现依赖注入。
95 0
|
19天前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
39 0
|
19天前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
29 0
|
19天前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
34 0
|
19天前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
45 0

热门文章

最新文章

推荐镜像

更多