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


    入口分析

    先回顾一下上节@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);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    字段注入:
    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    核心方法是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) {...}
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    可以看到,无论是字段注入,还是方法注入,解析依赖项要注入的值都调用了org.springframework.beans.factory.config.AutowireCapableBeanFactory#resolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, java.lang.String, java.util.Set, 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    protected ParameterNameDiscoverer getParameterNameDiscoverer() {
        return this.parameterNameDiscoverer;
    }
    
    • 1
    • 2
    • 3

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

    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());
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    优先利用反射去拿,如果运行环境是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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    延迟依赖查找功能,只有当使用者调用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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    判断是否是懒注入

    判断是否有@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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    注意虽然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());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    在这里插入图片描述

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

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

    篇幅过长,下一节讲。

  • 相关阅读:
    git stash详解
    JVM面试常考的4个问题详解
    TensorFlow Lite Micro简介与使用
    掌握 CocoaPods:iOS 开发的依赖管理神器,一文全攻略!
    数字图像处理之matlab常见函数
    【DB2】—— 数据库表查询一直查不出来数据
    腾讯智能表格文档如何做进度管理
    JavaScript设计模式中的享元模式
    RK3399 Android7.1修改安兔兔等读到的Build Fingerprint内容
    efficientsam-pytorch基于point、box和segment everthing推理模型
  • 原文地址:https://blog.csdn.net/weixin_41947378/article/details/127585682