首页 > 编程语言 >Spring源码架构-核心概念解析

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

时间:2024-07-27 20:28:23浏览次数:16  
标签:架构 Spring Dev 源码 spring java class out

目录

一、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 在许多项目中都有使用,包括:

标签:架构,Spring,Dev,源码,spring,java,class,out
From: https://blog.csdn.net/qq_45061342/article/details/140737249

相关文章

  • 计算机Java项目|基于SpringBoot的智能无人仓库管理的设计与实现
    作者主页:编程指南针作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验,被多个学校常年聘为校外企业导师,指导学生毕业设计并参与学生毕业答辩指导,有较为丰富的相关经验。期待与......
  • 基于springboot的球鞋销售及鞋迷交流系统的开发与实现 /WEB
    摘要计算机网络与信息化管理相配合,可以有效地提高管理人员的工作效能和改进工作的质量。良好的球鞋销售及鞋迷交流系统可以使管理员工作得到更好的管理和应用,并有助于管理员更好地管理球鞋销售及鞋迷交流,并有助于解决人力管理中出现的差错等问题。因此一套好的球鞋销售及鞋......
  • SpringbBoot的运动鞋交易系统/交易网站/Java/web
    摘要近年来,随着网络产业的飞速发展,人们的日常生活和工作方式也随之发生变化。各行各业正在把常规的工作方式与因特网相融合,于是,网上交易系统亦应运而生。与传统的店铺销售相比,网上运动鞋店具有方便、快捷、信息畅通的特点,交易环节的缩减,使交易成本大为降低,消费者选择购物的......
  • 【51单片机仿真】基于51单片机设计的广告机系统仿真&源码&原理图&设计文档
     效果:摘要该系统基于51单片机,通过LED点阵显示字符和简单图案,并实现按键控制。系统可以用于广告机,通过两个按键实现暂停/继续显示和显示方向切换功能。系统包含硬件电路设计和软件编程两部分。目录 第1章绪论 第2章系统分析与总体设计第3章系统的硬件结构实现......
  • 【51单片机仿真】基于51单片机设计的多功能电子时钟(实时时钟闹钟秒表温度检测)系统仿真
    效果:1.项目概述该项目是基于单片机的多功能电子时钟系统,集成了时间显示、闹钟设置、秒表功能以及温度显示等功能。主要硬件包括1602液晶显示屏、DS1302时钟芯片、DS18B20温度传感器和蜂鸣器。系统通过按键进行时间和闹钟的设置,并实时显示当前时间和温度。目录效果:1.......
  • 属性填充底层源码深入剖析前戏
    属性填充底层源码深入剖析前戏方式一:使用set方式注入创建两个类:packagecom.coding.spring.practies;publicclassTestDIBean{ publicStringsay(){ return"IamTestDIBean.say()"; }}packagecom.coding.spring.practies;publicclassTestDIBean2{ priva......
  • 查看 NVIDIA GPU 架构
    NVIDAGPU架构演进可见:https://blog.csdn.net/daijingxin/article/details/1150423532022Hopper2020Ampere2018Turing2017Volta2016Pascal2014Maxwell2012Kepler2010Fermi2008Tesla终端输入下述命令,可以查看所属架构vidia-smi-q|grepArchitecture单......
  • SpringBoot入门实战:SpringBoot整合Shiro
    1.背景介绍SpringBoot是一个用于快速开发Spring应用程序的框架。它的核心是对Spring框架的一层封装,使其更加简单易用。SpringBoot整合Shiro是一种将SpringBoot与Shiro整合的方法,以实现身份验证和授权功能。Shiro是一个强大的Java安全框架,它提供了身份验证、授权、密码存......
  • Spring Boot + Spring Batch + Quartz 整合定时批量任务
     ​ 博客主页:   南来_北往系列专栏:SpringBoot实战前言最近一周,被借调到其他部门,赶一个紧急需求,需求内容如下:PC网页触发一条设备升级记录(下图),后台要定时批量设备更新。这里定时要用到Quartz,批量数据处理要用到SpringBatch,二者结合,可以完成该需求。由于之前,没有......
  • 怎么去架构一个前后端分离的微服务项目
    架构一个微服务项目,尤其是前后端分离的新项目,是一个复杂但系统化的过程。以下是一些关键步骤和考虑因素:需求分析:明确项目目标和功能需求。确定用户角色和权限。设计原则:单一职责原则:每个服务负责一个单一的功能。服务自治:服务之间通过定义良好的API进行通信,尽量避免直接依......