首页 > 其他分享 >【重写SpringFramework】第一章beans模块:填充对象(chapter 1-6)

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

时间:2024-06-19 22:32:01浏览次数:28  
标签:chapter 对象 SpringFramework 环境变量 beans 注解 解析 public 注入

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编程探微】,加群一起讨论。
在这里插入图片描述

标签:chapter,对象,SpringFramework,环境变量,beans,注解,解析,public,注入
From: https://blog.csdn.net/Stimd/article/details/139612506

相关文章

  • 【重写SpringFramework】第一章beans模块:Bean的销毁(chapter 1-9)
    1.前言Bean的生命周期包括初始化和销毁操作,上节介绍了Bean初始化流程,本节来看Bean的销毁流程是如何实现的。在实际应用中,绝大多数对象并不需要执行销毁操作,但某些对象本身管理着一定的资源。当Spring容器关闭时,所有的对象都会被虚拟机回收。在此之前,这些特殊的对象......
  • 【重写SpringFramework】第一章beans模块:Bean的初始化(chapter 1-8)
    1.前言前边我们介绍了创建实例和填充对象的流程,这是整个创建流程最重要的工作。有时候用户需要对Bean进行自定义的操作,这一过程称为初始化。此外,还有一些比较特殊的对象,本身管理着一定的资源,当对象销毁时需要释放这些资源,因此我们还需要相应的销毁操作。初始化和销毁操作......
  • jmeter中beanshell处理器
    Jmeter工具中存在BeanShell取样器,BeanShell预处理程序,BeanShell后置处理程序,那么这次详细整理下BeanShell在JMeter的作用以及案例。beanshell是什么:BeanShell是一个小型嵌入式Java源代码解释器,能够动态地执行java代码,不需要提前编译JMeter在它的BeanShell中内置了变量,用户......
  • [TinyRenderer] Chapter1 p3 Line
    (注:本小节不是对划线算法事无巨细的证明,如果你需要更加系统的学习,请跳转至文末的参考部分)如果你是一名曾经学习过图形学基础的学生,那么你一定对画线算法稔熟于心,中点划线算法,Bresenham算法。其中,现代光栅化器中使用最多的就是Bresenham算法,它以去除了除法和浮点运算而著称。但如......
  • Chapter1 p2 vec
    在上一小节中,我们完成了对BMPImage类的构建,成功实现了我们这个小小引擎的图像输出功能。你已经完成了图像输出了,接着就开始路径追踪吧。。。开个玩笑XD对于曾经学习过一些图形学经典教材的人来说,下一步应当开始着手于画线算法了,但对于本文来说,肯定是要走一些不走寻常路的。所......
  • 【jmeter】使用beanshell simpler测试redis性能
    一、场景   由于redisdataset支持的类型有限,所以采取使用beanshellsampler 二、安装jedis包https://mvnrepository.com/artifact/redis.clients/jedis 三、添加BeanShellSampler添加脚本importjava.util.Map;importredis.clients.jedis.Jedis;importorg.a......
  • [TinyRenderer] Chapter1 p1 Output Image
    由于本文章是对TinyRenderer的模仿,所以并不打算引入外部库。那么我们第一步需要解决的就是图形输出的问题,毕竟,如果连渲染的结果都看不到,那还叫什么Renderer嘛。由于不引入外部库,所以选择输出的图片格式应该越简单越好,各种位图就成为了我们的首选。这里我们选择了生态较好的bmp......
  • java: 错误: 无效的源发行版:17解决方法、java: 无法访问org.springframework.web.bind
    可能的问题与解决方法java:错误:无效的源发行版:17(18)解决方法遇到这种问题大概率是版本以及配置出现问题,可以试试看按下面的步骤排除检查先检查自己的Java版本去到项目结构看Java配置是否正确这里以我的Java1.8举例主要是修改SDK为正确对应检查依赖项是否正确(i......
  • 禹晶、肖创柏、廖庆敏《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》Chap
    禹晶、肖创柏、廖庆敏《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》Chapter7插图......
  • AoPS - Chapter 14 Inequalities
    TODO:全文的\(\sum\)与\(\prod\)在无特殊说明时默认为\(i=1,2,\cdots,n\)。平凡的不等式\(\forallx\in\mathbbR\),\[x^2\ge0\]例1Example证明:\(\forallx\in\mathbbR\),\(\cos2x+\sin^2x\ge0\)。Solution由\(\cos\)二倍角公式可得:\[\begi......