Spring源码架构-核心概念解析

目录

一、BeanDefinition

二、BeanDefinitionReader

AnnotatedBeanDefinitionReader

XmlBeanDefinitionReader

ClassPathBeanDefinitionScanner

三、BeanFactory

四、ApplicationContext

AnnotationConfigApplicationContext

ClassPathXmlApplicationContext

国际化

资源加载

获取运行时环境

事件发布

五、类型转化

PropertyEditor

ConversionService

TypeConverter

六、OrderComparator

七、BeanPostProcessor

八、BeanFactoryPostProcessor

九、FactoryBean

十、ExcludeFilter和IncludeFilter

十一、MetadataReader、ClassMetadata、AnnotationMetadata


一、BeanDefinition

BeanDefinition表示Bean定义,BeanDefinition中存在很多属性用来描述一个Bean的特点。比如:

  • class,表示Bean类型
  • scope,表示Bean作用域,单例或原型等
  • lazyInit:表示Bean是否是懒加载
  • initMethodName:表示Bean初始化时要执行的方法
  • destroyMethodName:表示Bean销毁时要执行的方法
  • 还有很多...

在Spring中,经常会通过以下几种方式来定义Bean:

  1. <bean/>
  2. @Bean
  3. @Component(@Service,@Controller)

这些,可以称之声明式定义Bean

还可以编程式定义Bean,那就是直接通过BeanDefinition,比如:

// 这样的话在User中可以不写@Component注解

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

// 生成一个BeanDefinition对象,并设置beanClass为User.class,并注册到ApplicationContext中
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);
context.registerBeanDefinition("user", beanDefinition);

System.out.println(context.getBean("user"));//com.bubble.service.User@31b7dea0

还可以通过BeanDefinition设置一个Bean的其他属性

beanDefinition.setScope("prototype"); // 设置作用域
beanDefinition.setInitMethodName("init"); // 设置初始化方法
beanDefinition.setLazyInit(true); // 设置懒加载

和声明式事务、编程式事务类似,通过<bean/>,@Bean,@Component等声明式方式所定义的Bean,最终都会被Spring解析为对应的BeanDefinition对象,并放入Spring容器中。

二、BeanDefinitionReader

接下来,介绍几种在Spring源码中所提供的BeanDefinition读取器(BeanDefinitionReader),这些BeanDefinitionReader在使用Spring时用得少,但在Spring源码中用得多,相当于Spring源码的基础设施。

AnnotatedBeanDefinitionReader

可以直接把某个类转换为BeanDefinition,并且会解析该类上的注解,比如

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(context);

// 将User.class解析为BeanDefinition
annotatedBeanDefinitionReader.register(User.class);

System.out.println(context.getBean("user"));//com.bubble.service.User@763d9750

注意:它能解析的注解是:@Conditional,@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description

XmlBeanDefinitionReader

可以解析<bean/>标签

<bean id = "user" class="com.bubble.service.User"/>

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(context);
int i = xmlBeanDefinitionReader.loadBeanDefinitions("spring.xml");//spring.xml中定义bean的个数

System.out.println(context.getBean("user"));//com.bubble.service.User@71f2a7d5

ClassPathBeanDefinitionScanner

ClassPathBeanDefinitionScanner是扫描器,但是它的作用和BeanDefinitionReader类似,它可以进行扫描,扫描某个包路径,对扫描到的类进行解析,比如,扫描到的类上如果存在@Component注解,那么就会把这个类解析为一个BeanDefinition,比如:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();

ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.scan("com.bubble");

System.out.println(context.getBean("userService"));//com.bubble.service.UserService@3caeaf62

三、BeanFactory

BeanFactory表示Bean工厂,所以很明显,BeanFactory会负责创建Bean,并且提供获取Bean的API。

而ApplicationContext是BeanFactory的一种,在Spring源码中,是这么定义的:

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
  MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

            ...
}

首先,在Java中,接口是可以多继承的,我们发现ApplicationContext继承了ListableBeanFactory和HierarchicalBeanFactory,而ListableBeanFactory和HierarchicalBeanFactory都继承至BeanFactory,所以可以认为ApplicationContext继承了BeanFactory,相当于苹果继承水果,宝马继承汽车一样,ApplicationContext也是BeanFactory的一种,拥有BeanFactory支持的所有功能,不过ApplicationContext比BeanFactory更加强大,ApplicationContext还继承了其他接口,也就表示ApplicationContext还拥有其他功能,比如MessageSource表示国际化,ApplicationEventPublisher表示事件发布,EnvironmentCapable表示获取环境变量,等等,关于ApplicationContext后面再详细讨论。

在Spring的源码实现中,当new一个ApplicationContext时,其底层会new一个BeanFactory出来,当使用ApplicationContext的某些方法时,比如getBean(),底层调用的是BeanFactory的getBean()方法。

在Spring源码中,BeanFactory接口存在一个非常重要的实现类是:DefaultListableBeanFactory,也是非常核心的。

所以,我们可以直接来使用DefaultListableBeanFactory,而不用使用ApplicationContext的某个实现类,比如:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);

beanFactory.registerBeanDefinition("user", beanDefinition);

System.out.println(beanFactory.getBean("user"));//com.bubble.service.User@728938a9

DefaultListableBeanFactory是非常强大的,支持很多功能,可以通过查看DefaultListableBeanFactory的类继承实现结构来看

它实现了很多接口,表示,它拥有很多功能:

  1. AliasRegistry:支持别名功能,一个名字可以对应多个别名
  2. BeanDefinitionRegistry:可以注册、保存、移除、获取某个BeanDefinition
  3. BeanFactory:Bean工厂,可以根据某个bean的名字、或类型、或别名获取某个Bean对象
  4. SingletonBeanRegistry:可以直接注册、获取某个单例Bean
  5. SimpleAliasRegistry:它是一个类,实现了AliasRegistry接口中所定义的功能,支持别名功能
  6. ListableBeanFactory:在BeanFactory的基础上,增加了其他功能,可以获取所有BeanDefinition的beanNames,可以根据某个类型获取对应的beanNames,可以根据某个类型获取{类型:对应的Bean}的映射关系
  7. HierarchicalBeanFactory:在BeanFactory的基础上,添加了获取父BeanFactory的功能
  8. DefaultSingletonBeanRegistry:它是一个类,实现了SingletonBeanRegistry接口,拥有了直接注册、获取某个单例Bean的功能
  9. ConfigurableBeanFactory:在HierarchicalBeanFactory和SingletonBeanRegistry的基础上,添加了设置父BeanFactory、类加载器(表示可以指定某个类加载器进行类的加载)、设置Spring EL表达式解析器(表示该BeanFactory可以解析EL表达式)、设置类型转化服务(表示该BeanFactory可以进行类型转化)、可以添加BeanPostProcessor(表示该BeanFactory支持Bean的后置处理器),可以合并BeanDefinition,可以销毁某个Bean等等功能
  10. FactoryBeanRegistrySupport:支持了FactoryBean的功能
  11. AutowireCapableBeanFactory:是直接继承了BeanFactory,在BeanFactory的基础上,支持在创建Bean的过程中能对Bean进行自动装配
  12. AbstractBeanFactory:实现了ConfigurableBeanFactory接口,继承了FactoryBeanRegistrySupport,这个BeanFactory的功能已经很全面了,但是不能自动装配和获取beanNames
  13. ConfigurableListableBeanFactory:继承了ListableBeanFactory、AutowireCapableBeanFactory、ConfigurableBeanFactory
  14. AbstractAutowireCapableBeanFactory:继承了AbstractBeanFactory,实现了AutowireCapableBeanFactory,拥有了自动装配的功能
  15. DefaultListableBeanFactory:继承了AbstractAutowireCapableBeanFactory,实现了ConfigurableListableBeanFactory接口和BeanDefinitionRegistry接口,所以DefaultListableBeanFactory的功能很强大

四、ApplicationContext

上面有分析到,ApplicationContext是个接口,实际上也是一个BeanFactory,不过比BeanFactory更加强大,比如:

  1. HierarchicalBeanFactory:拥有获取父BeanFactory的功能
  2. ListableBeanFactory:拥有获取beanNames的功能
  3. ResourcePatternResolver:资源加载器,可以一次性获取多个资源(文件资源等等)
  4. EnvironmentCapable:可以获取运行时环境(没有设置运行时环境功能)
  5. ApplicationEventPublisher:拥有广播事件的功能(没有添加事件监听器的功能)
  6. MessageSource:拥有国际化功能

先来看ApplicationContext两个比较重要的实现类:

  1. AnnotationConfigApplicationContext
  2. ClassPathXmlApplicationContext

AnnotationConfigApplicationContext

  1. ConfigurableApplicationContext:继承了ApplicationContext接口,增加了,添加事件监听器、添加BeanFactoryPostProcessor、设置Environment,获取ConfigurableListableBeanFactory等功能
  2. AbstractApplicationContext:实现了ConfigurableApplicationContext接口
  3. GenericApplicationContext:继承了AbstractApplicationContext,实现了BeanDefinitionRegistry接口,拥有了所有ApplicationContext的功能,并且可以注册BeanDefinition,注意这个类中有一个属性(DefaultListableBeanFactory beanFactory)
  4. AnnotationConfigRegistry:可以单独注册某个为类为BeanDefinition(可以处理该类上的@Configuration注解,已经可以处理@Bean注解),同时可以扫描
  5. AnnotationConfigApplicationContext:继承了GenericApplicationContext,实现了AnnotationConfigRegistry接口,拥有了以上所有的功能

ClassPathXmlApplicationContext

它也是继承了AbstractApplicationContext,但是相对于AnnotationConfigApplicationContext而言,功能没有AnnotationConfigApplicationContext强大,比如不能注册BeanDefinition

国际化

先定义一个MessageSource:

/*
* 在AppConfig中定义
*/
@Bean
public MessageSource messageSource() {
 ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
 messageSource.setBasename("messages");// Spring 将会在类路径下寻找名为 messages.properties 的消息资源文件
 return messageSource;
}

有了这个Bean,你可以在你任意想要进行国际化的地方使用该MessageSource。
同时,因为ApplicationContext也拥有国家化的功能,所以可以直接这么用:

context.getMessage("test", null, new Locale("en_CN"))

System.out.println(applicationContext.getMessage("test", null, new Locale("en_CN")));
//输出messages.properties中定义的内容(test=a)
//结果--> a

资源加载

ApplicationContext还拥有资源加载的功能,比如,可以直接利用ApplicationContext获取某个文件的内容:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

		Resource resource = context.getResource("file://E:\\Dev\\Spring\\spring-framework-5.3.10\\spring\\src\\main\\java\\com\\bubble\\service\\User.java");
		try {
			System.out.println(resource.contentLength());//内容长度
		} catch (IOException e) {
			throw new RuntimeException(e);
		}

如果不使用ApplicationContext,而是自己来实现这个功能,就比较费时间了。

还比如可以:


AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

Resource resource = context.getResource("file://E:\\Dev\\Spring\\spring-framework-5.3.10\\spring\\src\\main\\java\\com\\bubble\\service\\UserService.java");
System.out.println(resource);//URL [file://E:/Dev/Spring/spring-framework-5.3.10/spring/src/main/java/com/bubble/service/UserService.java]
System.out.println(resource.contentLength());//1146
System.out.println(resource.getFilename());//UserService.java

Resource[] resource = new Resource[]{context.getResource("file://E:\\Dev\\Spring\\spring-framework-5.3.10\\spring\\src\\main\\java\\com\\bubble\\service\\UserService.java")};
System.out.println(resource);//[Lorg.springframework.core.io.Resource;@233c0b17
System.out.println(resource[0]);//URL [file://E:/Dev/Spring/spring-framework-5.3.10/spring/src/main/java/com/bubble/service/UserService.java]

Resource resource1 = context.getResource("https://www.baidu.com");
System.out.println(resource1.contentLength());
System.out.println(resource1.getURL());

Resource resource2 = context.getResource("classpath:spring.xml");
System.out.println(resource2.contentLength());
System.out.println(resource2.getURL());

还可以一次性获取多个:

Resource[] resources = context.getResources("classpath:com/bubble/*.class");
for (Resource resource : resources) {
 System.out.println(resource.contentLength());
 System.out.println(resource.getFilename());
}

获取运行时环境

1. context.getEnvironment().getSystemEnvironment()

  • 这个方法用于获取系统环境变量的信息。
  • 返回一个包含系统环境变量键值对的 Map。
  • 系统环境变量是操作系统级别的变量,它们对整个操作系统及其中运行的所有应用程序都是可见的。
  • 例如,系统环境变量可以包括操作系统的路径变量、用户账户信息等。

2. context.getEnvironment().getSystemProperties()

  • 这个方法用于获取系统属性的信息。
  • 返回一个包含系统属性键值对的 Map。
  • 系统属性是 Java 虚拟机(JVM)级别的属性,它们会影响整个 JVM 实例,包括应用程序在内。
  • 例如,系统属性可以包括 Java 版本、操作系统信息等。

3. context.getEnvironment().getPropertySources()

  • 这个方法用于获取 Spring 环境属性源(PropertySources)的信息。
  • 返回一个表示 Spring 环境属性源的集合,这些属性源包含了应用程序配置的属性信息。
  • 属性源可以来自多种来源,比如系统环境变量、系统属性、属性文件等。
  • 通过属性源,Spring 可以获取应用程序配置的各种属性值。
// 创建一个 AnnotationConfigApplicationContext 上下文,基于注解配置类 AppConfig 进行初始化
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

// 获取系统环境变量
Map<String, Object> systemEnvironment = context.getEnvironment().getSystemEnvironment();
System.out.println(systemEnvironment);

System.out.println("=======");

// 获取系统属性
Map<String, Object> systemProperties = context.getEnvironment().getSystemProperties();
System.out.println(systemProperties);

System.out.println("=======");

// 获取属性源信息
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
System.out.println(propertySources);

System.out.println("=======");

System.out.println(context.getEnvironment().getProperty("NO_PROXY"));
System.out.println(context.getEnvironment().getProperty("sun.jnu.encoding"));
System.out.println(context.getEnvironment().getProperty("bubble"));//在spring.properties中配置了bubble=xxx,输出结果:xxx

运行结果:

{USERDOMAIN_ROAMINGPROFILE=MSID, PROCESSOR_LEVEL=6, SESSIONNAME=Console, 
 ALLUSERSPROFILE=C:\ProgramData, PROCESSOR_ARCHITECTURE=AMD64, 
 GATEWAY_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\gateway.vmoptions, 
 PSModulePath=C:\Program Files\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules, 
 SystemDrive=C:, EFC_8128=1, MAVEN_HOME=E:\Dev\dep\maven3.9.0\apache-maven-3.9.0, 
 RIDER_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\rider.vmoptions, USERNAME=DSC, 
 DEVECOSTUDIO_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\devecostudio.vmoptions, 
 STUDIO_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\studio.vmoptions, 
 ProgramFiles(x86)=C:\Program Files (x86), FPS_BROWSER_USER_PROFILE_STRING=Default, 
 PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW, 
 APPCODE_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\appcode.vmoptions, 
 DriverData=C:\Windows\System32\Drivers\DriverData, 
 DATASPELL_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\dataspell.vmoptions, 
 ProgramData=C:\ProgramData, ProgramW6432=C:\Program Files,
 HOMEPATH=\Users\DSC, PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 183 Stepping 1, GenuineIntel, 
 ProgramFiles=C:\Program Files, PUBLIC=C:\Users\Public, windir=C:\Windows, =::=::\, ZES_ENABLE_SYSMAN=1, 
 DATAGRIP_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\datagrip.vmoptions, 
 LOCALAPPDATA=C:\Users\DSC\AppData\Local, IntelliJ IDEA=E:\Software\IDEA\IntelliJ IDEA 2022.3.2\bin;, 
 USERDOMAIN=MSID, FPS_BROWSER_APP_PROFILE_STRING=Internet Explorer, 
 LOGONSERVER=\\MSID, PYCHARM_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\pycharm.vmoptions, 
 WEBSTORM_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\webstorm.vmoptions, 
 JAVA_HOME=C:\Program Files\Java\jdk1.8.0_333, CLION_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\clion.vmoptions, 
 JETBRAINSCLIENT_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\jetbrainsclient.vmoptions, 
 GOLAND_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\goland.vmoptions, OneDrive=C:\Users\DSC\OneDrive, 
 APPDATA=C:\Users\DSC\AppData\Roaming, IDEA_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\idea.vmoptions, 
 RUBYMINE_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\rubymine.vmoptions, 
 JETBRAINS_CLIENT_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\jetbrains_client.vmoptions, 
 CommonProgramFiles=C:\Program Files\Common Files, Path=E:\Oracle\Oracle\WINDOWS.X64_193000_db_home\bin;
 E:\Dev\dep\Python\Python310\Scripts\;E:\Dev\dep\Python\Python310\;E:\Software\VMware\Workstation\bin\;
 C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;
C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\Java\jdk1.8.0_333\bin;
E:\Dev\dep\maven3.9.0\apache-maven-3.9.0\bin;D:\Software\Bandizip\;
E:\Software\Git\cmd;E:\Software\Node\nodejs\;E:\MySQL\MySQL Server 8.0\bin;
E:\MySQL\MySQL Shell 8.0\bin\;C:\Users\DSC\AppData\Local\Microsoft\WindowsApps;;
E:\Software\Microsoft VS Code\bin;E:\Software\IDEA\IntelliJ IDEA 2022.3.2\bin;;
C:\Users\DSC\AppData\Roaming\npm, OS=Windows_NT, COMPUTERNAME=MSID, PROCESSOR_REVISION=b701, 
CommonProgramW6432=C:\Program Files\Common Files, ComSpec=C:\Windows\system32\cmd.exe, SystemRoot=C:\Windows, 
TEMP=C:\Users\DSC\AppData\Local\Temp, WEBIDE_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\webide.vmoptions, HOMEDRIVE=C:, 
USERPROFILE=C:\Users\DSC, TMP=C:\Users\DSC\AppData\Local\Temp,
CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files, NUMBER_OF_PROCESSORS=24, 
PHPSTORM_VM_OPTIONS=E:\Software\IDEA\jetbra\vmoptions\phpstorm.vmoptions, IDEA_INITIAL_DIRECTORY=C:\Users\DSC\Desktop}

=======

{java.runtime.name=Java(TM) SE Runtime Environment, sun.boot.library.path=C:\Program Files\Java\jdk1.8.0_333\jre\bin, 
java.vm.version=25.333-b02, java.vm.vendor=Oracle Corporation, java.vendor.url=http://java.oracle.com/, 
path.separator=;, java.vm.name=Java HotSpot(TM) 64-Bit Server VM, file.encoding.pkg=sun.io, user.country=CN, user.script=, 
sun.java.launcher=SUN_STANDARD, sun.os.patch.level=, java.vm.specification.name=Java Virtual Machine Specification, 
user.dir=E:\Dev\Spring\spring-framework-5.3.10, java.runtime.version=1.8.0_333-b02, 
java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment, 
java.endorsed.dirs=C:\Program Files\Java\jdk1.8.0_333\jre\lib\endorsed, os.arch=amd64, 
java.io.tmpdir=C:\Users\DSC\AppData\Local\Temp\, line.separator=
, java.vm.specification.vendor=Oracle Corporation, user.variant=, os.name=Windows 11, sun.jnu.encoding=GBK, 
java.library.path=C:\Program Files\Java\jdk1.8.0_333\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;
E:\Oracle\Oracle\WINDOWS.X64_193000_db_home\bin;E:\Dev\dep\Python\Python310\Scripts\;E:\Dev\dep\Python\Python310\;
E:\Software\VMware\Workstation\bin\;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;
C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;
C:\Windows\System32\OpenSSH\;C:\Program Files\Java\jdk1.8.0_333\bin;E:\Dev\dep\maven3.9.0\apache-maven-3.9.0\bin;
D:\Software\Bandizip\;E:\Software\Git\cmd;E:\Software\Node\nodejs\;
E:\MySQL\MySQL Server 8.0\bin;E:\MySQL\MySQL Shell 8.0\bin\;C:\Users\DSC\AppData\Local\Microsoft\WindowsApps;;
E:\Software\Microsoft VS Code\bin;E:\Software\IDEA\IntelliJ IDEA 2022.3.2\bin;;
C:\Users\DSC\AppData\Roaming\npm;., java.specification.name=Java Platform API Specification, java.class.version=52.0, 
sun.management.compiler=HotSpot 64-Bit Tiered Compilers, os.version=10.0, 
user.home=C:\Users\DSC, user.timezone=Asia/Shanghai, java.awt.printerjob=sun.awt.windows.WPrinterJob, 
file.encoding=UTF-8, java.specification.version=1.8, java.class.path=C:\Program Files\Java\jdk1.8.0_333\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\deploy.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\cldrdata.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\jaccess.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\jfxrt.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\nashorn.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunec.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunmscapi.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\zipfs.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\jfxswt.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\management-agent.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\rt.jar;E:\Dev\Spring\spring-framework-5.3.10\spring\out\production\classes;
E:\Dev\Spring\spring-framework-5.3.10\spring\out\production\resources;
E:\Dev\Spring\spring-framework-5.3.10\spring-core\build\libs\spring-cglib-repack-3.3.0.jar;
E:\Dev\Spring\spring-framework-5.3.10\spring-core\build\libs\spring-objenesis-repack-3.2.jar;
E:\Dev\Spring\spring-framework-5.3.10\spring-jdbc\out\production\classes;
E:\Dev\Spring\spring-framework-5.3.10\spring-jdbc\out\production\resources;
E:\Dev\Spring\spring-framework-5.3.10\spring-web\out\production\classes;
E:\Dev\Spring\spring-framework-5.3.10\spring-web\out\production\resources;
E:\Dev\Spring\spring-framework-5.3.10\spring-context\out\production\classes;
E:\Dev\Spring\spring-framework-5.3.10\spring-context\out\production\resources;
E:\Dev\Spring\spring-framework-5.3.10\spring-aop\out\production\classes;
E:\Dev\Spring\spring-framework-5.3.10\spring-aop\out\production\resources;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.mybatis\mybatis-spring\2.0.6\eae03712acdf041a3590b816460945d4bd2691bc\mybatis-spring-2.0.6.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\mysql\mysql-connector-java\8.0.21\53fd3a0887667aae7e40a3439fff2d03d93ec4c2\mysql-connector-java-8.0.21.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.mybatis\mybatis\3.4.5\5200759b13f70652995fd206a52fdc98b01c65cd\mybatis-3.4.5.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.aspectj\aspectjrt\1.9.5\dc063f2557f6734ccb529b4c1d97132e4c8c739\aspectjrt-1.9.5.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.aspectj\aspectjweaver\1.9.5\1740dc9140103b796d1722668805fd4cf852780c\aspectjweaver-1.9.5.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\javax.annotation\javax.annotation-api\1.2\479c1e06db31c432330183f5cae684163f186146\javax.annotation-api-1.2.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\javax.servlet\javax.servlet-api\3.1.0\3cd63d075497751784b2fa84be59432f4905bf7c\javax.servlet-api-3.1.0.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\javax.inject\javax.inject\1\6975da39a7040257bd51d21a231b76c915872d38\javax.inject-1.jar;
E:\Dev\Spring\spring-framework-5.3.10\spring-tx\out\production\classes;E:\Dev\Spring\spring-framework-5.3.10\spring-tx\out\production\resources;
E:\Dev\Spring\spring-framework-5.3.10\spring-beans\out\production\classes;E:\Dev\Spring\spring-framework-5.3.10\spring-beans\out\production\resources;
E:\Dev\Spring\spring-framework-5.3.10\spring-core\out\production\classes;E:\Dev\Spring\spring-framework-5.3.10\spring-core\out\production\resources;
E:\Dev\Spring\spring-framework-5.3.10\spring-oxm\out\production\classes;E:\Dev\Spring\spring-framework-5.3.10\spring-oxm\out\production\resources;
E:\Dev\Spring\spring-framework-5.3.10\spring-expression\out\production\classes;
E:\Dev\Spring\spring-framework-5.3.10\spring-expression\out\production\resources;
E:\Dev\Spring\spring-framework-5.3.10\spring-instrument\out\production\classes;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\com.google.protobuf\protobuf-java\3.11.4\7ec0925cc3aef0335bbc7d57edfd42b0f86f8267\protobuf-java-3.11.4.jar;
E:\Dev\Spring\spring-framework-5.3.10\spring-jcl\out\production\classes;E:\Dev\Spring\spring-framework-5.3.10\spring-jcl\out\production\resources;
E:\Dev\Spring\spring-framework-5.3.10\spring-core\kotlin-coroutines\out\production\classes;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-reflect\1.5.21\802f1f39735ae1eb2b75714a40fa19bb2e687e96\kotlin-reflect-1.5.21.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlinx\kotlinx-coroutines-reactor\1.5.1\10892c3871bf20d2b6649f9910f489dfc4c2f7de\kotlinx-coroutines-reactor-1.5.1.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib\1.5.21\2f537cad7e9eeb9da73738c8812e1e4cf9b62e4e\kotlin-stdlib-1.5.21.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\io.projectreactor\reactor-core\3.4.8\a654cf9f8f29f09339d80cc30a925f69137de258\reactor-core-3.4.8.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlinx\kotlinx-coroutines-reactive\1.5.1\438f174cd1f8806c34eead62bf3c53e7a89bf737\kotlinx-coroutines-reactive-1.5.1.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib-jdk8\1.5.21\6b3de2a43405a65502728047db37a98a0c7e72f0\kotlin-stdlib-jdk8-1.5.21.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlinx\kotlinx-coroutines-core-jvm\1.5.1\59d09c330d2977b39e1e4c0e3711fc903e1b46b0\kotlinx-coroutines-core-jvm-1.5.1.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.jetbrains\annotations\13.0\919f0dfe192fb4e063e7dacadee7f8bb9a2672a9\annotations-13.0.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib-common\1.5.21\cc8bf3586fd2ebcf234058b9440bb406e62dfacb\kotlin-stdlib-common-1.5.21.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.reactivestreams\reactive-streams\1.0.3\d9fb7a7926ffa635b3dcaa5049fb2bfa25b3e7d0\reactive-streams-1.0.3.jar;
E:\Dev\Spring\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib-jdk7\1.5.21\f059658740a4b3a3461aba9681457615332bae1c\kotlin-stdlib-jdk7-1.5.21.jar;
E:\Software\IDEA\IntelliJ IDEA 2022.3.2\lib\idea_rt.jar, user.name=DSC, java.vm.specification.version=1.8, sun.java.command=com.bubble.Test, 
java.home=C:\Program Files\Java\jdk1.8.0_333\jre, sun.arch.data.model=64, user.language=zh, java.specification.vendor=Oracle Corporation, awt.toolkit=sun.awt.windows.WToolkit, 
java.vm.info=mixed mode, java.version=1.8.0_333, java.ext.dirs=C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext;C:\Windows\Sun\Java\lib\ext, 
sun.boot.class.path=C:\Program Files\Java\jdk1.8.0_333\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\rt.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\sunrsasign.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_333\jre\classes, 
java.vendor=Oracle Corporation, file.separator=\, java.vendor.url.bug=
http://bugreport.sun.com/bugreport/, sun.io.unicode.encoding=UnicodeLittle, sun.cpu.endian=little, sun.desktop=windows, sun.cpu.isalist=amd64}

=======

[PropertiesPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}, ResourcePropertySource {name='class path resource [spring.properties]'}]

=======

null
GBK
xxx

注意,可以利用

// @PropertySource("classpath:spring.properties")

/*
* AppConfig.java
* 可以在 Spring 的配置类上使用 @PropertySource 注解来指定属性文件的位置
*/
@Configuration
@PropertySource("classpath:spring.properties")
public class AppConfig {
    // 这里可以定义其他的 Bean 和配置
}
/*
* spring.properties
*/
app.name=MyApp
app.version=1.0
/*
* MyComponent.java
*/
@Component
public class MyComponent {
    @Value("${app.name}")
    private String appName;

    @Value("${app.version}")
    private String appVersion;

    // 其他业务逻辑
}

来使得某个properties文件中的参数添加到运行时环境中

事件发布

先定义一个事件监听器

/*
* AppConfig.java
*/
@Bean
public ApplicationListener applicationListener() {
 return new ApplicationListener() {
  @Override
  public void onApplicationEvent(ApplicationEvent event) {
   System.out.println("接收到了一个事件");
  }
 };
}

然后发布一个事件:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
context.publishEvent("kkk");


//运行结果

接收到了一个事件
接收到了一个事件

/*
看起来结果出现两次输出 "接收到了一个事件" 的情况,可能是由于事件发布的方式导致的,可能的原因:

事件发布:
当你调用 applicationContext.publishEvent("kkk"); 时,
你实际上是发布了一个 PayloadApplicationEvent 事件,其中 kkk 是事件的负载(payload)。
由于你的 ApplicationListener 没有对事件的类型进行过滤,它会对所有类型的事件作出反应,包括 PayloadApplicationEvent。

触发事件:
在 AnnotationConfigApplicationContext 初始化过程中,可能会触发一些初始化事件。
因此,在创建应用程序上下文时,可能会触发一个或多个事件,导致 "接收到了一个事件" 输出多次。
*/

五、类型转化

在Spring源码中,有可能需要把String转成其他类型,所以在Spring源码中提供了一些技术来更方便的做对象的类型转化,关于类型转化的应用场景, 后续看源码的过程中会遇到很多。

PropertyEditor

这其实是JDK中提供的类型转化工具类

public class StringToUserPropertyEditor extends PropertyEditorSupport implements PropertyEditor {

 @Override
 public void setAsText(String text) throws IllegalArgumentException {
  User user = new User();
  user.setName(text);
  this.setValue(user);
 }
}
/*
* Test
*/
StringToUserPropertyEditor propertyEditor = new StringToUserPropertyEditor();
propertyEditor.setAsText("1");
User value = (User) propertyEditor.getValue();
System.out.println(value);//com.bubble.service.User@7e0ea639

如何向Spring中注册PropertyEditor:

/*
* AppConfig.java
*/
@Bean
public CustomEditorConfigurer customEditorConfigurer() {
 CustomEditorConfigurer customEditorConfigurer = new CustomEditorConfigurer();
 Map<Class<?>, Class<? extends PropertyEditor>> propertyEditorMap = new HashMap<>();
    
 // 表示StringToUserPropertyEditor可以将String转化成User类型,在Spring源码中,
 //如果发现当前对象是String,而需要的类型是User,就会使用该PropertyEditor来做类型转化
 propertyEditorMap.put(User.class, StringToUserPropertyEditor.class);
 customEditorConfigurer.setCustomEditors(propertyEditorMap);
 return customEditorConfigurer;
}
/*
* Test
*/
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();

假设现在有如下Bean,那么test属性就能正常的完成属性赋值

@Component
public class UserService {
	@Value("xxx")
	private User user;

	public void test() {
		System.out.println(user);//com.bubble.service.User@17ed40e0
	}
}

ConversionService

Spring中提供的类型转化服务,它比PropertyEditor更强大

/**
 * 自定义类型转换器:将String类型转换为User类型的转换器
 */
public class StringToUserConverter implements ConditionalGenericConverter {

/**
 * 判断是否满足类型转换条件
 *
 * @param sourceType   源类型描述符
 * @param targetType   目标类型描述符
 * @return 如果源类型是String且目标类型是User,返回true;否则返回false
 */
 @Override
 public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
  return sourceType.getType().equals(String.class) && targetType.getType().equals(User.class);
 }

/**
 * 获取可转换的类型对
 *
 * @return 包含String到User类型转换的可转换类型对
 */
 @Override
 public Set<ConvertiblePair> getConvertibleTypes() {
  return Collections.singleton(new ConvertiblePair(String.class, User.class));
 }

 /**
 * 执行实际的类型转换
 *
 * @param source       源对象
 * @param sourceType   源类型描述符
 * @param targetType   目标类型描述符
 * @return 转换后的User对象,其中名称为源String对象的值
 */
 @Override
 public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
  User user = new User();
  user.setName((String)source);
  return user;
 }
}
/*
* Test
*/
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToUserConverter());
User value = conversionService.convert("1", User.class);
System.out.println(value);//com.bubble.service.User@41cf53f9

如何向Spring中注册ConversionService:

@Bean
public ConversionServiceFactoryBean conversionService() {
 ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
 conversionServiceFactoryBean.setConverters(Collections.singleton(new StringToUserConverter()));

 return conversionServiceFactoryBean;
}
/*
* Test
*/
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
@Component
public class UserService {
	@Value("xxx")
	private User user;

	public void test() {
		System.out.println(user);//com.bubble.service.User@47fd17e3
	}
}

TypeConverter

整合了PropertyEditor和ConversionService的功能,是Spring内部用的

SimpleTypeConverter typeConverter = new SimpleTypeConverter();
typeConverter.registerCustomEditor(User.class, new StringToUserPropertyEditor());
//typeConverter.setConversionService(conversionService);
User value = typeConverter.convertIfNecessary("1", User.class);
System.out.println(value);//com.bubble.service.User@5b2133b1

六、OrderComparator

OrderComparator是Spring所提供的一种比较器,可以用来根据@Order注解或实现Ordered接口来执行值进行笔记,从而可以进行排序。

比如:


public class A implements Ordered {

 @Override
 public int getOrder() {
  return 3;
 }

 @Override
 public String toString() {
  return this.getClass().getSimpleName();
 }
}
public class B implements Ordered {

 @Override
 public int getOrder() {
  return 2;
 }

 @Override
 public String toString() {
  return this.getClass().getSimpleName();
 }
}
public class Main {

 public static void main(String[] args) {
  A a = new A(); // order=3
  B b = new B(); // order=2

  OrderComparator comparator = new OrderComparator();
  System.out.println(comparator.compare(a, b));  // 1

  List list = new ArrayList<>();
  list.add(a);
  list.add(b);

  // 按order值升序排序
  list.sort(comparator);

  System.out.println(list);  // B,A
 }
}

另外,Spring中还提供了一个OrderComparator的子类:AnnotationAwareOrderComparator,它支持用@Order来指定order值。比如:

@Order(3)
public class A {

 @Override
 public String toString() {
  return this.getClass().getSimpleName();
 }

}
@Order(2)
public class B {

 @Override
 public String toString() {
  return this.getClass().getSimpleName();
 }

}
public class Main {

 public static void main(String[] args) {
  A a = new A(); // order=3
  B b = new B(); // order=2

  AnnotationAwareOrderComparator comparator = new AnnotationAwareOrderComparator();
  System.out.println(comparator.compare(a, b)); // 1

  List list = new ArrayList<>();
  list.add(a);
  list.add(b);

  // 按order值升序排序
  list.sort(comparator);

  System.out.println(list); // B,A
 }
}

七、BeanPostProcessor

BeanPostProcess表示Bean的后置处理器,可以定义一个或多个BeanPostProcessor,比如通过一下代码定义一个BeanPostProcessor:

@Component
public class BubbleBeanPostProcessor implements BeanPostProcessor {

 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  if ("userService".equals(beanName)) {
   System.out.println("初始化前"+bean);
  }

  return bean;
 }

 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  if ("userService".equals(beanName)) {
   System.out.println("初始化后"+bean);
  }

  return bean;
 }
}
@Component
public class UserService {

	@Autowired
	private OrderService orderService;

	public void test(){
		System.out.println("test---> "+orderService);
	}
}
/*
* AppConfig.java
*/
@Bean
public UserService userService() {
	UserService userService = new UserService();
	return userService;
}
/*
* Test
*/
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();

// 运行结果
初始化前com.bubble.service.UserService@22a71081
初始化后com.bubble.service.UserService@22a71081
test---> com.bubble.service.OrderService@763d9750

注释Bean

//	@Bean
//	public UserService userService() {
//		UserService userService = new UserService();
//		return userService;
//	}
@Component("userService")
public class BubbleFactoryBean implements FactoryBean {

	@Override
	public Object getObject() throws Exception {
		UserService userService = new UserService();

		return userService;
	}

	@Override
	public Class<?> getObjectType() {
		return UserService.class;
	}
}
//Test运行结果:
初始化前com.bubble.service.BubbleFactoryBean@2be94b0f
初始化后com.bubble.service.BubbleFactoryBean@2be94b0f
初始化后com.bubble.service.UserService@66048bfd
test---> null

一个BeanPostProcessor可以在任意一个Bean初始化之前以及初始化之后去额外的做一些用户自定义的逻辑,当然,可以通过判断beanName来进行针对性处理(针对某个Bean,或某部分Bean)。

可以通过定义BeanPostProcessor来干涉Spring创建Bean的过程。

八、BeanFactoryPostProcessor

BeanFactoryPostProcessor表示Bean工厂的后置处理器,其实和BeanPostProcessor类似,BeanPostProcessor是干涉Bean的创建过程,BeanFactoryPostProcessor是干涉BeanFactory的创建过程。比如,可以这样定义一个BeanFactoryPostProcessor:

@Component
public class BubbleBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  System.out.println("加工beanFactory");
 }
}
接以上代码,运行结果:
加工beanFactory
初始化前com.bubble.service.BubbleFactoryBean@50675690
初始化后com.bubble.service.BubbleFactoryBean@50675690
初始化后com.bubble.service.UserService@61a52fbd
test---> null

可以在postProcessBeanFactory()方法中对BeanFactory进行加工。

九、FactoryBean

上面提到,可以通过BeanPostPorcessor来干涉Spring创建Bean的过程,但是如果想一个Bean完完全全由我们来创造,也是可以的,比如通过FactoryBean:

@Component
public class BubbleFactoryBean implements FactoryBean {

 @Override
 public Object getObject() throws Exception {
  UserService userService = new UserService();//UserService没有完整的bean生命周期

  return userService;
 }

 @Override
 public Class<?> getObjectType() {
  return UserService.class;
 }
}
// 创建一个Spring容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
// userService.java无@Component注解
System.out.println(applicationContext.getBean("bubbleFactoryBean"));//com.bubble.service.UserService@5474c6c
System.out.println(applicationContext.getBean("&bubbleFactoryBean"));//com.bubble.service.BubbleFactoryBean@5fe5c6f
System.out.println(applicationContext.getBean("userService"));//No bean named 'userService' available
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();


//运行结果
com.bubble.service.UserService@5474c6c
com.bubble.service.BubbleFactoryBean@5fe5c6f
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userService' available
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:881)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1367)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:313)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1198)
	at com.bubble.Test.main(Test.java:57)

通过上面这段代码,我们自己创造了一个UserService对象,并且它将成为Bean。但是通过这种方式创造出来的UserService的Bean,只会经过初始化后,其他Spring的生命周期步骤是不会经过的,比如依赖注入。

通过@Bean也可以自己生成一个对象作为Bean,那么和FactoryBean的区别是什么呢?其实在很多场景下他俩是可以替换的,但是站在原理层面来说的,区别很明显,@Bean定义的Bean是会经过完整的Bean生命周期的。

十、ExcludeFilter和IncludeFilter

这两个Filter是Spring扫描过程中用来过滤的。ExcludeFilter表示排除过滤器,IncludeFilter表示包含过滤器

比如以下配置,表示扫描com.bubble这个包下面的所有类,但是排除UserService类,也就是就算它上面有@Component注解也不会成为Bean。

@ComponentScan(value = "com.bubble",
		excludeFilters = {@ComponentScan.Filter(
				type = FilterType.ASSIGNABLE_TYPE,
				classes = UserService.class)})
public class AppConfig {
}

再比如以下配置,就算UserService类上没有@Component注解,它也会被扫描成为一个Bean。


@ComponentScan(value = "com.bubble",
		includeFilters = {@ComponentScan.Filter(
				type = FilterType.ASSIGNABLE_TYPE,
				classes = UserService.class)})
public class AppConfig {
}

FilterType分为:

  1. ANNOTATION:表示是否包含某个注解
  2. ASSIGNABLE_TYPE:表示是否是某个类
  3. ASPECTJ:表示否是符合某个Aspectj表达式
  4. REGEX:表示是否符合某个正则表达式
  5. CUSTOM:自定义

在Spring的扫描逻辑中,默认会添加一个AnnotationTypeFilter给includeFilters,表示默认情况下Spring扫描过程中会认为类上有@Component注解的就是Bean。

十一、MetadataReader、ClassMetadata、AnnotationMetadata

在Spring中需要去解析类的信息,比如类名、类中的方法、类上的注解,这些都可以称之为类的元数据,所以Spring中对类的元数据做了抽象,并提供了一些工具类。

MetadataReader表示类的元数据读取器,默认实现类为SimpleMetadataReader。比如:

public class Test {

	public static void main(String[] args) throws IOException {

		SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();

		// 构造一个MetadataReader
		MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader("com.bubble.service.UserService");

		// 得到一个ClassMetadata,并获取了类名
		ClassMetadata classMetadata = metadataReader.getClassMetadata();

		System.out.println(classMetadata.getClassName());//com.bubble.service.UserService

		// 获取一个AnnotationMetadata,并获取类上的注解信息
		AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
		//用于检查给定的注解元信息是否有某个特定注解的元注解。
		//返回 true 表示注解元信息上存在指定注解的元注解,即某个注解是另一个注解的元注解。
		//返回 false 表示注解元信息上没有指定注解的元注解。
		System.out.println(annotationMetadata.hasMetaAnnotation(Component.class.getName()));
        
		//用于检查给定的注解元信息是否直接标注了某个特定注解。
		// 返回 true 表示注解元信息上直接标注了指定注解。
		// 返回 false 表示注解元信息上没有直接标注指定注解。
		System.out.println(annotationMetadata.hasAnnotation(Component.class.getName()));

		for (String annotationType : annotationMetadata.getAnnotationTypes()) {
				System.out.println(annotationType);
				//输出结果
				//org.springframework.stereotype.Component
				//org.springframework.core.annotation.Order
			}
	}
}

需要注意的是,SimpleMetadataReader去解析类时,使用的ASM技术来解析类的元数据,可以在不加载类的情况下获取类的信息。

为什么要使用ASM技术,Spring启动的时候需要去扫描,如果指定的包路径比较宽泛,那么扫描的类是非常多的,那如果在Spring启动时就把这些类全部加载进JVM了,这样不太好,所以使用了ASM技术。

ASM官网

ASM是一个通用的 Java 字节码操作和分析框架。它可用于直接以二进制形式修改现有类或动态生成类。ASM 提供了一些常见的字节码转换和分析算法,可以从中构建自定义的复杂转换和代码分析工具。ASM 提供与其他 Java 字节码框架类似的功能,但重点关注性能。因为它的设计和实现尽可能小且尽可能快,所以它非常适合在动态系统中使用(但当然也可以以静态方式使用,例如在编译器中)。

ASM 在许多项目中都有使用,包括:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值