Spring源码分析(九)依赖注入源码解析2:AutowireCapableBeanFactory#resolveDependency 从工厂解析依赖项

2023-11-19

入口分析

先回顾一下上节@Autowired注解注入,寻找注入点的方法:
在这里插入图片描述
AutowiredFieldElement:字段对应的注入点
AutowiredMethodElement:方法对应的注入点
并且是针对@Autowired、@Value、@Inject注解标记的字段或方法:
在这里插入图片描述

注入点注入入口:
org.springframework.beans.factory.annotation.InjectionMetadata#inject

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Collection<InjectedElement> checkedElements = this.checkedElements;
    Collection<InjectedElement> elementsToIterate =
            (checkedElements != null ? checkedElements : this.injectedElements);
    if (!elementsToIterate.isEmpty()) {
        //遍历每个注入点进行依赖注入,有可能是字段,也有可能是方法
        for (InjectedElement element : elementsToIterate) {
            element.inject(target, beanName, pvs);
        }
    }
}

根据字段、方法有不同的实现,依次看一下

字段注入:
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Field field = (Field) this.member;
    Object value;
    //优先从缓存取,先不看
    if (this.cached) {...}
    else {
        //根据field从BeanFactory中查到的匹配的Bean对象
        value = resolveFieldValue(field, bean, beanName);
    }
    if (value != null) {
        //反射给field赋值
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
    }
}

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue

private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
    //构造依赖描述符对象
    //	DependencyDescriptor:描述符用于即将被注入的特定依赖项。包装构造函数参数、方法参数或字段,允许统一访问它们的元数据。
    //	this.required:@Autowired的required属性
    DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
    desc.setContainingClass(bean.getClass());
    
    //autowiredBeanNames(在后续执行过程中会)记录当前Bean需要注入的Bean的BeanName
    Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
    Assert.state(beanFactory != null, "No BeanFactory available");
    //类型转换器
    TypeConverter typeConverter = beanFactory.getTypeConverter();
    Object value;
    try {
        //核心方法,从工厂中解析依赖项要注入的值
        //针对这个工厂中定义的bean解析指定的依赖项
        value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
    }
    catch (BeansException ex) {...}
    //后面是将结果缓存,先不看
    synchronized (this) {...}
    return value;
}

核心方法是AutowireCapableBeanFactory#resolveDependency

方法注入:
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement#inject

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    //如果pvs中已经有当前注入点的值了,则跳过注入
    if (checkPropertySkipping(pvs)) {
        return;
    }
    Method method = (Method) this.member;
    Object[] arguments;
    //有缓存先从缓存取,暂时不看
    if (this.cached) {...}
    else {
        //当前是方法入注,这里会解析方法入参
        arguments = resolveMethodArguments(method, bean, beanName);
    }
    if (arguments != null) {
        try {
            //反射调用方法
            ReflectionUtils.makeAccessible(method);
            method.invoke(bean, arguments);
        }
        catch (InvocationTargetException ex) {...}
    }
}

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement#resolveMethodArguments

    private Object[] resolveMethodArguments(Method method, Object bean, @Nullable String beanName) {
        //方法有可能有多个参数,所以这里都是数组
        int argumentCount = method.getParameterCount();
        Object[] arguments = new Object[argumentCount];
        DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
        
        //autowiredBeanNames记录了当前Bean需要注入的Bean的BeanName
        Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
        Assert.state(beanFactory != null, "No BeanFactory available");
        //类型转换器
        TypeConverter typeConverter = beanFactory.getTypeConverter();

        //遍历每个方法参数,找到匹配的bean对象
        for (int i = 0; i < arguments.length; i++) {
            //i是索引,代表方法上的第几个参数
            MethodParameter methodParam = new MethodParameter(method, i);
            //构造依赖描述符(对于方法入注,多个参数情况就会对应多个依赖描述符)
            DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
            currDesc.setContainingClass(bean.getClass());
            descriptors[i] = currDesc;
            try {
                //看到也是调用了beanFactory.resolveDependency
                //核心方法,从工厂中解析依赖项要注入的值
                //针对这个工厂中定义的bean解析指定的依赖项
                Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
                if (arg == null && !this.required) {
                    arguments = null;
                    break;
                }
                //每个参数对应的要传入的值
                arguments[i] = arg;
            }
            catch (BeansException ex) {
                throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
            }
        }
        //缓存逻辑,和字段注入差不多,暂时不看
        synchronized (this) {...}
        return arguments;
    }
}

可以看到,无论是字段注入,还是方法注入,解析依赖项要注入的值都调用了org.springframework.beans.factory.config.AutowireCapableBeanFactory#resolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, java.lang.String, java.util.Set<java.lang.String>, org.springframework.beans.TypeConverter)

从工厂解析依赖项

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

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
        @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
	//descriptor可能是字段,也可能是普通方法、构造方法中的一个参数

    //1.初始化参数名字发现器,用来获取方法入参名字的
    //	如果是字段注入,拿字段的类型和字段的名字比较简单
    //  但是方法注入,拿方法参数的类型简单,但是拿方法参数的名字就比较复杂
    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());

    //接下来会判断 字段/方法参数 的类型,走不同的逻辑:

    // 2. 处理Optional类型的依赖注入
    if (Optional.class == descriptor.getDependencyType()) {
        //为指定的依赖项创建可选包装器。
        return createOptionalDependency(descriptor, requestingBeanName);
    }
    // 3. 处理ObjectFactory和ObjectProvider类型的依赖注入
    else if (ObjectFactory.class == descriptor.getDependencyType() ||
            ObjectProvider.class == descriptor.getDependencyType()) {
        //可序列化的ObjectFactory/ObjectProvider用于延迟解析依赖项。
        return new DependencyObjectProvider(descriptor, requestingBeanName);
    }
    // 处理JSR330 相关的依赖注入
    else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
        return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
    }
    else {
    	//4. 查找具体的依赖注入对象(最常见)

        //4.1 判断是否是懒注入
        //	在属性或set方法上使用了@Lazy注解,那么则构造一个代理对象并返回,真正使用该代理对象时才进行类型筛选bean
        Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
                descriptor, requestingBeanName);
        if (result == null) {
            //4.2 真正开始解析依赖项,核心是doResolveDependency
            //	descriptor表示某个字段属性、或某个方法上的某个入参
            //	requestingBeanName表示当前正在进行依赖注入的Bean
            result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
        }
        return result;
    }
}

1. 初始化参数名字发现器

在jdk1.7中,拿方法入参类型有直接的api,但是获取参数名字没有直接提供api:
在这里插入图片描述
而1.8提供了getParameters方法可以获取Parameter,但是getName名字并不是我们期望的:
在这里插入图片描述
还需要配置一个编译时期的参数:
在这里插入图片描述
刷新一下maven,并重新编译以后再次执行:
在这里插入图片描述

上面可以看出,java反射层面其实并没有什么比较方便的api可以拿到方法入参到名字,那Spring是如何拿到的呢?默认支持两种方式,一个是上面说的反射,另一个是利用了一些字节码层面的技术,直接分析字节码内容是可以拿到的。

看Spring源码:
org.springframework.beans.factory.config.DependencyDescriptor#initParameterNameDiscovery

//这个方法比较简单,就是set赋值
public void initParameterNameDiscovery(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) {
    if (this.methodParameter != null) {
        this.methodParameter.initParameterNameDiscovery(parameterNameDiscoverer);
    }
}

//org.springframework.core.MethodParameter#initParameterNameDiscovery
public void initParameterNameDiscovery(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) {
    this.parameterNameDiscoverer = parameterNameDiscoverer;
}

获取参数名字发现器:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getParameterNameDiscoverer

protected ParameterNameDiscoverer getParameterNameDiscoverer() {
    return this.parameterNameDiscoverer;
}

有默认实现提供:
在这里插入图片描述

public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {

	public DefaultParameterNameDiscoverer() {
		// TODO Remove this conditional inclusion when upgrading to Kotlin 1.5, see https://youtrack.jetbrains.com/issue/KT-44594
		if (KotlinDetector.isKotlinReflectPresent() && !NativeDetector.inNativeImage()) {
			addDiscoverer(new KotlinReflectionParameterNameDiscoverer());
		}
        //看到有两个具体的实现,会依次调用Discoverer来获取某个方法的参数名
        //StandardReflectionParameterNameDiscoverer:它使用JDK 8的反射功能来内省参数名(基于“-parameters”编译器标志)。
		addDiscoverer(new StandardReflectionParameterNameDiscoverer());
        //LocalVariableTableParameterNameDiscoverer:使用ObjectWeb的ASM库来分析类文件。
        //解析字节码文件,使用方法属性中的LocalVariableTable信息来发现参数名称。
		addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
	}

}

优先利用反射去拿,如果运行环境是jdk1.7或者jdk1.8没有设置编译参数,有可能拿不到,再利用字节码中的本地变量表去拿。
小仙女讲JVM(4)—类文件结构

2. 处理Optional类型的依赖注入

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

private Optional<?> createOptionalDependency(
        DependencyDescriptor descriptor, @Nullable String beanName, final Object... args) {

    DependencyDescriptor descriptorToUse = new NestedDependencyDescriptor(descriptor) {
        @Override
        public boolean isRequired() {
            return false;
        }
        @Override
        public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) {
            return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, args) :
                    super.resolveCandidate(beanName, requiredType, beanFactory));
        }
    };
    //4.2 核心是doResolveDependency,真正开始解析依赖项
    Object result = doResolveDependency(descriptorToUse, beanName, null, null);
    //最终返回结果如果不是Optional,会用Optional再包装一下
    return (result instanceof Optional ? (Optional<?>) result : Optional.ofNullable(result));
}

3. 处理ObjectFactory和ObjectProvider类型的依赖注入

org.springframework.beans.factory.support.DefaultListableBeanFactory.DependencyObjectProvider#getObject()

public Object getObject() throws BeansException {
    if (this.optional) {
        //2. 处理Option类型的依赖注入
        return createOptionalDependency(this.descriptor, this.beanName);
    }
    else {
        //4.2 核心还是doResolveDependency,真正开始解析依赖项
        Object result = doResolveDependency(this.descriptor, this.beanName, null, null);
        if (result == null) {
            throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());
        }
        return result;
    }
}

延迟依赖查找功能,只有当使用者调用ObjectProvider#getObject()方法时,才会通过依赖查找的方式获取对应的Bean

Bean的延迟依赖查找功能,ObjectFactory 和 ObjectProvider

4. 查找具体的依赖注入对象

4.1 判断是否是懒注入

@Lazy注解可以加在类上、字段上,也可以加在方法入参上
在这里插入图片描述

//4.1 判断是否是懒注入
//	在属性或set方法上使用了@Lazy注解,那么则构造一个代理对象并返回,真正使用该代理对象时才进行类型筛选bean
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
        descriptor, requestingBeanName);
if (result == null) {
    //4.2 真正开始解析依赖项,核心是doResolveDependency
    //	descriptor表示某个字段属性、或某个方法上的某个入参
    //	requestingBeanName表示当前正在进行依赖注入的Bean
    result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;

getAutowireCandidateResolver默认返回的是ContextAnnotationAutowireCandidateResolver
org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary

//判断是不是懒注入(@Autowired + @Lazy),如果是,则会在注入时先生成一个代理对象注入给属性
//所以懒注入并不代表属性为null
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
    //不是懒注入,则返回null,否则创建一个代理对象
    return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
}

判断是否是懒注入

判断是否有@Lazy注解,且value是true:
org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#isLazy

protected boolean isLazy(DependencyDescriptor descriptor) {
    //1.参数级别获取注解
    //获取与被包装的字段或方法/构造函数参数关联的注解
    for (Annotation ann : descriptor.getAnnotations()) {
        Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
        if (lazy != null && lazy.value()) {
            return true;
        }
    }
	//2.方法级别获取注解
	//判断方法/构造函数上的注解
    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;
}

注意虽然MethodParameter代表的是方法上的某一个参数,但是methodParam.getAnnotatedElement()返回的是方法上的注解,非参数级别的。

创建懒注入的代理对象

动态代理,不是本节重点,以后讲SpringAOP的时候会细说。
只要知道这里会生成一个代理对象即可,当真正执行方法的时候才会去解析依赖项。

org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#buildLazyResolutionProxy

protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
    BeanFactory beanFactory = getBeanFactory();
    Assert.state(beanFactory instanceof DefaultListableBeanFactory,
            "BeanFactory needs to be a DefaultListableBeanFactory");
    final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;

    TargetSource ts = new TargetSource() {
        //被代理对象类型
        @Override
        public Class<?> getTargetClass() {
            return descriptor.getDependencyType();
        }
        //是否是静态的:true,则每次调用getTarget()返回的都是相同的对象
        @Override
        public boolean isStatic() {
            return false;
        }
        //获取被代理对象
        @Override
        public Object getTarget() {
            Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);
            //4.2 核心还是doResolveDependency,真正开始解析依赖项
            Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);
            if (target == null) {...}
            if (autowiredBeanNames != null) {...}
            return target;
        }
        @Override
        public void releaseTarget(Object target) {...}
    };

    ProxyFactory pf = new ProxyFactory();
    pf.setTargetSource(ts);
    Class<?> dependencyType = descriptor.getDependencyType();
    if (dependencyType.isInterface()) {
        pf.addInterface(dependencyType);
    }
    //生成一个代理对象
    return pf.getProxy(dlbf.getBeanClassLoader());
}

在这里插入图片描述

一开始只会赋值一个代理对象,当真正使用这个对象执行其方法的时候,作为一个代理对象,最终肯定会去拿到被代理对象,执行被代理对象对应的方法,所以就会调用org.springframework.aop.TargetSource#getTarget这个方法拿到被代理对象,而在这个方法中才会真正解析依赖项:
在这里插入图片描述

4.2 真正开始解析依赖项(最核心方法)

篇幅过长,下一节讲。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring源码分析(九)依赖注入源码解析2:AutowireCapableBeanFactory#resolveDependency 从工厂解析依赖项 的相关文章

  • 如何向我的 Spring MVC REST 服务添加错误?

    如果用户没有输入我正在编码的两个名称 我如何更改 更新来自 Spring MVC 的以下 REST 调用以返回错误 未找到 RequestMapping value name method RequestMethod GET Respons
  • 使用 Spring / JPA 写入 Postgres 数据库的 JSON 列

    我有一个名为 test 的表 其中包含 Postgres 9 3 中 json 类型的列 sample column 我正在尝试使用 Spring JPA 将以下内容写入列中 name 更新的名称 我在其他帖子中读到 我需要添加自定义转换器
  • 使用@Transactional注解批量插入

    在我的 Spring 应用程序中 我想一次性在数据库中插入近 1500 条记录 我在后端使用 Spring 4 X 和普通休眠 在我的服务层中 我使用 Transactional 注释 现在 在某个时间点之后插入记录时 我遇到内存不足错误
  • Spring 作为 JNDI 提供者?

    我想使用 Spring 作为 JNDI 提供程序 这意味着我想在 Spring 上下文中配置一个 bean 可以通过 JNDI 访问该 bean 这看起来像这样
  • Spring @ContextConfiguration

    我正在运行下一个测试 import static org junit Assert assertEquals import org junit Test import org junit runner RunWith import org
  • Jackson:将对象引用为属性

    在我的 java spring 应用程序中 我正在使用 hibernate 和 jpa 并使用 jackson 来填充数据库中的数据 这是用户类 Data Entity public class User Id GeneratedValue
  • 在 swagger 中隐藏 spring 请求正文中的某些字段

    下面的示例 api 允许用户创建一个对象 用户应该能够指定name的领域Thing对象 而id字段应该自动生成 根据以下设置 将显示 swaggerboth请求的名称和 id 字段作为用户可以输入的内容 并将这两个字段显示为可选 事实上 对
  • Spring Data JPA,对多对多实体的一个属性的更改错误地显示在共享它的所有其他实体上

    当我更改实体的一个属性时 使用该实体的每个其他实体也会以某种方式更改它 我有三个实体 如下所示 学生和课程之间需要有多对多的关系 课程需要和课程讲座有一对多的关系 当我通过 Transactional 更改属于特定学生的课程或课程讲座时st
  • Java 11 - 将 Spring @PostConstruct 替换为 afterPropertiesSet 或使用 initMethod

    我正在使用 spring 应用程序 有时会使用 PostConstruct用于代码和测试中的设置 看来注释将被排除在外Java 11 https www baeldung com spring postconstruct predestro
  • 如何在url请求中发送数组

    我的要求如下 我想给出演员姓名 开始日期 结束日期并获取他在该时期出演的所有电影 因此 我的服务请求是这样的 http localhost 8080 MovieDB GetJson name Actor startDate 20120101
  • Spring中的ProxyFactoryBean

    有人可以解释一下吗代理工厂Bean http static springsource org spring docs current javadoc api org springframework aop framework ProxyFa
  • 具有 JPA 持久性的 Spring 状态机 - 存储库使用

    我试图弄清楚如何轻松使用 Spring 状态机 包括使用 JPA 进行持久化 这是我正在处理的问题 不兼容的数据类型 工厂和持久性 在程序的某个时刻 我想使用连接到用户的状态机 有用于此目的的存储库 项目spring statemachin
  • 多线程Spring-boot控制器方法

    因此 我的应用程序 spring boot 运行速度非常慢 因为它使用 Selenium 来抓取数据 处理数据并显示在主页中 我遇到了多线程 我认为它对我的应用程序很有用 可以让它运行得更快 但是教程似乎显示在带有 main c 的普通 j
  • Spring RestTemplate 使用 cookie 遵循重定向

    最近我遇到了一个问题 我需要做一个GET请求远程服务 我假设使用一个简单的 servlet 并且 RestTemplate 返回Too many redirects 经过一番调查 似乎对指定远程服务发出的第一个请求实际上只是一个 302 重
  • Spring Boot自动装配存储库始终为空[重复]

    这个问题在这里已经有答案了 每次我进入我的服务类时 存储库似乎都没有自动连接 因为它不断抛出 NullPointerException 谁能帮我检查一下我缺少什么吗 这是我的代码 演示应用程序 java package com exampl
  • @Autowire注释的问题(空)

    我在验证器类中自动连接的两个服务有问题 这些服务工作正常 因为在我的控制器中是自动连接的 我有一个 applicationContext xml 文件和 MyApp servlet xml 文件 我的基础包是 es unican meteo
  • Docker 和 Eureka 与 Spring Boot 无法注册客户端

    我有一个使用 Spring Boot Docker Compose Eureka 的非常简单的演示 我的服务器在端口 8671 上运行 具有以下应用程序属性 server port 8761 eureka instance prefer i
  • OpenCSV:将嵌套 Bean 映射到 CSV 文件

    我正在尝试将 bean 映射到 CSV 文件 但问题是我的 bean 具有其他嵌套 bean 作为属性 所发生的情况是 OpenCSV 遍历属性找到一个 bean 然后进入其中并映射该 bean 内的所有数据 如果找到另一个 bean 它就
  • Spring表单ModelAttribute字段验证避免400 Bad Request错误

    我有一个ArticleFormModel包含正常发送的数据html form由 Spring 使用注入 ModelAttribute注释 即 RequestMapping value edit method RequestMethod PO
  • Spring Boot - YML 配置 - 合并时擦除条目

    我的应用程序有一个基本 YML 配置 在类路径中如下所示 hello world values bar name bar name description bar description foo name foo name descript

随机推荐