【重写SpringFramework】第一章beans模块:填充对象(chapter 1-6)

1. 前言

在对象实例化之后,我们需要对一些字段进行赋值,这一过程称之为对象的填充(populate)。填充对象由两部分组成,一是属性访问,二是自动装配(autowire)。属性访问的功能已经介绍过了,本节主要讨论的是自动装配的问题。自动装配也称依赖注入,包括两个部分,即环境变量解析和对象解析,主要有三点区别:

  • 一是注解不同,环境变量解析只使用 @Value 注解,对象解析使用 @Autowired@Resource@Inject 等注解。
  • 二是依赖项的来源不同,环境变量解析的依赖项来自 Enviroment 的某个属性值,对象解析的依赖项来自 Spring 容器中的单例。
  • 三是注入方式不同,环境变量解析只能通过字段注入,对象解析可以使用字段注入或 setter 方法注入。

无论是环境变量解析,还是对象解析,最终都要通过依赖解析的功能,完成依赖项的查找和注入。依赖解析分为三种,嵌入值处理仅用于环境变量的解析,集合类型和单一类型的解析则用于对象解析。

在这里插入图片描述

2. 自动装配原理

2.1 注入目标

关于依赖注入,我们可以从两个角度来审视。首先,从注入目标来说,可以分为两种:一是字段,二是方法参数。示例代码演示了对象解析的两种方式,一是字段注入,@Autowired 注解声明在字段上。二是 setter 方法注入,@Autowired 注解声明在 setter 方法上,实际注入的是方法参数 bar

//示例代码:注入目标为字段
public class Foo {
    @Autowired
    private Bar bar;
}

//示例代码:注入目标为方法参数
public class Foo {
    private Bar bar;

    @Autowired
    public void setBar(Bar bar) {
        this.bar = bar;
    }
}

对于环境变量和对象解析来说,都可以通过字段注入的方式设置依赖项。示例代码如下,@Value 注解声明在 profile 字段上,表明该字段的值来自环境变量。@Autowired 注解声明在自定义对象上,表明该字段的值来自容器托管的单例。既然环境变量和对象解析都可以进行字段注入,那么基本逻辑是相同的。本节主要介绍环境变量的解析过程,并以此作为对象解析的基础。

//示例代码:字段注入
public class Foo {
    @Value("${spring.profiles.active}")
    private String profile;
    @Autowired
    private Bar bar;
}

2.2 DependencyDescriptor

Spring 对注入目标进行了抽象,使用 DependencyDescriptor 来描述一个字段或方法参数的依赖信息,从这个角度来看,该类与 BeanDefinition 的作用是相似的。

  • containingClass:注入目标所属的具体类
  • field:表明注入目标是一个字段(与方法参数二选一)
  • methodParameter:表明注入目标是一个方法参数(与字段二选一)
  • nestingLevel:嵌套层级,简单类型的层级为 1,泛型集合或数组的层级大于 1,比如 List<String> 的层级为 2
  • required:依赖项是否必须存在,如果为 true 且没有找到依赖项则报错
  • fieldAnnotations:声明在字段上的注解
  • resolvableType:如果依赖类型是集合或数组,则需要得到元素的类型
public class DependencyDescriptor {
    private Class<?> containingClass;
    private Field field;
    private MethodParameter methodParameter;
    private int nestingLevel = 1;
    private final boolean required;
    private volatile Annotation[] fieldAnnotations;
    private volatile ResolvableType resolvableType;
}

2.3 InjectionMetadata

注入目标决定了注入方式,InjectionMetadata 表示一个类的注入元数据,所谓元数据是指这个类所有需要注入的元素的信息。targetClass 字段表示目标对象,也就是 BeanFactory 正在处理的实例。injectedElements 字段表示需要注入的元素,由于一个对象可能有多个字段或方法参数需要注入,因此该字段是集合类型。

public class InjectionMetadata {
    private final Class<?> targetClass;
    private final Collection<InjectedElement> injectedElements;

    public static abstract class InjectedElement{
        protected final Member member;
        protected final PropertyDescriptor pd;

        protected abstract void inject(Object bean, String beanName) throws Throwable;
    }
}

InjectedElement 作为内部抽象类,作用是描述一个注入的元素。member 字段表示类的成员,在这里指字段和方法。AutowiredAnnotationBeanPostProcessor 组件定义了 InjectedElement 的两个子类,其中 AutowiredFieldElement 表示注入到字段上,AutowiredMethodElement 表示注入 setter 方法。

在这里插入图片描述

3. BeanPostProcessor

3.1 概述

俗话说一个好汉三个帮,Spring 容器的强大功能离不开各种组件的支持。BeanPostProcessor 提供了若干钩子方法,在 Spring 容器创建对象的不同阶段进行回调。BeanPostProcessor 不仅被 Spring 框架使用,同时作为面向用户的扩展接口,允许开发者进行自定义的操作。BeanPostProcessor 的出现,极大地丰富了 Spring 容器的操作空间,很多重要的功能都是由该组件完成的。

3.2 核心 API

BeanPostProcessor 接口定义了在 Bean 的初始化前后进行处理的方法,也就是说该接口的调用时机是在初始化阶段,目前处于填充对象的流程,我们将在之后的对象的初始化流程介绍具体的用法。

  • postProcessBeforeInitialization 方法:在对象初始化前执行
  • postProcessAfterInitialization 方法:在对象初始化后执行
public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName);
    Object postProcessAfterInitialization(Object bean, String beanName);
}

本节我们关心的是它的子接口 InstantiationAwareBeanPostProcessor,从类名可以看到,该接口的关注点是对象的实例化过程。

  • postProcessPropertyValues 方法的调用时机是在填充对象的过程中。
  • postProcessBeforeInstantiation 方法的调用时机是在创建对象之前,尝试返回一个代理对象。
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
    //完成定制的依赖注入和依赖检查,比如@Autowired、@Resource、@PersistContext
    void postProcessPropertyValues(Object bean, String beanName);
    //在实例化对象之前调用,返回的可能是一个代理
    Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
}

AutowiredAnnotationBeanPostProcessor 作为 InstantiationAwareBeanPostProcessor 接口的实现类,通过注解声明的方式提供自动装配的功能。

  • autowiredAnnotationTypes 字段表示支持自动装配的注解集合
  • injectionMetadataCache 字段的作用是缓存已解析的注入元数据

在构造方法中,默认添加了三个支持自动装配的注解,其中 @Autowired@Value 是 Spring 框架定义的,@Inject 注解是 JDK 定义的。由于 @Inject 注解所属的 javax.inject 是一个扩展包,应用程序可能不会引用该包,因此只能尝试获取该注解,有可能不存在。

public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, PriorityOrdered {

    private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
    private final Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256);

    public AutowiredAnnotationBeanPostProcessor(){
        this.autowiredAnnotationTypes.add(Autowired.class);
        this.autowiredAnnotationTypes.add(Value.class);

        //兼容@Inject注解
        try {
            this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
                    ClassUtils.forName("javax.inject.Inject", getClass().getClassLoader()));
        }catch (ClassNotFoundException ex) {}
    }
}

3.3 相关调整

ConfigurableBeanFactory 接口定义了与 BeanPostProcessor 有关的方法,AbstractBeanFactory 类的 beanPostProcessors 字段用于缓存 BeanPostProcessor 组件。

public interface ConfigurableBeanFactory {
    void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
    int getBeanPostProcessorCount();
}

public abstract class AbstractBeanFactory {
    private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();
}

4. createBean 方法

4.1 实例化前创建代理

回到 AbstractAutowireCapableBeanFactorycreateBean 方法,在真正创建对象之前,提供一个机会来尝试获取代理对象。如果代理对象存在,则不执行 doCreateBean 方法。但这并不是创建代理对象的主流方式,相关内容将在第二章 aop 模块中介绍,仅了解。

//所属类[cn.stimd.spring.beans.factory.support.AbstractAutowireCapableBeanFactory]
//创建Bean的流程
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd) {
    //尝试创建代理对象
    Object bean = applyBeanPostProcessorsBeforeInstantiation(beanName, mbdToUse);
    if (bean != null) {
        return bean;
    }

    return doCreateBean(beanName, mbdToUse);
}

4.2 填充对象

接下来是 doCreateBean 方法的调整,执行完第一步之后,得到了一个空的对象,在第三步填充对象中设置若干属性,使对象变得可用。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
    //1. Bean的实例化(略)
    //2. 提前暴露Bean以解决循环依赖(TODO)

    //3. 填充对象
    populateBean(beanName, mbd, instanceWrapper);
    //4. 初始化(TODO)
}

populateBean 方法实现了自动装配和属性访问的功能,其中属性访问已经详细介绍过了,我们重点关注自动装配是如何实现的。首先遍历容器中的 BeanPostProcessor 集合,找到 InstantiationAwareBeanPostProcessor 接口的实现类,调用 postProcessPropertyValues 方法来处理属性值。

//填充属性
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
    //1. 自动装配,处理@Autowired、@Value、@Inject等注解
    if(!mbd.isSynthetic()){
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                ibp.postProcessPropertyValues(bw.getWrappedInstance(), beanName);
            }
        }
    }

    //2. 属性访问,通过BeanWrapper为字段赋值
    PropertyValues pvs = mbd.getPropertyValues();
    if (pvs != null) {
        bw.setPropertyValues(pvs);
    }
}

5. 自动装配

5.1 概述

自动装配的流程分为两个阶段,我们结合时序图来分析第三步和第四步。第一阶段对注入目标进行解析。AutowiredAnnotationBeanPostProcessor 检查对象的字段或方法是否声明了 @Value@Autowired 等注解,如果存在,则将注入目标和依赖信息包装成 InjectionMetadataDependencyDescriptor。第二阶段寻找依赖项,并赋给注入目标。这一过程是由 Spring 容器完成的。这一点也很好理解,因为 Spring 容器掌握着所有的资源,便于寻找依赖项。

在这里插入图片描述

5.2 注入目标解析

AutowiredAnnotationBeanPostProcessor 实现了 postProcessPropertyValues 方法,完成了自动装配的流程。该方法可以分为两步,首先获取注解元数据,所有声明了 @Autowired 等注解的字段和方法都被包装成 InjectionMetadata。然后执行注入操作,遍历所有的 InjectedElement,依次进行依赖注入。

@Override
public void postProcessPropertyValues(Object bean, String beanName) {
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass());
    metadata.inject(bean, beanName);
}

第一步,寻找自动装配的元数据。findAutowiringMetadata 方法主要对缓存进行处理,真正的构建工作是由 buildAutowiringMetadata 方法完成的。

//寻找自动装配的元数据
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz) {
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    //如果缓存中没有InjectionMetadata,则构建新的实例
    if(metadata == null){
        metadata = buildAutowiringMetadata(clazz);
        this.injectionMetadataCache.put(cacheKey, metadata);
    }
    return metadata;
}

do...while 的循环中遍历对象及其父类,检查字段或者方法上是否声明了有关依赖注入的注解,具体流程如下:

  1. 调用 ReflectionUtils 工具类的 doWithLocalFields 方法,以反射的方式得到当前对象的所有字段和方法
  2. 调用 findAutowiredAnnotation 方法检查字段上是否声明了相关注解,如果存在,则包装成 AutowiredFieldElement 实例,并添加到集合中
  3. 继续检查方法上是否声明了相关等注解,如果存在,则包装成 AutowiredMethodElement 实例,并添加到集合中(代码略)
  4. 构建 InjectionMetadata 对象,返回注解元数据
//构建自动装配的元数据
public InjectionMetadata buildAutowiringMetadata(final Class<?> clazz){
    LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
    Class<?> targetClass = clazz;

    //遍历Bean及其父类上的字段和方法
    do {
        //检查字段上是否有@AutoWired、@Inject等注解
        ReflectionUtils.doWithLocalFields(targetClass, field -> {
            AnnotationAttributes attrs = findAutowiredAnnotation(field);
            if(attrs != null){
                boolean required = determineRequiredStatus(attrs);
                elements.add(new AutowiredFieldElement(field, required));
            }
        });

        //检查方法上是否有@AutoWired、@Inject等注解(略)

        targetClass = targetClass.getSuperclass();
    }
    while(targetClass != null & targetClass != Object.class);
    return new InjectionMetadata(clazz, elements);
}

第二步,得到 InjectionMetadata 对象之后,执行注入操作。inject 方法的逻辑很简单,将真正的注入工作委托给 injectedElements 集合中的元素。

public class InjectionMetadata {

    public void inject(Object target, String beanName) throws Throwable {
        for (InjectedElement element : this.injectedElements) {
            element.inject(target, beanName);
        }
    }
}

注入目标分为字段和方法,两者的实现基本一致,我们以 AutowiredFieldElement 为例说明。inject 方法分为三步,第二步依赖解析最为关键。

  1. 准备工作,需要创建一个 DependencyDescriptor 对象,包含了依赖的相关信息
  2. 调用 AutowireCapableBeanFactory 接口的 resolveDependency 方法对依赖进行解析,返回结果可能是一个对象,也可能是基本类型(字符串等)
  3. 通过反射的方式将解析后的值赋给字段
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement{

    @Override
    protected void inject(Object bean, String beanName) throws Throwable{
        Field field = (Field) this.member;
        Object value;

        //创建依赖描述符
        DependencyDescriptor desc= new DependencyDescriptor(field, this.required);
        desc.setContainingClass(bean.getClass());

        //对依赖进行解析,如果Bean不存在则创建并加入到IOC容器中
        value = beanFactory.resolveDependency(desc, beanName);

        //使用反射为字段赋值
        if(value != null){
            ReflectionUtils.makeAccessible(field);
            field.set(bean, value);
        }
    }
}

5.3 依赖解析

从上述代码可以看到,AutowiredAnnotationBeanPostProcessor 负责对单例进行分析,找出所有需要注入的元素,最核心的依赖解析交还给 Spring 容器处理。这一点也很好理解,因为 Spring 容器掌握着所有的资源,便于寻找符合条件的依赖项。AutowireCapableBeanFactory 接口声明了 resolveDependency 方法,负责寻找依赖项。

public interface AutowireCapableBeanFactory extends BeanFactory {
    Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName) throws BeansException;
}

DefaultListableBeanFactory 类实现了 resolveDependency 方法,实际上 resolveDependency 方法只是做了一些外围工作,具体流程是由 doResolveDependency 方法完成的。该方法的逻辑比较复杂,我们先来看一下总体流程,分为以下三步:

  1. 环境变量的解析,也就是处理 @Value 注解
  2. 集合类型的依赖,比如 List<T>
  3. 单一对象依赖,这是依赖解析的核心,因为集合类型的依赖可以分解成单一类型依赖
private Object doResolveDependency(DependencyDescriptor descriptor){
    //1. 环境变量解析
    //2. 集合类型依赖
    //3. 单一对象依赖
}

6. 环境变量解析

6.1 概述

所谓环境变量是指应用程序在运行过程中所依赖的参数信息,它们来自操作系统、配置文件等多种途径。Spring 核心包提供了 Environment 接口对环境变量进行抽象。我们经常需要引用某些参数,常规做法是获取 Environment 对象并调用 getProperty 方法。此外,Spring 提供了 @Value 注解简化这一操作,在字段上声明该注解,由 Spring 容器将对应的参数注入到字段上。

6.2 代码实现

回到 DefaultListableBeanFactory 类的 doResolveDependency 方法,第一步就是环境变量的解析,也就是处理 @Value 注解。整个过程比较简单,分为以下三步:

  • 获取 @Value 注解上的 value 属性值,格式为 ${xxx}
  • 解析占位字符串对应的参数,具体逻辑由 StringValueResolver 接口的实现类完成
  • 进行类型转换并返回
//所属类[cn.stimd.spring.beans.factory.support.DefaultListableBeanFactory]
//依赖解析
private Object doResolveDependency(DependencyDescriptor descriptor){
    //1. 处理@Value注解
    //获取注解上的value属性值
    Object value = this.autowireCandidateResolver.getSuggestedValue(descriptor);
    if (value != null) {
        if(value instanceof String){
            //使用StringValueResolver解析占位符参数
            value = resolveEmbeddedValue((String) value);
        }
        //类型转换
        return getTypeConverter().convertIfNecessary(value, descriptor.getDependencyType(), descriptor.getField());
    }
    ...
}

这里用到了 AutowireCandidateResolverStringValueResolver 两个组件。需要注意的是,Spring 没有直接提供 StringValueResolver 接口的实现类,而是通过创建匿名类的方式,将参数占位符的解析交给 Environment 对象处理。示例代码如下:

//示例代码:添加一个StringValueResolver,用于解析环境变量
public void addStringValueResolver() {
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    StandardEnvironment environment = new StandardEnvironment();

    //添加字符串值解析器
    factory.addEmbeddedValueResolver(new StringValueResolver() {
        @Override
        public String resolveStringValue(String strVal) {
            //委托给环境变量处理
            return environment.resolvePlaceholders(strVal);
        }
    });
}

6.2 AutowireCandidateResolver

AutowireCandidateResolver 接口的作用是对候选项进行解析,环境变量的解析是通过 getSuggestedValue 方法完成的。

  • isAutowireCandidate 方法:判断指定 BeanDefinition 是否为依赖项
  • getSuggestedValue 方法:获取建议的值,该方法是为 @Value 注解量身定做的
  • isRequired 方法:判断一个依赖项是否必须,如果为 true,当依赖项不存在时抛出异常
public interface AutowireCandidateResolver {
    boolean isAutowireCandidate(BeanDefinitionHolder holder, DependencyDescriptor descriptor);
    Object getSuggestedValue(DependencyDescriptor descriptor);
    boolean isRequired(DependencyDescriptor descriptor);
}

DefaultListableBeanFactory 持有一个 AutowireCandidateResolver 类型的字段,使用 QualifierAnnotationAutowireCandidateResolver 作为默认实现。

public class DefaultListableBeanFactory {
    private AutowireCandidateResolver autowireCandidateResolver = new QualifierAnnotationAutowireCandidateResolver();
}

6.3 StringValueResolver

StringValueResolver 接口是 Spring 核心包提供的组件,作用是对字符串类型的值进行解析。ConfigurableBeanFactory 接口定义了管理 StringValueResolver 的相关方法,AbstractBeanFactory 则持有一个 StringValueResolver 的集合。

public interface ConfigurableBeanFactory {
    void addEmbeddedValueResolver(StringValueResolver valueResolver);
    boolean hasEmbeddedValueResolver();
    String resolveEmbeddedValue(String value);
}


public abstract class AbstractBeanFactory {
    //字符串值解析器的缓存
    private final List<StringValueResolver> embeddedValueResolvers = new LinkedList<>();
}

7. 测试

7.1 属性访问

之前我们单独测试过属性访问的功能,现在是把 BeanWrapper 整合到在 Spring 容器创建对象的整个流程中进行考察。

public class AutowireConfig {
    private String name;

    //getter/setter方法略
}

在测试方法中,首先创建 RootBeanDefinition 对象,然后添加属性值。接下来创建了 BeanFactory 的实例,并将 RootBeanDefinition 注册到容器中。最后调用 getBean 方法,在创建目标对象的过程中,完成了属性访问的操作。

@Test
public void testPropertyValues(){
    RootBeanDefinition beanDefinition = new RootBeanDefinition(AutowireConfig.class);
    beanDefinition.getPropertyValues().addPropertyValue("name", "Stimd");

    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    factory.registerBeanDefinition("autowireConfig", beanDefinition);

    AutowireConfig autowireConfig = factory.getBean("autowireConfig", AutowireConfig.class);
    System.out.println("PropertyValues注入测试:name的值为" + autowireConfig.getName());
}

从测试结果可以看到,BeanDefinition 中预先设置的属性值赋给了 BeanFactory 创建的对象,符合预期。

PropertyValues注入测试:name的值为Stimd

7.2 环境变量解析

在测试类中,profile 属性有着特殊的含义,它代表了应用程序的运行环境,常见的有开发、测试、生产等环境。不同的运行环境下可以进行相应的设置,比如连接不同的数据库。

public class AutowireConfig {
    @Value("${spring.profiles.active}")
    private String profile;

    public String getProfile() {
        return this.profile;
    }
}

测试方法稍微有点复杂,可以分为以下几步:

  1. 创建 BeanFactory 实例,并将 AutowiredAnnotationBeanPostProcessor 注册到容器中。
  2. 模拟环境变量,创建 StandardEnvironment 对象,并添加一个配置项。
  3. 创建匿名的 StringValueResolver 对象,resolveStringValue 方法的逻辑是从环境变量中检索指定的 key 所对应的 value。
  4. 创建 RootBeanDefinition 对象,并注册到容器中,然后调用 getBean 方法,在创建对象的过程中完成环境变量的解析。
//测试方法
@Test
public void testResolveEnvironment(){
    //添加解析@Value的BeanPostProcessor
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
    processor.setBeanFactory(factory);
    factory.addBeanPostProcessor(processor);

    //模拟环境变量
    StandardEnvironment environment = new StandardEnvironment();
    environment.getSystemProperties().put("spring.profiles.active", "dev");

    //添加StringValueResolver
    factory.addEmbeddedValueResolver(new StringValueResolver() {
        @Override
        public String resolveStringValue(String strVal) {
            return environment.resolvePlaceholders(strVal);
        }
    });

    factory.registerBeanDefinition("autowireConfig", new RootBeanDefinition(AutowireConfig.class));
    AutowireConfig autowireConfig = factory.getBean(AutowireConfig.class);
    System.out.println("@Value测试,profile的值为" + autowireConfig.getProfile());
}

从测试结果可以看到,环境变量对象中的属性值,通过 @Value 注解读取了出来,符合预期。

环境变量解析测试,profile的值为dev

8. 总结

本节介绍了填充对象的基本流程,包括两个方面,一是属性访问,通过 PropertyAccessor 组件将属性值赋给对象。二是自动装配,为声明了指定注解的字段或方法寻找合适的依赖项。二者的区别在于,属性访问的数据通常是事先指定的,自动装配的依赖项来源于 Spring 容器所管理的资源,包括单例和环境变量等。之前已经讲解过属性访问,本节重点关注自动装配的实现。

自动装配的流程分为两步,首先对注入目标进行解析,具体工作是由 AutowiredAnnotationBeanPostProcessor 组件完成的,负责解析一个类的注入信息。其次,对依赖项进行解析,并注入到对应的目标上。寻找依赖项的过程是由 Spring 容器完成的,主要处理了两种情况:

  • 环境变量解析:负责处理 @Value 注解,通过 StringValueResolver 组件寻找环境变量中指定的属性。
  • 对象解析:负责处理 @Autowired@Inject 等注解,在 Spring 容器中寻找符合条件的单例。

本节实现了环境变量的解析,完成了自动装配的基本流程。虽然环境变量解析仅涉及字段注入,但和 setter 方法注入的流程是一样的,只是在具体注入的细节上有所差别。因此,有了环境变量解析的基础,对象解析只需要处理第二阶段,即依赖解析的流程即可

9. 项目信息

新增修改一览,新增(13),修改(5)。

beans
└─ src
   ├─ main
   │  └─ java
   │     └─ cn.stimd.spring.beans
   │        └─ factory
   │           ├─ annotation
   │           │  ├─ Autowired.java (+)
   │           │  ├─ AutowiredAnnotationBeanPostProcessor.java (+)
   │           │  ├─ InjectionMetadata.java (+)
   │           │  ├─ Qualifier.java (+)
   │           │  └─ Value.java (+)
   │           ├─ config
   │           │  ├─ AutowireCapableBeanFactory.java (*)
   │           │  ├─ BeanDefinitionHolder.java (+)
   │           │  ├─ BeanPostProcessor.java (+)
   │           │  ├─ ConfigurableBeanFactory.java (*)
   │           │  ├─ DependencyDescriptor.java (+)
   │           │  └─ InstantiationAwareBeanPostProcessor.java (+)
   │           └─ support
   │              ├─ AbstractAutowireCapableBeanFactory.java (*)
   │              ├─ AbstractBeanFactory.java (*)
   │              ├─ AutowireCandidateResolver.java (+)
   │              ├─ DefaultListableBeanFactory.java (*)
   │              └─ QualifierAnnotationAutowireCandidateResolver.java (+)
   └─ test
      └─ java
         └─ beans
            └─ autowire
               ├─ AutowireConfig.java (+)
               └─ AutowireTest.java (+)

注:+号表示新增、*表示修改
  • 项目地址:https://gitee.com/stimd/spring-wheel

  • 本节分支:https://gitee.com/stimd/spring-wheel/tree/chapter1-6

注:项目的 master 分支会跟随教程的进度不断更新,如果想查看某一节的代码,请选择对应小节的分支代码。


关注公众号【Java编程探微】,加群一起讨论。
在这里插入图片描述

  • 41
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值