目录
ClassPathBeanDefinitionScanner
AnnotationConfigApplicationContext
ClassPathXmlApplicationContext
十一、MetadataReader、ClassMetadata、AnnotationMetadata
一、BeanDefinition
BeanDefinition表示Bean定义,BeanDefinition中存在很多属性用来描述一个Bean的特点。比如:
- class,表示Bean类型
- scope,表示Bean作用域,单例或原型等
- lazyInit:表示Bean是否是懒加载
- initMethodName:表示Bean初始化时要执行的方法
- destroyMethodName:表示Bean销毁时要执行的方法
- 还有很多...
在Spring中,经常会通过以下几种方式来定义Bean:
- <bean/>
- @Bean
- @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的类继承实现结构来看
它实现了很多接口,表示,它拥有很多功能:
- AliasRegistry:支持别名功能,一个名字可以对应多个别名
- BeanDefinitionRegistry:可以注册、保存、移除、获取某个BeanDefinition
- BeanFactory:Bean工厂,可以根据某个bean的名字、或类型、或别名获取某个Bean对象
- SingletonBeanRegistry:可以直接注册、获取某个单例Bean
- SimpleAliasRegistry:它是一个类,实现了AliasRegistry接口中所定义的功能,支持别名功能
- ListableBeanFactory:在BeanFactory的基础上,增加了其他功能,可以获取所有BeanDefinition的beanNames,可以根据某个类型获取对应的beanNames,可以根据某个类型获取{类型:对应的Bean}的映射关系
- HierarchicalBeanFactory:在BeanFactory的基础上,添加了获取父BeanFactory的功能
- DefaultSingletonBeanRegistry:它是一个类,实现了SingletonBeanRegistry接口,拥有了直接注册、获取某个单例Bean的功能
- ConfigurableBeanFactory:在HierarchicalBeanFactory和SingletonBeanRegistry的基础上,添加了设置父BeanFactory、类加载器(表示可以指定某个类加载器进行类的加载)、设置Spring EL表达式解析器(表示该BeanFactory可以解析EL表达式)、设置类型转化服务(表示该BeanFactory可以进行类型转化)、可以添加BeanPostProcessor(表示该BeanFactory支持Bean的后置处理器),可以合并BeanDefinition,可以销毁某个Bean等等功能
- FactoryBeanRegistrySupport:支持了FactoryBean的功能
- AutowireCapableBeanFactory:是直接继承了BeanFactory,在BeanFactory的基础上,支持在创建Bean的过程中能对Bean进行自动装配
- AbstractBeanFactory:实现了ConfigurableBeanFactory接口,继承了FactoryBeanRegistrySupport,这个BeanFactory的功能已经很全面了,但是不能自动装配和获取beanNames
- ConfigurableListableBeanFactory:继承了ListableBeanFactory、AutowireCapableBeanFactory、ConfigurableBeanFactory
- AbstractAutowireCapableBeanFactory:继承了AbstractBeanFactory,实现了AutowireCapableBeanFactory,拥有了自动装配的功能
- DefaultListableBeanFactory:继承了AbstractAutowireCapableBeanFactory,实现了ConfigurableListableBeanFactory接口和BeanDefinitionRegistry接口,所以DefaultListableBeanFactory的功能很强大
四、ApplicationContext
上面有分析到,ApplicationContext是个接口,实际上也是一个BeanFactory,不过比BeanFactory更加强大,比如:
- HierarchicalBeanFactory:拥有获取父BeanFactory的功能
- ListableBeanFactory:拥有获取beanNames的功能
- ResourcePatternResolver:资源加载器,可以一次性获取多个资源(文件资源等等)
- EnvironmentCapable:可以获取运行时环境(没有设置运行时环境功能)
- ApplicationEventPublisher:拥有广播事件的功能(没有添加事件监听器的功能)
- MessageSource:拥有国际化功能
先来看ApplicationContext两个比较重要的实现类:
- AnnotationConfigApplicationContext
- ClassPathXmlApplicationContext
AnnotationConfigApplicationContext
- ConfigurableApplicationContext:继承了ApplicationContext接口,增加了,添加事件监听器、添加BeanFactoryPostProcessor、设置Environment,获取ConfigurableListableBeanFactory等功能
- AbstractApplicationContext:实现了ConfigurableApplicationContext接口
- GenericApplicationContext:继承了AbstractApplicationContext,实现了BeanDefinitionRegistry接口,拥有了所有ApplicationContext的功能,并且可以注册BeanDefinition,注意这个类中有一个属性(DefaultListableBeanFactory beanFactory)
- AnnotationConfigRegistry:可以单独注册某个为类为BeanDefinition(可以处理该类上的@Configuration注解,已经可以处理@Bean注解),同时可以扫描
- 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分为:
- ANNOTATION:表示是否包含某个注解
- ASSIGNABLE_TYPE:表示是否是某个类
- ASPECTJ:表示否是符合某个Aspectj表达式
- REGEX:表示是否符合某个正则表达式
- 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是一个通用的 Java 字节码操作和分析框架。它可用于直接以二进制形式修改现有类或动态生成类。ASM 提供了一些常见的字节码转换和分析算法,可以从中构建自定义的复杂转换和代码分析工具。ASM 提供与其他 Java 字节码框架类似的功能,但重点关注性能。因为它的设计和实现尽可能小且尽可能快,所以它非常适合在动态系统中使用(但当然也可以以静态方式使用,例如在编译器中)。
ASM 在许多项目中都有使用,包括:
- OpenJDK ,生成lambda 调用站点,也在Nashorn编译器中,
- Groovy编译器和Kotlin编译器,
- Cobertura和Jacoco,用于检测 类以测量代码覆盖率,
- Byte Buddy,用于动态生成类,本身用于其他项目,例如Mockito(用于生成模拟类),
- Gradle,在运行时生成一些类。