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

简介: 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 方法。



相关文章
|
6月前
|
缓存 安全 Java
Spring Security通用权限管理模型解析
Spring Security作为Spring生态的核心安全框架,结合RBAC与ACL权限模型,基于IoC与AOP构建灵活、可扩展的企业级权限控制体系,涵盖认证、授权流程及数据库设计、性能优化等实现策略。
461 0
|
6月前
|
缓存 安全 Java
Spring Security权限管理解析
Spring Security是Spring生态中的核心安全框架,采用认证与授权分离架构,提供高度可定制的权限管理方案。其基于过滤器链实现认证流程,通过SecurityContextHolder管理用户状态,并结合RBAC模型与动态权限决策,支持细粒度访问控制。通过扩展点如自定义投票器、注解式校验与前端标签,可灵活适配多租户、API网关等复杂场景。结合缓存优化与无状态设计,适用于高并发与前后端分离架构。
470 0
|
6月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
2261 0
|
5月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
5月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
5月前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
5月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
637 2
|
6月前
|
Java 数据库 数据安全/隐私保护
Spring Boot四层架构深度解析
本文详解Spring Boot四层架构(Controller-Service-DAO-Database)的核心思想与实战应用,涵盖职责划分、代码结构、依赖注入、事务管理及常见问题解决方案,助力构建高内聚、低耦合的企业级应用。
1301 1
|
XML NoSQL Java
Spring核心(IoC) 入门解读
Spring核心(IoC) 入门解读
245 1
Spring核心(IoC) 入门解读
|
8月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
1239 0

热门文章

最新文章