【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 对象
  • 作用范围、是否懒加载等等

结构图如下:

1671763022574

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,可以只对有父子关系的beanvoid 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();// 设置是否主要beanvoid 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 BeanMetadataAttributeAccessorimplements 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_DEFAULTprivate 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 {@Nullableprivate String parentName;public GenericBeanDefinition() {super();}// 通过深拷贝创建一个beanpublic GenericBeanDefinition(BeanDefinition original) {super(original);}@Overridepublic void setParentName(@Nullable String parentName) {this.parentName = parentName;}@Override@Nullablepublic String getParentName() {return this.parentName;}@Overridepublic AbstractBeanDefinition cloneBeanDefinition() {return new GenericBeanDefinition(this);}
}

测试

    @Testpublic 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的创建需要依赖父DefinitionChildBeanDefinition 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 {// 注册一个BeanDefinitionvoid 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 的时候进行第一次扩容。

在这里插入图片描述

    @Testpublic 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的创建需要依赖父DefinitionChildBeanDefinition 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 注册倒一个注册器中:

    @Testpublic void testRegistryByXml() {BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();// 创建一个 xml 的 readerXmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);reader.loadBeanDefinitions("classpath:spring-test.xml");System.out.println(registry);}

2、加载带注解的 bean

    @Testpublic void testRegistryByXml() {BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();// 创建一个 xml 的 readerXmlBeanDefinitionReader 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、类路径扫描

    @Testpublic void testRegistryByScanner(){BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();// 通过扫描包的方式去注册 BeanDefinitionClassPathBeanDefinitionScanner 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或者cglibScopeMetadata 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支持成为自动注入候选beanif (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}// 如果bean定义是AnnotatedBeanDefinition类型,ScannedGenericBeanDefinition同样属于AnnotatedBeanDefinition类型if (candidate instanceof AnnotatedBeanDefinition) {// 4 处理类上的其他通用注解:@Lazy, @Primary, @DependsOn, @Role, @DescriptionAnnotationConfigUtils.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/**/*.classString 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)) {// 如果成功的话就把元数据封装成 ScannerGenericBeanDefinitionScannedGenericBeanDefinition 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 {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return false;}
}
@Component
@Data
@Conditional(MyCondition.class)
public class Person {private String name;private Integer age;}

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

在这里插入图片描述

包扫描过程总结

  • 构建 ClassPathBeanDefinitionScanner 包扫描器对象;

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

    • 在这里插入图片描述
  • 调用 doScan 方法进行包扫描。

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

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

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

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

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

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

在这里插入图片描述

五、内省 API

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

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

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

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

1673077112706

  1. 获取到 BeanInfo 内省信息

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

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

    在这里插入图片描述

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

    在这里插入图片描述

    @Testpublic 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 提供了一系列静态方法,用于复制、合并、获取、设置对象的属性值等操作。

    @Testpublic 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 的配合使用:

    @Testpublic 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);}

批量构造

    @Testpublic 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、去遍历每个 beanDefinitionString[] 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 集然后去遍历就会出现问题:

在这里插入图片描述

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

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

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

在这里插入图片描述

ResolvableType

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

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

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

    private Map<String,Object> map;private Integer mm;@Testpublic 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

在这里插入图片描述

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

public class DefaultConversionService extends GenericConversionService {// 这里添加 volatile 关键字,保证了指令的有序性@Nullableprivate 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> {@Overridepublic Boolean convert(TypedStringValue source) {return Boolean.valueOf(source.getValue());}
}
// 通过方法指明类型
public class TypedStringValueToInt implements GenericConverter {@Overridepublic Set<ConvertiblePair> getConvertibleTypes() {return Collections.singleton(new ConvertiblePair(TypedStringValue.class, Integer.class));}@Overridepublic Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {return Integer.valueOf(((TypedStringValue) source).getValue());}
}

测试:

    @Testpublic 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、去遍历每个 beanDefinitionString[] 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 <T> 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);}

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

CharacterToNumberFactory 的实现:

final class CharacterToNumberFactory implements ConverterFactory<Character, Number> {@Overridepublic <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;}@Overridepublic T convert(Character source) {return NumberUtils.convertNumberToTargetClass((short) source.charValue(), this.targetType);}}}

4、转换器注册器源码

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

	@Overridepublic 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) {// 如果没有限定转换类型,添加到globalConvertersthis.globalConverters.add(converter);}else {// 如果已经存在转换类型,我们写的都在这里for (ConvertiblePair convertiblePair : convertibleTypes) {// 找到与之匹配的加进去,这里是个链表getMatchableConverters(convertiblePair).add(converter);}}}@Nullablepublic 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;}@Nullableprivate GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
TypeDescriptor targetType, ConvertiblePair convertiblePair) {// 根据convertiblePair获取ConvertersForPairConvertersForPair 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 来存储,保证顺序:

他的结构如下:

1671937149896

	private static class ConvertersForPair {// 内部维护的双向队列private final Deque<GenericConverter> converters = new ConcurrentLinkedDeque<>();public void add(GenericConverter converter) {this.converters.addFirst(converter);}@Nullablepublic 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;}@Overridepublic String toString() {return StringUtils.collectionToCommaDelimitedString(this.converters);}}

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

	// 这个方法支持泛型,可以指定转换器由什么类型转换成什么类型// 然后构造成ConverterAdapter适配器进行添加进注册器中// 其中这个 ConverterAdapter 适配器实现了 ConditionalGenericConverter// 也就是实现了 match、convert、getConvertibleTypes方法// 最后匹配这个转换器是根据 match 去走的。@Overridepublic 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 <S> and target type <T> for your " +"Converter [" + converter.getClass().getName() + "]; does the class parameterize those types?");}addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));}// 这是添加有下线和上线的类型转换器// 最后构造一个ConverterAdapter适配器去添加进注册器中@Overridepublic <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注册器中@Overridepublic 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<ConvertiblePair,ConvertersForPair> 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: 等。

    • 在这里插入图片描述
  • ClassPathResource:该类表示应该从【类路径】中获取的资源。它使用线程上下文类装入器、给定的类装入器或给定的类装入资源。

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

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

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

    @Testpublic 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

下面是它的结构图:

在这里插入图片描述

它提供了 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* 前缀的@Overridepublic 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 patternCollections.addAll(resources, findPathMatchingResources(locationPattern));}else {// all class path resources with the given nameCollections.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 patternreturn findPathMatchingResources(locationPattern);}else {// a single resource with the given namereturn new Resource[] {getResourceLoader().getResource(locationPattern)};}}}

测试:

    @Testpublic 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:组播,多播;

1672030494623

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

简单地测试使用如下:

    @Testvoid testEventMulticaster() {SimpleApplicationEventMulticaster eventMulticaster = newSimpleApplicationEventMulticaster();eventMulticaster.addApplicationListener(new EmailListener());eventMulticaster.addApplicationListener(new MessageListener());eventMulticaster.addApplicationListener(new EmailListener());// 使用多播器发送事件eventMulticaster.multicastEvent(new OrderEvent(this));}

在这里插入图片描述

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

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

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

在这里插入图片描述

通过上面的 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。看看下图它是如何管理的:

1672034117063

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

1673015589506

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

SimpleApplicationEventMulticaster 源码如下:

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {// 定义一个线程池,事件被触发时可由他来实现事件,默认为nullprivate 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;}@Nullableprotected ErrorHandler getErrorHandler() {return this.errorHandler;}//设置一个自定义执行器(线程池)来调用每个侦听器。public void setTaskExecutor(@Nullable Executor taskExecutor) {this.taskExecutor = taskExecutor;}@Nullableprotected Executor getTaskExecutor() {return this.taskExecutor;}// 广播一个事件@Overridepublic void multicastEvent(ApplicationEvent event) {multicastEvent(event, resolveDefaultEventType(event));}// 广播一个事件的具体实现@Overridepublic void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));Executor executor = getTaskExecutor();// 核心一:getApplicationListeners(event, type)稍后看// 他的重点是如何设计的缓存// 获取所有与event事件匹配的listener并调用核心方法onApplicationEventfor (ApplicationListener<?> listener : getApplicationListeners(event, type)) {// 如果你设置了线程池他会将任务丢给线程池if (executor != null) {// 核心二:调用Listener的方法invokeListenerexecutor.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时产生放入errorHandlererrorHandler.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 AbstractApplicationEventMulticasterimplements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {// 真正保存listener的地方,他保存了传入的listener实例和容器里的监听器bean的名字// 其结构如图1private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();// 这个集合是对已调用的event和listener的缓存// ListenerCacheKey保存了一组event和source// CachedListenerRetriever保存了已注册的、容器的单例bean、容器的非单例beanName// 非单例的bean只能缓存name,实例会消亡final Map<ListenerCacheKey, Cached  Retriever> retrieverCache = new ConcurrentHashMap<>(64);@Nullableprivate ClassLoader beanClassLoader;@Nullableprivate ConfigurableBeanFactory beanFactory;// 以编程的方式添加监听器@Overridepublic void addApplicationListener(ApplicationListener<?> listener) {// 这个过程可能存在线程安全的问题,比如一线线程synchronized (this.defaultRetriever) {// 因为你的Aop可能影响到该listener// 我们需要将代理对象从defaultRetriever中删除,因为我们并不需要Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);if (singletonTarget instanceof ApplicationListener) {this.defaultRetriever.applicationListeners.remove(singletonTarget);}// 添加原生的监听器到defaultRetrieverthis.defaultRetriever.applicationListeners.add(listener);// 清理缓存this.retrieverCache.clear();}}// 直接以bean的方式将listenerBeanName添加到defaultRetriever@Overridepublic 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(增删查改)代码// 我们看一个比较有难度的方法,该方法根据事件获取满足条件的listenersprotected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {// 获得事件源(那个类中发布的时间(如OrderService类发布是orderEvent))// source就是OrderService,event就是orderEventObject source = event.getSource();Class<?> sourceType = (source != null ? source.getClass() : null);// 通过事件类型和源类型创建缓存key// 第二次请求就避免了再次检索ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);// 定义新的Listener缓存器,他是作为retrieverCache的valueCachedListenerRetriever 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// 他包含编程式的实例和容器的beanList<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和listenerBeanssynchronized (this.defaultRetriever) {// 优先做一个拷贝listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);}// 将所有的listener实例遍历,过滤满足条件的for (ApplicationListener<?> listener : listeners) {if (supportsEvent(listener, eventType, sourceType)) {// 如果传递了缓存器,就将它存入filteredListenersif (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工厂获取,实例化beanApplicationListener<?> listener =beanFactory.getBean(listenerBeanName,ApplicationListener.class);if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {if (retriever != null) {// 如果是单例的就加入filteredListenersif (beanFactory.isSingleton(listenerBeanName)) {filteredListeners.add(listener);}// 如果不是单例bean,则加入filteredListenerBeans// 原因是非单例bean使用结束可能会被gc,下次使用需要重新实例化// 所以,我们并不缓存非单例的listenerbeanelse {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的名字// 下次从缓存获取的时候还是会再次实例化非单例的beanif (retriever != null) {// 如果啥也没有过滤则添加全部的Listeners和ListenerBeans到retrieverif (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@Nullablepublic volatile Set<ApplicationListener<?>> applicationListeners;// 保存了满足条件的非单例的applicationListenerBeans@Nullablepublic volatile Set<String> applicationListenerBeans;// 该方法是从特定缓存获取applicationListeners// 这个方法会再次实例化非单例的bean@Nullablepublic Collection<ApplicationListener<?>> getApplicationListeners() {Set<ApplicationListener<?>> applicationListeners = this.applicationListeners;Set<String> applicationListenerBeans = this.applicationListenerBeans;if (applicationListeners == null || applicationListenerBeans == null) {// Not fully populated yetreturn 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;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/104499.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++视觉处理---仿射变换和二维旋转变换矩阵的函数

仿射变换cv::warpAffine cv::warpAffine 是OpenCV中用于执行仿射变换的函数。仿射变换是一种线性变换&#xff0c;可用于执行平移、旋转、缩放和剪切等操作。下面是 cv::warpAffine 函数的基本用法&#xff1a; cv::warpAffine(src, dst, M, dsize, flags, borderMode, borde…

【学习笔记】Spring Security 01 认识Spring Security的重要特征(Features)

Spring Security 零、概述 Spring Security&#xff08;简称SS&#xff09;是一个高可用的、可自定义的身份认证和鉴权控制的框架。 类似的框架还有Shiro。 需求场景&#xff1a; 现今流行的web开发中&#xff0c;安全的第一位。 原本的鉴权开发流程&#xff1a;springweb自…

【ROS】使用vscode浏览navigation2源码时,提示:没有那个文件或目录

1、问题描述 使用vscode浏览navigation2源码时,头文件下面有波浪线,并提示:没有那个文件或目录。比如没有:geometry_msgs/msg/polygon.h 错误信息: 无法打开源文件 “geometry_msgs/msg/polygon.h” (dependency of “nav2_controller/controller_server.hpp”)C/C++(16…

【C++】异常处理之throw、catch、try、局部资源管理、标准异常库

一、抛出异常 异常处理机制两个主要成分&#xff1a; 异常的鉴定与发出&#xff1b;异常的处理方式。 C通过throw表达式产生异常&#xff1a; inline void Triangular_iterator:: check_integrity() {if(_index>Triangular::max_elems){throw iterator_overflow(_index,…

解决react使用css module无法重写bootstrap样式的问题

react使用css module虽然能够解决样式污染&#xff0c;但是同时也失去了写css样式的灵活性&#xff0c;特别是&#xff1a;在.module.css文件中当子元素是非变量的静态class类&#xff08;比如bootstrap&#xff09;, 此时使用css选择器对该子元素的样式不会起作用的 比如下面…

什么是API网关?——驱动数字化转型的“隐形冠军”

什么是API网关 API网关&#xff08;API Gateway&#xff09;是一个服务器&#xff0c;位于应用程序和后端服务之间&#xff0c;提供了一种集中式的方式来管理API的访问。它是系统的入口点&#xff0c;负责接收并处理来自客户端的请求&#xff0c;然后将请求路由到相应的后端服…

为什么Excel插入图片不显示,点击能够显示

今天在做Excel表格时&#xff0c;发现上传图片后不能显示&#xff0c;但是点击还是能够出现图片的 点击如下 点击能看到&#xff0c;但是不显示&#xff1f;那么只需鼠标右键点击浮动即可显示

CSS 滚动驱动动画 animation-range

animation-range 语法 normallength-percentagetimeline-range-name 具名时间线范围 named timeline rangecovercontainentry 和 entry-crossingexit 和 exit-crossing 兼容性 animation-range 这个属性可同时对 scroll progress timeline 和 view progress timeline 这两种不…

了解华为交换机路由器的基本命令

什么是CLI&#xff1a;使用户与设备交互的界面&#xff0c;用户输入对应的命令&#xff0c;设备会回复我们输入的内容&#xff0c;回车车后设备会执行对应命令&#xff0c;达到管理、配置、查看的目的。 CLI界面分为三种操作视图&#xff1a; 用户试图&#xff1a;设备登陆后…

【java|golang】多字段排序以及排序规则

奖励最顶尖的 K 名学生 给你两个字符串数组 positive_feedback 和 negative_feedback &#xff0c;分别包含表示正面的和负面的词汇。不会 有单词同时是正面的和负面的。 一开始&#xff0c;每位学生分数为 0 。每个正面的单词会给学生的分数 加 3 分&#xff0c;每个负面的词…

selenium打开火狐浏览器

项目上需求为&#xff1a;甲方OA 系统是IE系统&#xff0c;需要从IE系统点个按钮打开火狐浏览器单点登录跳转到我们的系统 前期解决方案为&#xff1a;打开浏览器就行了&#xff0c;然后就用的是打开本地浏览器&#xff0c;但是由于B/S架构&#xff0c;有别人远程访问我的ip来…

如何使用ChatPPT生成PPT文档

简介 ChatPPT是一个基于人工智能的PPT生成工具&#xff0c;可以帮助用户快速生成高质量的PPT文档。ChatPPT使用自然语言处理技术&#xff0c;可以根据用户的指令生成PPT内容、设计和排版。 使用方法 ChatPPT提供了两种使用方式&#xff1a;在线体验版和Office插件版。 在线…

【博客搭建】1、拾壹博客本地启动遇到的问题和需要注意的坑

一、后端&#xff08;blog&#xff09;启动 1、修改application.yml中的数据库链接与密码&#xff0c;Redis账号密码&#xff0c;即可启动成功&#xff1b; 2、运行之前先导入sql&#xff1b; 3、 如需上传文件保存至本地&#xff08;例如相册的照片&#xff09;&#xff0c;需…

【竞赛题目】木块(C语言详解)

" 木块 " 是【第二届全国高校计算机技能竞赛】里的一道竞赛题目&#xff0c;博主觉得很新颖想推荐给大家&#xff1b; 题目描述 为了提高词汇量&#xff0c;小理得到了一套四块木块&#xff0c;其中每块都是一个立方体&#xff0c;六面各写着一个字母。他正在通过将…

神经网络中卷积和池化的区别

1、什么叫卷积&#xff1f; 卷积层是用一个固定大小的矩形区去席卷原始数据&#xff0c;将原始数据分成一个个和卷积核大小相同的小块&#xff0c;然后将这些小块和卷积核相乘输出一个卷积值&#xff08;注意这里是一个单独的值&#xff0c;不再是矩阵了&#xff09;。 卷积的…

YOLOv5算法改进(11)— 主干网络介绍(MobileNetV3、ShuffleNetV2和GhostNet)

前言:Hello大家好,我是小哥谈。主干网络通常指的是深度学习中的主干模型,通常由多个卷积层和池化层组成,用于提取输入数据的特征。在训练过程中,主干网络的参数会被不断优化以提高模型的准确性。YOLOv5算法中的主干网络可以有多种替换方案,为了后面讲解的方便,本篇文章就…

CVE-2017-12615 Tomcat远程命令执行漏洞

漏洞简介 2017年9月19日&#xff0c;Apache Tomcat官方确认并修复了两个高危漏洞&#xff0c;漏洞CVE编号&#xff1a;CVE-2017-12615和CVE-2017-12616&#xff0c;其中 远程代码执行漏洞&#xff08;CVE-2017-12615&#xff09; 当 Tomcat 运行在 Windows 主机上&#xff0c;…

加持智慧医疗,美格智能5G数传+智能模组让就医触手可及

智慧医疗将云计算、物联网、大数据、AI等新兴技术融合赋能医疗健康领域&#xff0c;是提高医疗健康服务的资源利用效率&#xff0c;创造高质量健康医疗的新途径。《健康中国2030规划纲要》把医疗健康提升到了国家战略层面&#xff0c;之后《“十四五”全面医疗保障规划》等一系…

时间复杂度O(40n*n)的C++算法:修改图中的边权

本篇源码下载 点击下载 1.12.1. 题目 给你一个 n 个节点的 无向带权连通 图&#xff0c;节点编号为 0 到 n - 1 &#xff0c;再给你一个整数数组 edges &#xff0c;其中 edges[i] [ai, bi, wi] 表示节点 ai 和 bi 之间有一条边权为 wi 的边。 部分边的边权为 -1&#xff08…

CANdb++数据库操作

CANdb数据库操作 创建工程结构文件夹新建数据库&总线描述节点设置节点创建配置Message属性信号设置节点收发信号 环境变量配置一致性检验数据库工程XVehicle.dbc导入工程文件总结 创建工程结构文件夹 在文件夹X-Vehicle-1下&#xff0c;建立工程目录文件夹CANdb&#xff0…