【Spring源码分析】Bean的元数据和一些Spring的工具

Bean的元数据和一些Spring工具

  • 一、BeanDefinition
    • 1、认识 BeanDifinition
    • 2、AbstractBeanDefinition
    • 3、GenericBeanDefinition
    • 测试
  • 二、BeanDefinition 注册器
  • 三、加载BeanDefinition
  • 四、包扫描过程分析
    • 包扫描过程总结
  • 五、内省 API
  • 六、反射工具
    • Bean 的创建
    • 批量构造
    • ResolvableType
  • 七、类型转换
    • 1、转换服务
    • 2、自定义转换器
    • 3、转换器工厂(ConverterFactory)
    • 4、转换器注册器源码
      • 为什么一个类型转换成另一个类型还需要多种转换器?
      • 存储在 Map 中的 Hash 问题
    • 5、注意:是类型转换器
  • 八、资源获取
    • 1、内置的 Resource 的实现
    • 2、classpath 前缀
    • 3、PathMatchingResourcePatternResolver
  • 九、Spring Event
    • SimpleApplicationEventMulticaster源码分析

参考文献:
Spring源码-基础

一、BeanDefinition

BeanDefinition 为我们统一了 bean 的元数据,bean 的元数据就是指我们使用 xml 或者注解进行配置的数据,我们的 spring 容器启动之前的第一步就是加载配置数据,这些元数据会被加载到内存中以一个个 beanDefinition 的形式保存在一个 map 中。

一个 BeanDifinition 大概保存了以下信息:

  • 定义了 id、别名与Bean的队友关系(BeanDefinitionHolder)
  • 具体的工厂方法(Class 类型),包括工厂方法的返回类型,工厂方法的 Method 对象。
  • 构造函数、构造函数形参类型
  • Bean 的 class 对象
  • 作用范围、是否懒加载等等

结构图如下:

【Spring源码分析】Bean的元数据和一些Spring的工具_第1张图片

beanDefinition来源不同可能会有不同实现,目前我们最常用的实现就是GenericBeanDefinition这个实现类。

小知识:Generic(一般的,通用的),几乎所有以这个单词打头的实现,都是spring的通用实现。

1、认识 BeanDifinition

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	// 常量标志一个bean的作用范围
	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

    // 设置父BeanDefinition,可以只对有父子关系的bean
	void setParentName(@Nullable String parentName);
	String getParentName();
    // bean的类的全限定名
	void setBeanClassName(@Nullable String beanClassName);
	String getBeanClassName();

	void setScope(@Nullable String scope);
	String getScope();

	void setLazyInit(boolean lazyInit);
	boolean isLazyInit();

    // 设置依赖性,被依赖的bean会优先创建
	void setDependsOn(@Nullable String... dependsOn);
	String[] getDependsOn();
	// 是否允许自动装配
	void setAutowireCandidate(boolean autowireCandidate);
	boolean isAutowireCandidate();

    // 设置是否主要bean
	void setPrimary(boolean primary);
	boolean isPrimary();

	// 工厂bean和工厂方法
	void setFactoryBeanName(@Nullable String factoryBeanName);
	String getFactoryBeanName();
	void setFactoryMethodName(@Nullable String factoryMethodName);
	String getFactoryMethodName();

	ConstructorArgumentValues getConstructorArgumentValues();
	default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}


    // 使用setter注入时的key-value对,都保存在这里
	MutablePropertyValues getPropertyValues();
	default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}

	// @since 5.1初始化方法和销毁方法
	void setInitMethodName(@Nullable String initMethodName);
	String getInitMethodName();
	void setDestroyMethodName(@Nullable String destroyMethodName);
	String getDestroyMethodName();

	// 为bean设置角色
	void setRole(int role);
	int getRole();

	// bean的描述
	void setDescription(@Nullable String description);
	String getDescription();

	// 返回此bean定义的可解析类型,基于bean类或其他特定元数据。
    // 这通常在运行时合并bean定义上完全解决但不一定是在配置时定义实例上。
	ResolvableType getResolvableType();
	boolean isSingleton();
	boolean isPrototype();
	boolean isAbstract();
}

2、AbstractBeanDefinition

该类对通用核心方法完成了实现(这也是抽象类的作用),同时对一些成员变量提供了默认值

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
		implements BeanDefinition, Cloneable {

	// 定义一些常量
	public static final String SCOPE_DEFAULT = "";
	public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
	public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
	public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
    // ...还有很多

	// 初始化默认值
	private volatile Object beanClass;
	private String scope = SCOPE_DEFAULT
	private boolean autowireCandidate = true;
	private boolean primary = false;
	// ...还有很多

	// 构造器
	protected AbstractBeanDefinition() {
		this(null, null);
	}

	// 指定构造器参数和属性参数
	protected AbstractBeanDefinition(@Nullable ConstructorArgumentValues cargs, @Nullable MutablePropertyValues pvs) {
		this.constructorArgumentValues = cargs;
		this.propertyValues = pvs;
	}

	// 使用深拷贝创建一个新的
	protected AbstractBeanDefinition(BeanDefinition original) {
		
	}


	// 复制一个bean的定义到当前bean,通常父子bean合并时可用
	public void overrideFrom(BeanDefinition other) {
		
	}

	// ...此处省略其他的方法实现
}

3、GenericBeanDefinition

该类实现比较简单,提供了设置父子关系和构建实例的方法,该类及其子类是目前版本使用最多的BeanDifinition。

ublic class GenericBeanDefinition extends AbstractBeanDefinition {

   @Nullable
   private String parentName;

   public GenericBeanDefinition() {
      super();
   }

   // 通过深拷贝创建一个bean
   public GenericBeanDefinition(BeanDefinition original) {
      super(original);
   }

   @Override
   public void setParentName(@Nullable String parentName) {
      this.parentName = parentName;
   }

   @Override
   @Nullable
   public String getParentName() {
      return this.parentName;
   }

   @Override
   public AbstractBeanDefinition cloneBeanDefinition() {
      return new GenericBeanDefinition(this);
   }
}

测试

    @Test
    public void testGenericBeanDefinition(){
        GenericBeanDefinition definition = new GenericBeanDefinition();
        definition.setBeanClassName("com.example.springanalyze.bean.MyTest");
        definition.setScope(AbstractBeanDefinition.SCOPE_PROTOTYPE);
        definition.setInitMethodName("init");
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue("age",19);
        definition.setPropertyValues(propertyValues);
        System.out.println(definition);
    }
@Test
public void testRootBeanDefinition() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    RootBeanDefinition dog = new RootBeanDefinition();
    dog.setBeanClassName("com.ydlclass.Dog");
    BeanMetadataAttribute color = new BeanMetadataAttribute("color","white");
    BeanMetadataAttribute age = new BeanMetadataAttribute("age","3");
    dog.addMetadataAttribute(color);
    dog.addMetadataAttribute(age);

    // 子Definition的创建需要依赖父Definition
    ChildBeanDefinition teddy = new ChildBeanDefinition("dog");
    teddy.setBeanClassName("com.ydlclass.TeddyDog");
    BeanMetadataAttribute name = new BeanMetadataAttribute("name","doudou");
    teddy.addMetadataAttribute(name);
}

GenericBeanDefinition 在很多场景下可以替换以下的内容,但是由于历史等原因,RootBeanDefinition 依旧存在而且很重要,后期的归一处理还是要将不同的 BeanDefinition 转换或合至成一个 RootBeanDefinition.

  • RootBeanDefinition不能有父BeanDefinition,可以和ChildBeanDefinition配合使用构建父子关系(bean是可以继承的)。

  • 目前最常用的BeanDefinition是GenericBeanDefinition及其子类的实现,GenericBeanDefinition很强大,也可以很轻松的独立的构建父子关系。

二、BeanDefinition 注册器

有了统一标准的元数据之后,我们就可以统一管理,这就需要一个容器去存储,当然我们可以使用 map 这样的集合类,当然 spring 差不多就是这样做的。他为我们提供了一个接口 BeanDefinitionRegistry。只要实现了这个接口,就会拥有注册 beanDefinition 的能力。

就好像 mybatis 中类型的别名(TypeAilasRegistry)和类型处理(TypeHandleRegistry)一样,需要注册器进行全局注册

接口代码如下:

public interface BeanDefinitionRegistry extends AliasRegistry {

	// 注册一个BeanDefinition
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException;

	void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	boolean containsBeanDefinition(String beanName);

	String[] getBeanDefinitionNames();

	int getBeanDefinitionCount();

	boolean isBeanNameInUse(String beanName);

}

底层其实就是通过 Map 对 BeanDefinition 的注册操作进行处理的,且默认容量设置为了 256,即256*3/4=196 的时候进行第一次扩容。

在这里插入图片描述

    @Test
    public void testRootBeanDefinition() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        // 定义一个注册器
        BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();

        RootBeanDefinition dog = new RootBeanDefinition();
        dog.setBeanClassName("com.ydlclass.Dog");
        BeanMetadataAttribute color = new BeanMetadataAttribute("color", "white");
        BeanMetadataAttribute age = new BeanMetadataAttribute("age", "3");
        dog.addMetadataAttribute(color);
        dog.addMetadataAttribute(age);
        registry.registerBeanDefinition("dog", dog);

        // 子Definition的创建需要依赖父Definition
        ChildBeanDefinition teddy = new ChildBeanDefinition("dog");
        teddy.setBeanClassName("com.ydlclass.TeddyDog");
        BeanMetadataAttribute name = new BeanMetadataAttribute("name", "doudou");
        teddy.addMetadataAttribute(name);
        registry.registerBeanDefinition("teddy", teddy);
    }

三、加载BeanDefinition

当然我们不可能为每一个类手动去编写与之对应的 BeanDefinition,元数据还是要从xml或者从配置类中获取的,spring 为我们提供了对应的工具。(对比 Mybatis 也是一样的,从mybatis核心配置xml里去得到你需要扫描的别名和类型处理器(当然类型处理器它自己内置了,如果自己自定义了的需要自己配置然后让它加载))。

1、 读取 xml 配置文件

该类通过解析 xml 完成 BeanDefinition 的读取,并且将它解析的 BeanDefinition 注册倒一个注册器中:

    @Test
    public void testRegistryByXml() {
        BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
        // 创建一个 xml 的 reader
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
        reader.loadBeanDefinitions("classpath:spring-test.xml");

        System.out.println(registry);
    }

2、加载带注解的 bean

    @Test
    public void testRegistryByXml() {
        BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
        // 创建一个 xml 的 reader
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
        reader.loadBeanDefinitions("classpath:spring-test.xml");

        System.out.println(registry);
    }

3、读取配置类

ConfigurationClassBeanDefinitionReader 可以读取配置类,只是这个类不让我们使用,该类提供了如下方法 :

private void loadBeanDefinitionsForConfigurationClass
private void registerBeanDefinitionForImportedConfigurationClass
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod)

他会将读取的元数据封装成为:ConfigurationClassBeanDefinition.

4、类路径扫描

    @Test
    public void testRegistryByScanner(){
        BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();

        // 通过扫描包的方式去注册 BeanDefinition
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
        scanner.scan("com.example.springanalyze.bean");

        System.out.println(registry);
    }

四、包扫描过程分析

无论是扫包还是其他方式,我们解析一个类无非有以下俩种方式:

  • 加载一个类倒内存,获取 Class 对象,通过反射获取到元数据;
  • 直接操纵字节码文件(.class),读取字节码内的元数据。

毫无疑问 spring 选择了第二种(使用 ASM 技术

  • 首先:第二种性能要优于第一种;

  • 其次:第一种会将扫描的类全部加载到堆内存,无疑会浪费空间,增加 gc 次数,第二种可以根据元数据按需加载。

  • doScan 核心代码分析:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // BeanDefinitionHolder持有 BeanDefinition实例和名字以及别名
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        // 这里是具体的扫描过程,找出全部符合过滤器要求的BeanDefinition
        // 返回的BeanDefinition的实际类型为ScannedGenericBeanDefinition
        // 实际是调用scanCandidateComponents方法
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        // 根据不同的bean类型做统一处理,如附默认值等
        // 因为有些数据我们并没有配置,需要这里做默认处理
        
        for (BeanDefinition candidate : candidates) {
            // 如果存在,则解析@Scope注解,为候选bean设置代理的方式ScopedProxyMode,XML属性也能配置:scope-resolver、scoped-proxy,可以指定代理方式jdk或者cglib
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            
            // 首先从注解中获取bean的名字,如果没有
            // 使用beanName生成器beanNameGenerator来生成beanName
            // 在注解中的bean的默认名称和xml中是不一致的
            // 注解中如果没有指定名字本质是通过ClassUtil 的 getShortName 方法获取的
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
  
            // 将进一步设置应用于给定的BeanDefinition,使用AbstractBeanDefinition的一些默认属性值
            //设置autowireCandidate属性,即XML的autowire-candidate属性,IoC学习的时候就见过该属性,默认为true,表示该bean支持成为自动注入候选bean
            if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            
            // 如果bean定义是AnnotatedBeanDefinition类型,ScannedGenericBeanDefinition同样属于AnnotatedBeanDefinition类型
            if (candidate instanceof AnnotatedBeanDefinition) {
 // 4 处理类上的其他通用注解:@Lazy, @Primary, @DependsOn, @Role, @Description
           AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            // 检查给定的 beanName,确定相应的bean 定义是否需要注册或与现有bean定义兼容
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                // 根据proxyMode属性的值,判断是否需要创建scope代理,一般都是不需要的
                definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

  • scanCandidateComponents (candidate:候选)方法进行核心源码分析:
    private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
        // 用 Set 去封装 BeanDefinition 进行返回
        Set<BeanDefinition> candidates = new LinkedHashSet();

        try {
            // 去解析出报名,然后拼接成包的解析路径:如:classpath*:com.test/**/*.class
            String var10000 = this.resolveBasePackage(basePackage);
            String packageSearchPath = "classpath*:" + var10000 + "/" + this.resourcePattern;
            // 通过上面拼接的要访问的路径然后获取对应 Resource 对象。
            Resource[] resources = this.getResourcePatternResolver().getResources(packageSearchPath);
            boolean traceEnabled = this.logger.isTraceEnabled();
            boolean debugEnabled = this.logger.isDebugEnabled();
            // 深拷贝一组用于后续处理
            Resource[] var7 = resources;
            int var8 = resources.length;

            for(int var9 = 0; var9 < var8; ++var9) { // 遍历获取到的 .class 对应的 resource 对象
                Resource resource = var7[var9];
                String filename = resource.getFilename();
                if (filename == null || !filename.contains("$$")) {

                    try {
                        // 通过 resource 去获取元数据
                        MetadataReader metadataReader = this.getMetadataReaderFactory().getMetadataReader(resource);
                        // 匹配,也就是判断是否能通过候选需要加载
                        // 检查读取到的类是否可以作为候选组件,即是否符合TypeFilter类型过滤器的要求
                // 使用IncludeFilter。就算目标类上没有@Component注解,它也会被扫描成为一个Bean
                // 使用ExcludeFilter,就算目标类上面有@Component注解也不会成为Bean
                       // 还有会判断是否通过 Confitional 注解,如果配置了的话
                        if (this.isCandidateComponent(metadataReader)) {
                            // 如果成功的话就把元数据封装成 ScannerGenericBeanDefinition
                            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                            sbd.setSource(resource);
                            // 这里的话还会再次进行判断,判断是否是独立的类,就是不是嵌套的类
                            // 当然静态内部类不一样,静态内部类是可以直接实例化的,这是源于加载阶段就。。。
                            if (this.isCandidateComponent((AnnotatedBeanDefinition)sbd)) {
                                if (debugEnabled) {
                                    this.logger.debug("Identified candidate component class: " + resource);
                                }

                                candidates.add(sbd);
                            } 
                    } catch (FileNotFoundException var14) {
                        if (traceEnabled) {
                            this.logger.trace("Ignored non-readable " + resource + ": " + var14.getMessage());
                        }
                    } catch (Throwable var15) {
                        throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, var15);
                    }
                }
            }

            return candidates;
        } catch (IOException var16) {
            throw new BeanDefinitionStoreException("I/O failure during classpath scanning", var16);
        }
    }

比如我在 Person 类上即用了 @Component 注解,也用了 @Conditional 注解,代码如下:

public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return false;
    }
}
@Component
@Data
@Conditional(MyCondition.class)
public class Person {

    private String name;

    private Integer age;

}

那么由于我配置的 Condition 是返回 false 的,即在上述的扫描中通过不了,所以会导致元数据注册不了,如下所示:

【Spring源码分析】Bean的元数据和一些Spring的工具_第2张图片

包扫描过程总结

  • 构建 ClassPathBeanDefinitionScanner 包扫描器对象;

  • 配置 @ComponentScan 注解及其属性配置,如:excludeFilters、includeFilters 配置.

    • 【Spring源码分析】Bean的元数据和一些Spring的工具_第3张图片
  • 调用 doScan 方法进行包扫描。

  • 获取扫描包下的所有 .class 文件对应的 Resource 对象。

  • 通过 ASM 技术获取字节码的文件信息。(此过程是没有经过类加载的,是直接用 ASM 技术处理的字节码)

  • 进行 filter + 条件注解(@Condictional)的判断;

  • 进行独立类、接口、抽象类、@Lookup的判断。

  • 判断生成的 BeanDefinition 是否重复;

  • 放入集合返回,然后处理默认值等。

【Spring源码分析】Bean的元数据和一些Spring的工具_第4张图片

五、内省 API

Java 中的内省(Introspection)是一种通过分析类的方法和属性来获取类的信息的机制。它允许在运行时检查类的属性和方法,并动态地调用它们。内省通常与Java的反射(Reflection)机制密切相关。

通过内省,可以在运行时获取类的属性(如名称、类型)和方法(如名称、参数等)的信息,并可以通过获取的信息来动态操作这些属性和方法。内省机制可以用来实现动态地设置和获取对象的属性值,以及动态地调用对象的方法。

内省的核心是 Introspector 类 和 PropertyDescriptor 类。Introspector 类用于获取一个类的 Bean 信息,而 PropertyDescriptor 类则用于描述一个 Bean 类的属性。

内省提供了一种方便的方式来操作对象的属性和方法,特别适用于在运行时根据条件动态地处理对象

【Spring源码分析】Bean的元数据和一些Spring的工具_第5张图片

  1. 获取到 BeanInfo 内省信息

    // 获取到 Person.class 中的 beaninfo,且不继承 Object 中的bean信息
    BeanInfo beanInfo = Introspector.getBeanInfo(Person.class,Object.class);

  2. 然后通过 BeanInfo 获取对应的 Descriptor

    【Spring源码分析】Bean的元数据和一些Spring的工具_第6张图片

  3. 然后通过 Descriptor 对象去获取对应的信息

    【Spring源码分析】Bean的元数据和一些Spring的工具_第7张图片

    @Test
    public void testIntrospector() throws IntrospectionException {
        // 获取到 Person.class 中的 beaninfo,且不继承 Object 中的bean信息
        BeanInfo beanInfo = Introspector.getBeanInfo(Person.class,Object.class);
        MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors();
        for (MethodDescriptor methodDescriptor : methodDescriptors) {
            String name = methodDescriptor.getName();
            System.out.println(name);
        }
    }

Spring 中的 BeanUtils 工具类就是通过内省实现的。它使用了 Java 的内省机制来获取和操作对象的属性,BeanUtils 提供了一系列静态方法,用于复制、合并、获取、设置对象的属性值等操作。

    @Test
    public void testBeanUtils() throws InvocationTargetException, IllegalAccessException {
        Person person = new Person();
        Method setAge = BeanUtils.findMethod(person.getClass(), "setAge", Integer.class);
        setAge.invoke(person,11);
        System.out.println(person);

        Person personF = new Person();
        BeanUtils.copyProperties(person,personF);
        System.out.println(personF);
    }
// Person(name=null, age=11)
// Person(name=null, age=11)

六、反射工具

Spring 和 Mybatis 一样,提供了自定封装的反射工具,针对原生 API 的复杂性,Spring同样进行了封装,让其使用起来更简单。

Bean 的创建

这个Spring提供的反射工具是 BeanWrapper,下面的例子展示了该类与 BeanDefinition 的配合使用:

    @Test
    public void testBeanWrapper() throws ClassNotFoundException {
        GenericBeanDefinition definition = new GenericBeanDefinition();
        definition.setBeanClassName("com.example.springanalyze.bean.Person");
        MutablePropertyValues multipartProperties = new MutablePropertyValues();
        multipartProperties.addPropertyValue("age",12);
        multipartProperties.addPropertyValue("name","傻逼");
        definition.setPropertyValues(multipartProperties);

        String className = definition.getBeanClassName();
        Class<?> aClass = Class.forName(className);

        BeanWrapper beanWrapper = new BeanWrapperImpl(aClass);
        beanWrapper.setPropertyValues(definition.getPropertyValues());
        Object obj = beanWrapper.getWrappedInstance();
        System.out.println(obj);
    }

批量构造

    @Test
    public void testBatchCreate() throws ClassNotFoundException {
        // 1、批量去加载 BeanDefinition 然后注册到 注册器中
        BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
        int cnt = reader.loadBeanDefinitions("classpath:spring-test.xml");
        log.info("加载的BeanDefinition的个数:{}", cnt);

        // 2、去遍历每个 beanDefinition
        String[] names = registry.getBeanDefinitionNames();
        for (String name : names) {
            BeanDefinition beanDefinition = registry.getBeanDefinition(name);
            String beanClassName = beanDefinition.getBeanClassName();
            Class<?> aClass = Class.forName(beanClassName);

            // 3、 通过 BeanWrapper 去实例化
            BeanWrapper beanWrapper = new BeanWrapperImpl(aClass);
            beanWrapper.setPropertyValues(beanDefinition.getPropertyValues());
            Object obj = beanWrapper.getWrappedInstance();
            System.out.println(obj);

        }

使用 XmlBeanDefinitionReader 去得到 BeanDefinition 集然后去遍历就会出现问题:

【Spring源码分析】Bean的元数据和一些Spring的工具_第8张图片

是说无法将一个 TypedStringValue 类型 转化成 int/Integer,没有合适的转化器。

这是由于去解析 xml 得到的数据是字符串的,直接注入属性值从而导致不匹配。

使用 ClassPathBeanDefinitionScanner 扫描器的话不会有这样的问题,它也不存在属性。

【Spring源码分析】Bean的元数据和一些Spring的工具_第9张图片

ResolvableType

该类可以封装Java类型,提供对超类类型、接口和泛型参数的访问,以及最终解析为类的能力,它能及其方便的简化对反射 api 的调用,在 Spring 的使用率非常高。

ResolvableType 可以从字段、方法参数、方法返回类型或类中获取,这个类上大多方法本身都会返回一个 ResolvableType,以便链式调用。

它提供了 类型、超类类型以及泛型参数,这也是注入所需要的。

    private Map<String,Object> map;

    private Integer mm;

    @Test
    public void testResolvableType() throws NoSuchFieldException {
        ResolvableType resolvableType = ResolvableType.forField(this.getClass().getDeclaredField("map"));
        System.out.println(resolvableType.getType());
//        System.out.println(resolvableType.getSuperType());
        System.out.println(resolvableType.asMap());
        System.out.println(resolvableType.getSource());
        System.out.println(resolvableType.getGenerics());

        ResolvableType resolvableType1 = ResolvableType.forField(this.getClass().getDeclaredField("mm"));
        System.out.println(resolvableType1.getType());
    }

就是它能够获取你设的任何类型,是一个获取类型的强大工具。

七、类型转换

从 xml 中搜集到的所有数据都是【字符串】,但是实际的类中的成员变量可能是数字,数组,集合,或者复杂的引用数据类型,所以 Spring 给我们提供了强大的转换服务(conversionService接口)

1、转换服务

ConversionService 接口很简单,可以根据原类型和目标类型进行判断是否可以进行转换,并执行转换:

public interface ConversionService {

	boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);

	boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);

	@Nullable
	<T> T convert(@Nullable Object source, Class<T> targetType);

	 // 将给定的{@code source}转换为指定的{@code targetType}。
	Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);

}

ConversionService 默认提供了实现:DefaultConversionService

【Spring源码分析】Bean的元数据和一些Spring的工具_第10张图片

看看DefaultConversationService的源码,更多核心的功能是在父类中实现的,在构造实例的时候,他会默认传入大量可用的转化器(和Mybatis映射类型处理一样,同样是通过类型注册器去管理):

public class DefaultConversionService extends GenericConversionService {

    // 这里添加 volatile 关键字,保证了指令的有序性
	@Nullable
	private static volatile DefaultConversionService sharedInstance;


	public DefaultConversionService() {
        // 添加大量的默认的转换器
		addDefaultConverters(this);
	}
    // 类似单例的获取方式
    // 双重校验实现懒汉式单例
	public static ConversionService getSharedInstance() {
		DefaultConversionService cs = sharedInstance;
		if (cs == null) {
			synchronized (DefaultConversionService.class) {
				cs = sharedInstance;
				if (cs == null) {
					cs = new DefaultConversionService();
					sharedInstance = cs;
				}
			}
		}
		return cs;
	}

	// 添加适合大多数环境的转换器
	public static void addDefaultConverters(ConverterRegistry converterRegistry) {
		// 添加简单类型的转换器
        addScalarConverters(converterRegistry);
		// 添加集合相关的转换器
        addCollectionConverters(converterRegistry);

		converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
		converterRegistry.addConverter(new StringToTimeZoneConverter());
		converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
		converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());

		//...还有好多
	}

	// 增加通用的转换器,例如集合、数组、对象等
	public static void addCollectionConverters(ConverterRegistry converterRegistry) {
		ConversionService conversionService = (ConversionService) converterRegistry;

		converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
		converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));

		converterRegistry.addConverter(new StringToCollectionConverter(conversionService));

		converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
		converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));

        //...还有好多
	}

    // 新增标量的转化器,主要是字符串数字类型
	private static void addScalarConverters(ConverterRegistry converterRegistry) {
		converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
		converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
		converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
		converterRegistry.addConverter(new StringToPropertiesConverter());
		converterRegistry.addConverter(new PropertiesToStringConverter());
		converterRegistry.addConverter(new StringToUUIDConverter());
        
        //...还有好多
	}

}

2、自定义转换器

自定义转换器:

// 利用泛型指明转换类型
public class TypeStringValueToBoolean implements Converter<TypedStringValue, Boolean> {
    @Override
    public Boolean convert(TypedStringValue source) {
        return Boolean.valueOf(source.getValue());
    }
}
// 通过方法指明类型
public class TypedStringValueToInt implements GenericConverter {
    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(TypedStringValue.class, Integer.class));
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        return Integer.valueOf(((TypedStringValue) source).getValue());
    }
}

测试:

    @Test
    public void testBatchCreate() throws ClassNotFoundException {
        // 1、批量去加载 BeanDefinition 然后注册到 注册器中
        BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
        int cnt = reader.loadBeanDefinitions("classpath:spring-test.xml");
        /*ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
        int cnt = scanner.scan("com.example.springanalyze.bean");*/
        log.info("加载的BeanDefinition的个数:{}", cnt);

        // 2、去遍历每个 beanDefinition
        String[] names = registry.getBeanDefinitionNames();
        for (String name : names) {
            BeanDefinition beanDefinition = registry.getBeanDefinition(name);
            String beanClassName = beanDefinition.getBeanClassName();
            Class<?> aClass = Class.forName(beanClassName);

            // 3、 通过 BeanWrapper 去实例化
            BeanWrapper beanWrapper = new BeanWrapperImpl(aClass);
            DefaultConversionService conversionService = new DefaultConversionService();
            // 添加转换器
            conversionService.addConverter(new TypedStringValueToInt());
            conversionService.addConverter(new TypeStringValueToBoolean());
            beanWrapper.setConversionService(conversionService);
            beanWrapper.setPropertyValues(beanDefinition.getPropertyValues());
            Object obj = beanWrapper.getWrappedInstance();
            System.out.println(obj);

        }
    }

3、转换器工厂(ConverterFactory)

下面是 ConverterFactory 源码(它是一个接口,利用了工厂设计模式,提供的职责是从S类型转换成R类型的工厂):

public interface ConverterFactory<S, R> {

	/**
	 * Get the converter to convert from S to target type T, where T is also an instance of R.
	 * @param  the target type
	 * @param targetType the target type to convert to
	 * @return a converter from S to T
	 */
	<T extends R> Converter<S, T> getConverter(Class<T> targetType);

}

可以看出 R 泛型是转换器目标的上限,也就是说该工厂可以将 S 类型的转换成 R 类型的工厂,转换类型为 R 及其子实现。

CharacterToNumberFactory 的实现:

final class CharacterToNumberFactory implements ConverterFactory<Character, Number> {

	@Override
	public <T extends Number> Converter<Character, T> getConverter(Class<T> targetType) {
		return new CharacterToNumber<>(targetType);
	}

	private static final class CharacterToNumber<T extends Number> implements Converter<Character, T> {

		private final Class<T> targetType;

		public CharacterToNumber(Class<T> targetType) {
			this.targetType = targetType;
		}

		@Override
		public T convert(Character source) {
			return NumberUtils.convertNumberToTargetClass((short) source.charValue(), this.targetType);
		}
	}

}

4、转换器注册器源码

使用 ConversationService 去添加转换器的时候都会通过以下方法调用:

	@Override
	public void addConverter(GenericConverter converter) {
		this.converters.add(converter);// 将转换器存储到注册器中
		invalidateCache();
	}

converters 是 Converters 的实例对象,Converters 是 GenericConversationService 的一个静态内部类。 接下来对其源码进行一个分析(所有的 converter 即转换器都存储在 Converters 中):

private static class Converters {

    // 存取通用的转换器,并不限定转换类型,一般用于兜底
    // 一般不会到这...
    private final Set<GenericConverter> globalConverters = new CopyOnWriteArraySet<>();
    // 指定了类型对,对应的转换器们的映射关系。
    // ConvertiblePair:表示一对,包含sourceType和targetType
    // ConvertersForPair:这一对对应的转换器们(因为能处理一对类型转换可能存在多个转换器),内部使用一个双端队列Deque来存储,保证顺序
    private final Map<ConvertiblePair, ConvertersForPair> converters = new ConcurrentHashMap<>(256);

    public void add(GenericConverter converter) {
        // 获得他的类型对儿
        Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
        if (convertibleTypes == null) {
			// 如果没有限定转换类型,添加到globalConverters
            this.globalConverters.add(converter);
        }
        else {
            // 如果已经存在转换类型,我们写的都在这里
            for (ConvertiblePair convertiblePair : convertibleTypes) {
                // 找到与之匹配的加进去,这里是个链表
                getMatchableConverters(convertiblePair).add(converter);
            }
        }
    }
    
    @Nullable
    public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
        // 搜索完整的类型层次结构,父类--->
        // 比如想要搜索【虎猫 -> 老虎】,但如过虎猫有父类(猫)
        // 我们还需检索【猫 -> 老虎】
        // 因为有些构造器转换对应类型是支持转换其子类的,比如上面提到的转换器工厂就这样设计的
        List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
        List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
        for (Class<?> sourceCandidate : sourceCandidates) {
            for (Class<?> targetCandidate : targetCandidates) {
                // 所有的类型都要匹配
                ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
                // 找到一个就返回
                GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
                if (converter != null) {
                    return converter;
                }
            }
        }
        return null;
    }

    @Nullable
    private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
TypeDescriptor targetType, ConvertiblePair convertiblePair) {

        // 根据convertiblePair获取ConvertersForPair
        ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
        // 如果对应的 convertersForPair 存在,就去获取对应的转换器
        if (convertersForPair != null) {
            GenericConverter converter = convertersForPair.getConverter(sourceType, targetType);
            if (converter != null) {
                return converter;
            }
        }
        // 检查是否能匹配兜底的全局转换器
        for (GenericConverter globalConverter : this.globalConverters) {
            if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
                return globalConverter;
            }
        }
        return null;
    }
   
}

ConvertiblePair:表示一对,包含sourceType和targetType。

final class ConvertiblePair {
    private final Class<?> sourceType;
    private final Class<?> targetType;
}

ConvertersForPair:这一对对应的是转换器们(因为能处理一对类型转换可能存在多个转换器),内部使用一个双端队列 Deque 来存储,保证顺序:

他的结构如下:

【Spring源码分析】Bean的元数据和一些Spring的工具_第11张图片

	private static class ConvertersForPair {
		// 内部维护的双向队列
		private final Deque<GenericConverter> converters = new ConcurrentLinkedDeque<>();

		public void add(GenericConverter converter) {
			this.converters.addFirst(converter);
		}

		@Nullable
		public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
            // 此处的判断条件说明:
            // 如果这个转换器实现了 ConditionalGenericConverter.match() 方法,就必须匹配成功;
            // 如果没有实现这个 ConditionalGenericConverter 接口
            // 此处表明,如果我们有特殊的需求,可以实现 ConditionalGenericConverter,实现特殊的匹配规则
            // 不同的converter可以对应不同的匹配规则,这也是去匹配一个类型转换对应着多个转换器
			for (GenericConverter converter : this.converters) {
				if (!(converter instanceof ConditionalGenericConverter genericConverter) ||
						genericConverter.matches(sourceType, targetType)) {
					return converter;
				}
			}
			return null;
		}

		@Override
		public String toString() {
			return StringUtils.collectionToCommaDelimitedString(this.converters);
		}
	}

其中添加转换器有三个重载方法:

	// 这个方法支持泛型,可以指定转换器由什么类型转换成什么类型
	// 然后构造成ConverterAdapter适配器进行添加进注册器中
	// 其中这个 ConverterAdapter 适配器实现了 ConditionalGenericConverter
	// 也就是实现了 match、convert、getConvertibleTypes方法
	// 最后匹配这个转换器是根据 match 去走的。
	@Override
	public void addConverter(Converter<?, ?> converter) {
		ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);
		if (typeInfo == null && converter instanceof DecoratingProxy decoratingProxy) {
			typeInfo = getRequiredTypeInfo(decoratingProxy.getDecoratedClass(), Converter.class);
		}
		if (typeInfo == null) {
			throw new IllegalArgumentException("Unable to determine source type  and target type  for your " +
					"Converter [" + converter.getClass().getName() + "]; does the class parameterize those types?");
		}
		addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
	}
	// 这是添加有下线和上线的类型转换器
	// 最后构造一个ConverterAdapter适配器去添加进注册器中
	@Override
	public <S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter) {
		addConverter(new ConverterAdapter(
				converter, ResolvableType.forClass(sourceType), ResolvableType.forClass(targetType)));
	}
	// 这个方法是核心,最后都是通过这个方法添加进conversations注册器中
	@Override
	public void addConverter(GenericConverter converter) {
		this.converters.add(converter);
		invalidateCache();
	}

所以我们去实现自己的转换器,去实现 ConditionalGenericConverter 就可以了,当然不需要匹配规则的话,也可以直接去实现 GenericConverter 接口即可。

为什么一个类型转换成另一个类型还需要多种转换器?

举个例子,假设我们有一个应用程序中的实体类 Person,它有属性 name 和 age。现在我们需要将 Person 对象转换成 DTO(Data Transfer Object)对象,以便在网络传输过程或持久化过程中使用。在转换过程中,我们可能会有以下几种转换方式:

  1. 通过直接复制相同属性名的方式进行转换,即将 Person 对象的 name 属性复制到 DTO 对象的 name 属性,将 age 属性复制到 DTO 对象的 age 属性;
  2. 通过自定义的转换逻辑进行转换,例如将 Person 对象的 age 属性按照一定的规则转换为 DTO 对象的 ageInfo 属性,表示年龄信息’’
  3. 通过调用其他服务接口或接口进行转换,例如根据 Person 对象的 name 属性查询其他系统获取到的 DTO 对象。

针对不同的转换方式,我们可以分别编写对应的转换器。在Spring 中,可以通过 ConvertiblePair 对象来表示源类型和目标类型的配对,ConvertersForPair 对象则是一个包含多个转换器的集合。为了匹配对应的转换器,我们可以去实现 ConditionalGenericConverter 接口去实现自己的转换器。

存储在 Map 中的 Hash 问题

有人可能会关心存储在 Map 中,那不同的 ConvertiblePair hash 一样导致 hash 冲突的话,不就引起了不同的类型转换用错了转换器吗?

其实在 ConvertiblePair 中重写了 equals 方法,其中最关键的代码如下(即在匹配的过程中会对源类型和目标类型进行 == 判断):

return (this.sourceType == otherPair.sourceType && this.targetType == otherPair.targetType);

5、注意:是类型转换器

Spring 中有多种转换机制,其中主要包括类型转换器(Converter)和 HTTP 消息转换器(HTTPMessageConverter)。

这里说的类加载器主要用于应用程序内部进行类型转换,例如将一个 Java 对象转换为另一个 Java 对象或基本数据类型的转换。它负责处理方法参数和返回值的类型转化,以及数据绑定等场景。

而 HTTP 消息转换器主要用于处理 Web 请求和响应的消息转换,例如将 Java 对象转换为 JSON 或 XML 格式的数据,以便于网络传输和处理。

  • 在 Spring 中,类型转换器主要由内置的 ConversionService 接口和实现类 DefaultConversionService 来管理,将转换器注册到 Converters 中,使用 Map converters 这样的数据结构来存储转换对应的多个转换器;
  • HttpMessageConverter 主要由内置的 HttpMessageConverter 接口和各种实现类来管理。它负责处理请求和响应的消息转换,如将 Java 对象转换为 JSON 或 XML 格式的数据,以及将 JSON 或 XML 格式数据转换成为 Java 对象。

八、资源获取

Spring 的【org.springframework.core.io.Resource】抽象了对资源的访问的能力。下面提供了【Resource】接口的概述,Spring 本身广泛地使用了 Resource 接口。

public interface Resource extends InputStreamSource {
    boolean exists();
    boolean isReadable();
    boolean isOpen();
    boolean isFile();
    URL getURL() throws IOException;
    URI getURI() throws IOException;
    File getFile() throws IOException;
    ReadableByteChannel readableChannel() throws IOException;
    long contentLength() throws IOException;
    long lastModified() throws IOException;
    Resource createRelative(String relativePath) throws IOException;
    String getFilename();
    String getDescription();
}

1、内置的 Resource 的实现

Spring 包含了几个内置的 Resource 实现,如下所示:

  • UrlResource:UrlResource 包装了 java.net.URL,可以用来访问任何需要通过 URL 访问的对象,例如文件、HTTPS目标、FTP目标等。所有URL都用一个标准化的字符串表示,这样就可以使用适当的标准化前缀来表示不同类型的URL。这包括用于访问文件系统的 file: ,用于通过https协议访问资源的 https: ,用于通过 ftp 访问资源的 ftp: 等。

    • 【Spring源码分析】Bean的元数据和一些Spring的工具_第12张图片
  • ClassPathResource:该类表示应该从【类路径】中获取的资源。它使用线程上下文类装入器、给定的类装入器或给定的类装入资源。

  • FileSystemResource:这是面向java.io的Resource实现,可以简单的实现对系统文件的操作。

  • InputStreamResource:给定的InputStream的Resource实现。 只有当没有特定的资源实现适用时,才应该使用它。

  • ByteArrayResource:这是一个给定字节数组的资源实现。

    @Test
    public void testClassPathResource() throws IOException {
        Resource test = new ClassPathResource("spring-test.xml");
        InputStream inputStream = test.getInputStream();
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = inputStream.read(bytes)) != -1) {
            System.out.println(new String(bytes, 0, len));
        }
    }

2、classpath 前缀

  • 前缀 [classpath:*] :只会到你的 target 下面的 class 路径中查找找文件,通常匹配一个资源;
  • 前缀 [classpath*:]:不仅包含 target 下面的 class 路径,还包括 jar 文件中(target 下面的class路径)进行查找,可以匹配多个资源,这种场景非常多,比如在实现了 springboot 自动装配相关的 jar 包中绝大多数都会由 spring.factories 文件。

3、PathMatchingResourcePatternResolver

下面是它的结构图:

【Spring源码分析】Bean的元数据和一些Spring的工具_第13张图片

它提供了 getResourcegetResources 方法给我们使用。

public Resource getResource(String location) {
    Assert.notNull(location, "Location must not be null");
	// 留给我们的扩展的协议解析器,如自定义
    for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
        Resource resource = protocolResolver.resolve(location, this);
        if (resource != null) {
            return resource;
        }
    }

   
    if (location.startsWith("/")) {
        return getResourceByPath(location);
    }
    // classpath:
    else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
        return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
    } else {
        try {
            // 尝试将位置解析为URL…
            URL url = ResourceUtils.toURL(location);
            // 检测是不是file:  vfsfile:打头的
            return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
        }
    }
}

	// 一般是用于 classpath* 前缀的
	@Override
	public Resource[] getResources(String locationPattern) throws IOException {
		Assert.notNull(locationPattern, "Location pattern must not be null");
		if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
			// a class path resource (multiple resources for same name possible)
			String locationPatternWithoutPrefix = locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length());
			// Search the module path first.
			Set<Resource> resources = findAllModulePathResources(locationPatternWithoutPrefix);
			if (getPathMatcher().isPattern(locationPatternWithoutPrefix)) {
				// a class path resource pattern
				Collections.addAll(resources, findPathMatchingResources(locationPattern));
			}
			else {
				// all class path resources with the given name
				Collections.addAll(resources, findAllClassPathResources(locationPatternWithoutPrefix));
			}
			return resources.toArray(new Resource[0]);
		}
		else {
			// Generally only look for a pattern after a prefix here,
			// and on Tomcat only after the "*/" separator for its "war:" protocol.
			int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
					locationPattern.indexOf(':') + 1);
			if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
				// a file pattern
				return findPathMatchingResources(locationPattern);
			}
			else {
				// a single resource with the given name
				return new Resource[] {getResourceLoader().getResource(locationPattern)};
			}
		}
	}

测试:

    @Test
    public void testResolver() throws IOException {
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources("classpath*:/META-INF/spring.factories");
        for (Resource resource : resources) {
            System.out.println(resource.getURI());
        }
    }
输出:
jar:file:/D:/Study/java/maven_work/maven_repositoryo/org/springframework/boot/spring-boot/3.0.1/spring-boot-3.0.1.jar!/META-INF/spring.factories
jar:file:/D:/Study/java/maven_work/maven_repositoryo/org/springframework/boot/spring-boot-autoconfigure/3.0.1/spring-boot-autoconfigure-3.0.1.jar!/META-INF/spring.factories
jar:file:/D:/Study/java/maven_work/maven_repositoryo/org/springframework/spring-aop/6.0.3/spring-aop-6.0.3.jar!/META-INF/spring.factories
jar:file:/D:/Study/java/maven_work/maven_repositoryo/org/springframework/boot/spring-boot-test/3.0.1/spring-boot-test-3.0.1.jar!/META-INF/spring.factories
jar:file:/D:/Study/java/maven_work/maven_repositoryo/org/springframework/boot/spring-boot-test-autoconfigure/3.0.1/spring-boot-test-autoconfigure-3.0.1.jar!/META-INF/spring.factories
jar:file:/D:/Study/java/maven_work/maven_repositoryo/org/springframework/spring-test/6.0.3/spring-test-6.0.3.jar!/META-INF/spring.factories

九、Spring Event

Spring 提供了 Event Multicaster,可以十分简单地实现发布订阅模式(观察者模式)。

Multicast:组播,多播;

【Spring源码分析】Bean的元数据和一些Spring的工具_第14张图片

这个多播器呢可以发布事件,事件会被对应的监听器监听,若事件被发布了,当然就会触发监听者执行相应的操作。Spring默认有个 SimpleApplicationEventMulticaster 多播器。

简单地测试使用如下:

    @Test
    void testEventMulticaster() {
        SimpleApplicationEventMulticaster eventMulticaster = new
                SimpleApplicationEventMulticaster();
        eventMulticaster.addApplicationListener(new EmailListener());
        eventMulticaster.addApplicationListener(new MessageListener());
        eventMulticaster.addApplicationListener(new EmailListener());

        // 使用多播器发送事件
        eventMulticaster.multicastEvent(new OrderEvent(this));
    }

【Spring源码分析】Bean的元数据和一些Spring的工具_第15张图片

自定义事件可以去继承 ApplicationEvent 类,自定义监听器可以去实现 ApplicationListener.实现如下:

public class MessageListener implements ApplicationListener<OrderEvent>
public class OrderEvent extends ApplicationEvent    

但是实际使用都是通过 bean 进行注入的,ApplicationContext 中的事件处理是通过 ApplicationEvent 类和 ApplicationListener 接口提供的。如果将实现的 ApplicationListener 接口中的 bean 部署到了容器中,则每次将 ApplicationEvent 发布到 ApplicationContext 时,都会通知到该 bean,这是典型的观察者模式。

【Spring源码分析】Bean的元数据和一些Spring的工具_第16张图片

通过上面的 UML 图,咱可以有个大概的理解:

  • 事件和 ApplicationEventPublisher 事件发布者是聚合的关系,事件是由 publisher 进行发布的,而ApplicationContext 就是对 ApplicationEventPublisher 的实现,就是说 ApplicationContext 操纵着事件的发布。
  • Multicaster 拥有很多 Listener,并在 ApplicationContext 中聚合,当然 ApplicationContext 也可以对监听者进行添加;也就是说 ApplicationContext 本身就担任监听器注册表的角色,在其子类 AbstractApplicationContext 中就聚合了事件广播器和事件监听器,并且提供了注册监听器的 addApplicationListener 方法。
  • 使用:
    1. 定义一个继承 ApplicationEvent 事件;
    2. 定义一个实现 ApplicationListener 的监听器或者使用 @EventListener 监听时间;
    3. 定义一个发送者,调用 ApplicationContext 直接发布或者使用 ApplicationEventPublisher 来发布自定义事件。

SimpleApplicationEventMulticaster源码分析

Spring 默认注入的多播器是 SimpleApplicationEventMulticaster,也就是说如果你不自定义 Publisher 发布事件的话,默认就是通过多播器帮你发了:

在这里插入图片描述

一般情况下监听器都是通过bean注入的方式进去的,那bean的话又分为多例prototype,单利singleton。看看下图它是如何管理的:

【Spring源码分析】Bean的元数据和一些Spring的工具_第17张图片

当listener被调用执行后,如何进行了缓存:

【Spring源码分析】Bean的元数据和一些Spring的工具_第18张图片

当listener被调用执行后,如果是单例的,会被缓存到 RetrieverCache 中,其中key 是 eventtype 和 sourcetype 的封装,value 是对应的 listeners,当然一个事件可能对应多个监听嘛!当一个事件触发后,是先去从缓存中查找有无对应的事件监听,无的话再遍历查找。

SimpleApplicationEventMulticaster 源码如下:

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

   // 定义一个线程池,事件被触发时可由他来实现事件,默认为null
   private Executor taskExecutor;

   // 这个用来捕获listener执行过程中产生的异常
   // 需要这用 赋值caster.setErrorHandler(new XxxErrorHandler())
   private ErrorHandler errorHandler;
   private volatile Log lazyLogger;


   public SimpleApplicationEventMulticaster() {}
   public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
      setBeanFactory(beanFactory);
   }
      
   public void setErrorHandler(@Nullable ErrorHandler errorHandler) {
      this.errorHandler = errorHandler;
   }

   @Nullable
   protected ErrorHandler getErrorHandler() {
      return this.errorHandler;
   }


   //设置一个自定义执行器(线程池)来调用每个侦听器。
   public void setTaskExecutor(@Nullable Executor taskExecutor) {
      this.taskExecutor = taskExecutor;
   }
   @Nullable
   protected Executor getTaskExecutor() {
      return this.taskExecutor;
   }
    
   // 广播一个事件
   @Override
   public void multicastEvent(ApplicationEvent event) {
      multicastEvent(event, resolveDefaultEventType(event));
   }

   // 广播一个事件的具体实现
   @Override
   public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
      ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
      Executor executor = getTaskExecutor();
      // 核心一:getApplicationListeners(event, type)稍后看
      // 他的重点是如何设计的缓存
      // 获取所有与event事件匹配的listener并调用核心方法onApplicationEvent
      for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
          // 如果你设置了线程池他会将任务丢给线程池
          if (executor != null) {
              // 核心二:调用Listener的方法invokeListener
            executor.execute(() -> invokeListener(listener, event));
         }
         // 否则就以单线程的方式运行
         else {
            invokeListener(listener, event);
         }
      }
   }

   // 调用listener的方法
   protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
      // ErrorHandler可以保存Listener在执行过程中产生的异常
      // 其默认为null,我们可以独立设置
      ErrorHandler errorHandler = getErrorHandler();
      if (errorHandler != null) {
         try {
            doInvokeListener(listener, event);
         }
         catch (Throwable err) {
            // 将执行listener时产生放入errorHandler
            errorHandler.handleError(err);
         }
      }
      else {
         // 负责直接调用
         doInvokeListener(listener, event);
      }
   }

   @SuppressWarnings({"rawtypes", "unchecked"})
   private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
      try {
         // 调用方法
         listener.onApplicationEvent(event);
      }
      // 捕获类型转化异常
      catch (ClassCastException ex) {
         String msg = ex.getMessage();
         if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
            (event instanceof PayloadApplicationEvent && matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))  ) {
            // 可能是lambda定义的侦听器,我们无法为其解析泛型事件类型
            // 让我们抑制异常。
            Log loggerToUse = this.lazyLogger;
            if (loggerToUse == null) {
               loggerToUse = LogFactory.getLog(getClass());
               this.lazyLogger = loggerToUse;
            }
            if (loggerToUse.isTraceEnabled()) {
               loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
            }
         } else {
            throw ex;
         }
      }
   }

   private boolean matchesClassCastMessage(String classCastMessage, Class<?> eventClass) {
      // 在Java 8上,消息以类名“Java .lang”开始。字符串不能强制转换…
      if (classCastMessage.startsWith(eventClass.getName())) {
         return true;
      }
      // 在Java 11中,消息以“class…”开头,也就是class . tostring ()
      if (classCastMessage.startsWith(eventClass.toString())) {
         return true;
      }
      // 在Java 9上,用于包含模块名称的消息:" Java .base/ Java .lang. lang. xml "。字符串不能强制转换…”
      int moduleSeparatorIndex = classCastMessage.indexOf('/');
      if (moduleSeparatorIndex != -1 && classCastMessage.startsWith(eventClass.getName(), moduleSeparatorIndex + 1)) {
         return true;
      }
      // 假设一个不相关的类转换失败……
      return false;
   }
}

父类 AbstractApplicationEventMulticaster 源码分析:

public abstract class AbstractApplicationEventMulticaster
    implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {

    // 真正保存listener的地方,他保存了传入的listener实例和容器里的监听器bean的名字
    // 其结构如图1
    private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();

    // 这个集合是对已调用的event和listener的缓存
    // ListenerCacheKey保存了一组event和source
    // CachedListenerRetriever保存了已注册的、容器的单例bean、容器的非单例beanName
    // 非单例的bean只能缓存name,实例会消亡
    final Map<ListenerCacheKey, Cached  Retriever> retrieverCache = new ConcurrentHashMap<>(64);

    @Nullable
    private ClassLoader beanClassLoader;

    @Nullable
    private ConfigurableBeanFactory beanFactory;

    // 以编程的方式添加监听器
    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        // 这个过程可能存在线程安全的问题,比如一线线程
        synchronized (this.defaultRetriever) {
            // 因为你的Aop可能影响到该listener
            // 我们需要将代理对象从defaultRetriever中删除,因为我们并不需要
            Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
            if (singletonTarget instanceof ApplicationListener) {
                this.defaultRetriever.applicationListeners.remove(singletonTarget);
            }
            // 添加原生的监听器到defaultRetriever
            this.defaultRetriever.applicationListeners.add(listener);
            // 清理缓存
            this.retrieverCache.clear();
        }
    }

    // 直接以bean的方式将listenerBeanName添加到defaultRetriever
    @Override
    public void addApplicationListenerBean(String listenerBeanName) {
        synchronized (this.defaultRetriever) {
            this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
            // 清理缓存
            this.retrieverCache.clear();
        }
    }

    // 获得所有的bean的实例
    // 第一部分:编程式的bean的实例
    // 第二部分:容器的bean,进行实例化
    protected Collection<ApplicationListener<?>> getApplicationListeners() {
        synchronized (this.defaultRetriever) {
            return this.defaultRetriever.getApplicationListeners();
        }
    }

    // ...省略掉大量雷同的crud(增删查改)代码

    // 我们看一个比较有难度的方法,该方法根据事件获取满足条件的listeners
    protected Collection<ApplicationListener<?>> getApplicationListeners(
        ApplicationEvent event, ResolvableType eventType) {
        // 获得事件源(那个类中发布的时间(如OrderService类发布是orderEvent))
        // source就是OrderService,event就是orderEvent
        Object source = event.getSource();
        Class<?> sourceType = (source != null ? source.getClass() : null);
        // 通过事件类型和源类型创建缓存key
        // 第二次请求就避免了再次检索
        ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

        // 定义新的Listener缓存器,他是作为retrieverCache的value
        CachedListenerRetriever newRetriever = null;

        // 核心:快速检查缓存中的内容
        CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
        if (existingRetriever == null) {
            // 满足条件,则在existingRetriever缓存一个新的条目
            // key->ListenerCacheKey(eventType和sourceType)
            // value->CachedListenerRetriever(保存了与key匹配的listeners)
            if (this.beanClassLoader == null ||(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
                                                (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
                // 创建一个新的缓存器,目前里边为空
                newRetriever = new CachedListenerRetriever();
                // 将这个key和检索器放入整体缓存map中
                existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
                // 添加失败,则将newRetriever置空
                // 多线程下可能会添加失败,两个线程同时添加,只能有一个成功
                if (existingRetriever != null) {
                    // 将new的缓存器置空,复用其他线程创建的
                    newRetriever = null;  
                }
            }
        }
        // 如果缓存命中(缓存中存在这一对key-value)
        if (existingRetriever != null) {
            Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
            // 将缓存中的Listeners全部取出,返回
            if (result != null) {
                return result;
            }
            // 缓存命中没有拿到结果,此时result为null
            // 这种一般出现在多线程情况,有一个线程已经创建了这个缓存器,但是还没有机会赋值
            // 当前线程又拿到了这个缓存器,那我们就继续
        }

        // 该方法会为我们当前根据key过滤合适的listeners,并缓存器赋值
        return retrieveApplicationListeners(eventType, sourceType, newRetriever);
    }

    // 该方法会给传入的newRetriever赋值,检索过程相对复杂
    // 这个方法会过滤满足条件的Listeners,并将过滤后的内容放到缓存中
    private Collection<ApplicationListener<?>> retrieveApplicationListeners(
        ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {

        // 保存所有过滤好的listeners
        // 他包含编程式的实例和容器的bean
        List<ApplicationListener<?>> allListeners = new ArrayList<>();
        // 过滤后的监听器,将来做缓存用
        Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
        // 过滤后的监听器beanName,将来做缓存用
        Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);

        Set<ApplicationListener<?>> listeners;
        Set<String> listenerBeans;

        // 每个线程拷贝独立的listeners和listenerBeans
        synchronized (this.defaultRetriever) {
            // 优先做一个拷贝
            listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
            listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
        }

        // 将所有的listener实例遍历,过滤满足条件的
        for (ApplicationListener<?> listener : listeners) {
            if (supportsEvent(listener, eventType, sourceType)) {
                // 如果传递了缓存器,就将它存入filteredListeners
                if (retriever != null) {
                    filteredListeners.add(listener);
                }
                allListeners.add(listener);
            }
        }

        // bean要在这里获取初始化
        if (!listenerBeans.isEmpty()) {
            // 这里初始化
            ConfigurableBeanFactory beanFactory = getBeanFactory();
            for (String listenerBeanName : listenerBeans) {
                try {
                    // 判断beanFactory是不是支持该事件
                    if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
                        // 从bean工厂获取,实例化bean
                        ApplicationListener<?> listener =
                            beanFactory.getBean(listenerBeanName,ApplicationListener.class);
                        if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                            if (retriever != null) {
                                // 如果是单例的就加入filteredListeners
                                if (beanFactory.isSingleton(listenerBeanName)) {
                                    filteredListeners.add(listener);
                                }
                                // 如果不是单例bean,则加入filteredListenerBeans
                                // 原因是非单例bean使用结束可能会被gc,下次使用需要重新实例化
                                // 所以,我们并不缓存非单例的listenerbean
                                else {
                                    filteredListenerBeans.add(listenerBeanName);
                                }
                            }
                            // 无论是不是单例,都将实例加入allListeners,
                            // 他将作为当前方法的返回值
                            allListeners.add(listener);
                        }
                    }
                    else {
                        // 删除不匹配的侦听器
                        // ApplicationListenerDetector,可能被额外的排除
                        // BeanDefinition元数据(例如工厂方法泛型)
                        Object listener = beanFactory.getSingleton(listenerBeanName);
                        if (retriever != null) {
                            filteredListeners.remove(listener);
                        }
                        allListeners.remove(listener);
                    }
                }
                catch (NoSuchBeanDefinitionException ex) {
                }
            }
        }

        // 给结果排序
        AnnotationAwareOrderComparator.sort(allListeners);

        // 实际进行缓存的地方
        // 在这里我们缓存了applicationListeners(编程式的和单例的bean)
        // 和applicationListenerBeans,主要是非单例bean的名字
        // 下次从缓存获取的时候还是会再次实例化非单例的bean
        if (retriever != null) {
            // 如果啥也没有过滤则添加全部的Listeners和ListenerBeans到retriever
            if (filteredListenerBeans.isEmpty()) {
                retriever.applicationListeners = new LinkedHashSet<>(allListeners);
                retriever.applicationListenerBeans = filteredListenerBeans;
            }
            else {
                retriever.applicationListeners = filteredListeners;
                retriever.applicationListenerBeans = filteredListenerBeans;
            }
        }
        return allListeners;
    }
}

key:ListenerCacheKey的实现:

private static final class ListenerCacheKey implements Comparable<ListenerCacheKey> {
    // 一个ListenerCacheKey包含了eventType和sourceType
    // eventType事件类型 | sourceType发布事件的类的类型、
    // 谁发布了这个事件
    private final ResolvableType eventType;
    private final Class<?> sourceType;
    public ListenerCacheKey(ResolvableType eventType, @Nullable Class<?> sourceType) {
        Assert.notNull(eventType, "Event type must not be null");
        this.eventType = eventType;
        this.sourceType = sourceType;
    }
    //...省略
}

value:CachedListenerRetriever的实现:

//listener缓存工具类,它封装了一组特定的目标侦听器,允许高效检索预先过滤的侦听器。每个事件类型和源类型缓存这个helper的实例。
private class CachedListenerRetriever {

    // 保存了满足条件的applicationListeners
    // 包含编程式的和容器内满足条件的单例bean
    @Nullable
    public volatile Set<ApplicationListener<?>> applicationListeners;
    // 保存了满足条件的非单例的applicationListenerBeans
    @Nullable
    public volatile Set<String> applicationListenerBeans;

    // 该方法是从特定缓存获取applicationListeners
    // 这个方法会再次实例化非单例的bean
    @Nullable
    public Collection<ApplicationListener<?>> getApplicationListeners() {
        Set<ApplicationListener<?>> applicationListeners = this.applicationListeners;
        Set<String> applicationListenerBeans = this.applicationListenerBeans;
        if (applicationListeners == null || applicationListenerBeans == null) {
            // Not fully populated yet
            return null;
        }
        // 创建一个临时的集合保存所有的监听器
        List<ApplicationListener<?>> allListeners = new ArrayList<>(
            applicationListeners.size() + applicationListenerBeans.size());
        allListeners.addAll(applicationListeners);
        // 这里实例化剩下的bean,容器内的非单例bean
        // 这里不一样的地方是非单单例的bean,每次清除缓存都要重新实例化
        if (!applicationListenerBeans.isEmpty()) {
            BeanFactory beanFactory = getBeanFactory();
            for (String listenerBeanName : applicationListenerBeans) {
                try {
                    allListeners.add(beanFactory.getBean(listenerBeanName, ApplicationListener.class));
                }
                catch (NoSuchBeanDefinitionException ex) {
                }
            }
        }
        // 对他进行重排序
        if (!applicationListenerBeans.isEmpty()) {
            AnnotationAwareOrderComparator.sort(allListeners);
        }
        return allListeners;
    }
}

默认的listener的存储器:

private class DefaultListenerRetriever {

    public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();

    public final Set<String> applicationListenerBeans = new LinkedHashSet<>();

    public Collection<ApplicationListener<?>> getApplicationListeners() {
        List<ApplicationListener<?>> allListeners = new ArrayList<>(
            this.applicationListeners.size() + this.applicationListenerBeans.size());
        allListeners.addAll(this.applicationListeners);
        if (!this.applicationListenerBeans.isEmpty()) {
            BeanFactory beanFactory = getBeanFactory();
            for (String listenerBeanName : this.applicationListenerBeans) {
                try {
                    ApplicationListener<?> listener =
                        beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                    if (!allListeners.contains(listener)) {
                        allListeners.add(listener);
                    }
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // 单例侦听器实例(没有支持bean定义)消失,可能在销毁阶段的中间
                }
            }
        }
        AnnotationAwareOrderComparator.sort(allListeners);
        return allListeners;
    }
}

你可能感兴趣的:(Java源码分析,spring,java,后端)