Spring依赖注入(DI)核心接口AutowireCandidateResolver深度分析,解析@Lazy、@Qualifier注解的原理【享学Spring】(中)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Spring依赖注入(DI)核心接口AutowireCandidateResolver深度分析,解析@Lazy、@Qualifier注解的原理【享学Spring】(中)

ContextAnnotationAutowireCandidateResolver


官方把这个类描述为:策略接口的完整实现。它不仅仅支持上面所有描述的功能,还支持@Lazy懒处理~~~(注意此处懒处理(延迟处理),不是懒加载~)


@Lazy一般含义是懒加载,它只会作用于BeanDefinition.setLazyInit()。而此处给它增加了一个能力:延迟处理(代理处理)


// @since 4.0 出现得挺晚,它支持到了@Lazy  是功能最全的AutowireCandidateResolver
public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {
  // 这是此类本身唯一做的事,此处精析 
  // 返回该 lazy proxy 表示延迟初始化,实现过程是查看在 @Autowired 注解处是否使用了 @Lazy = true 注解 
  @Override
  @Nullable
  public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
    // 如果isLazy=true  那就返回一个代理,否则返回null
    // 相当于若标注了@Lazy注解,就会返回一个代理(当然@Lazy注解的value值不能是false)
    return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
  }
  // 这个比较简单,@Lazy注解标注了就行(value属性默认值是true)
  // @Lazy支持标注在属性上和方法入参上~~~  这里都会解析
  protected boolean isLazy(DependencyDescriptor descriptor) {
    for (Annotation ann : descriptor.getAnnotations()) {
      Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
      if (lazy != null && lazy.value()) {
        return true;
      }
    }
    MethodParameter methodParam = descriptor.getMethodParameter();
    if (methodParam != null) {
      Method method = methodParam.getMethod();
      if (method == null || void.class == method.getReturnType()) {
        Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);
        if (lazy != null && lazy.value()) {
          return true;
        }
      }
    }
    return false;
  }
  // 核心内容,是本类的灵魂~~~
  protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
    Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
        "BeanFactory needs to be a DefaultListableBeanFactory");
    // 这里毫不客气的使用了面向实现类编程,使用了DefaultListableBeanFactory.doResolveDependency()方法~~~
    final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
    //TargetSource 是它实现懒加载的核心原因,在AOP那一章节了重点提到过这个接口,此处不再叙述
    // 它有很多的著名实现如HotSwappableTargetSource、SingletonTargetSource、LazyInitTargetSource、
    //SimpleBeanTargetSource、ThreadLocalTargetSource、PrototypeTargetSource等等非常多
    // 此处因为只需要自己用,所以采用匿名内部类的方式实现~~~ 此处最重要是看getTarget方法,它在被使用的时候(也就是代理对象真正使用的时候执行~~~)
    TargetSource ts = new TargetSource() {
      @Override
      public Class<?> getTargetClass() {
        return descriptor.getDependencyType();
      }
      @Override
      public boolean isStatic() {
        return false;
      }
      // getTarget是调用代理方法的时候会调用的,所以执行每个代理方法都会执行此方法,这也是为何doResolveDependency
      // 我个人认为它在效率上,是存在一定的问题的~~~所以此处建议尽量少用@Lazy~~~   
      //不过效率上应该还好,对比http、序列化反序列化处理,简直不值一提  所以还是无所谓  用吧
      @Override
      public Object getTarget() {
        Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
        if (target == null) {
          Class<?> type = getTargetClass();
          // 对多值注入的空值的友好处理(不要用null)
          if (Map.class == type) {
            return Collections.emptyMap();
          } else if (List.class == type) {
            return Collections.emptyList();
          } else if (Set.class == type || Collection.class == type) {
            return Collections.emptySet();
          }
          throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
              "Optional dependency not present for lazy injection point");
        }
        return target;
      }
      @Override
      public void releaseTarget(Object target) {
      }
    };
    // 使用ProxyFactory  给ts生成一个代理
    // 由此可见最终生成的代理对象的目标对象其实是TargetSource,而TargetSource的目标才是我们业务的对象
    ProxyFactory pf = new ProxyFactory();
    pf.setTargetSource(ts);
    Class<?> dependencyType = descriptor.getDependencyType();
    // 如果注入的语句是这么写的private AInterface a;  那这类就是借口 值是true
    // 把这个接口类型也得放进去(不然这个代理都不属于这个类型,反射set的时候岂不直接报错了吗????)
    if (dependencyType.isInterface()) {
      pf.addInterface(dependencyType);
    }
    return pf.getProxy(beanFactory.getBeanClassLoader());
  }
}


它很好的用到了TargetSource这个接口,结合动态代理来支持到了@Lazy注解。

标注有@Lazy注解完成注入的时候,最终注入只是一个此处临时生成的代理对象,只有在真正执行目标方法的时候才会去容器内拿到真是的bean实例来执行目标方法。


特别注意:此代理对象非彼代理对象,这个一定一定一定要区分开来~


通过@Lazy注解能够解决很多情况下的循环依赖问题,它的基本思想是先'随便'给你创建一个代理对象先放着,等你真正执行方法的时候再实际去容器内找出目标实例执行~


我们要明白这种解决问题的思路带来的好处是能够解决很多场景下的循环依赖问题,但是要知道它每次执行目标方法的时候都会去执行TargetSource.getTarget()方法,所以需要做好缓存,避免对执行效率的影响(实测执行效率上的影响可以忽略不计)


ContextAnnotationAutowireCandidateResolver这个处理器才是被Bean工厂最终最终使用的,因为它的功能是最全的~


回顾一下,注册进Bean工厂的参考代码处:


public abstract class AnnotationConfigUtils {
  ...
  public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {
    DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    if (beanFactory != null) {
      // 设置默认的排序器  支持@Order等
      if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
        beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
      }
      // 设置依赖注入的候选处理器
      // 可以看到只要不是ContextAnnotationAutowireCandidateResolver类型  直接升级为最强类型
      if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
      }
    }
    ... // ====下面是大家熟悉的注册默认6大后置处理器:====
    // 1.ConfigurationClassPostProcessor
    // 2.AutowiredAnnotationBeanPostProcessor 
    // 3.CommonAnnotationBeanPostProcessor
    // 4.Jpa的PersistenceAnnotationProcessor(没导包就不会注册)
    // 5.EventListenerMethodProcessor
    // 6.DefaultEventListenerFactory
  }
  ...
}


此段代码执行还是非常早的,在容器的刷新时的ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();这一步就完成了~


最后,把这个四个哥们 从上至下 简单总结如下:


  1. SimpleAutowireCandidateResolver 相当于一个简单的适配器
  2. GenericTypeAwareAutowireCandidateResolver 判断泛型是否匹配,支持泛型依赖注入(From Spring4.0)
  3. QualifierAnnotationAutowireCandidateResolver 处理 @Qualifier 和 @Value 注解
  4. ContextAnnotationAutowireCandidateResolver 处理 @Lazy 注解,重写了 getLazyResolutionProxyIfNecessary 方法。



相关文章
|
30天前
|
XML Java 数据格式
探索Spring之利剑:ApplicationContext接口
本文深入介绍了Spring框架中的核心接口ApplicationContext,解释了其作为应用容器的功能,包括事件发布、国际化支持等,并通过基于XML和注解的配置示例展示了如何使用ApplicationContext管理Bean实例。
59 6
|
17天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
2月前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
159 2
|
2月前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
67 2
|
2月前
|
前端开发 Java 开发者
Spring MVC中的控制器:@Controller注解全解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序控制层的核心。它不仅简化了控制器的定义,还提供了灵活的请求映射和处理机制。本文将深入探讨`@Controller`注解的用法、特点以及在实际开发中的应用。
121 0
|
3月前
|
存储 安全 Java
|
3月前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
57 0
|
3月前
|
自然语言处理 JavaScript Java
Spring 实现 3 种异步流式接口,干掉接口超时烦恼
本文介绍了处理耗时接口的几种异步流式技术,包括 `ResponseBodyEmitter`、`SseEmitter` 和 `StreamingResponseBody`。这些工具可在执行耗时操作时不断向客户端响应处理结果,提升用户体验和系统性能。`ResponseBodyEmitter` 适用于动态生成内容场景,如文件上传进度;`SseEmitter` 用于实时消息推送,如状态更新;`StreamingResponseBody` 则适合大数据量传输,避免内存溢出。文中提供了具体示例和 GitHub 地址,帮助读者更好地理解和应用这些技术。
495 0
|
4月前
|
存储 数据采集 Java
Spring Boot 3 实现GZIP压缩优化:显著减少接口流量消耗!
在Web开发过程中,随着应用规模的扩大和用户量的增长,接口流量的消耗成为了一个不容忽视的问题。为了提升应用的性能和用户体验,减少带宽占用,数据压缩成为了一个重要的优化手段。在Spring Boot 3中,通过集成GZIP压缩技术,我们可以显著减少接口流量的消耗,从而优化应用的性能。本文将详细介绍如何在Spring Boot 3中实现GZIP压缩优化。
502 6
|
3月前
|
存储 NoSQL Java
Spring Boot项目中使用Redis实现接口幂等性的方案
通过上述方法,可以有效地在Spring Boot项目中利用Redis实现接口幂等性,既保证了接口操作的安全性,又提高了系统的可靠性。
74 0

推荐镜像

更多