按照常理,上节课我们通过手写模拟了Spring的核心原理之后,已经具备了分析源码的基础。不过,在正式进入源码之前,我们还需要专门用一节课来熟悉Spring中的一些核心组件——它们就像一个个关键“点”。而像配置类解析这样的源码流程,则是一条“线”。只有把这些“点”理解透彻,才能连“线”成“面”,真正把握Spring的全貌。这些组件在Spring实际源码中确实都有应用,但本节课的重点不在于追踪它们具体如何被使用,而是先掌握每个组件本身的作用与使用方法。
主要内容:
核心组件BeanDefinition使用与底层解析
核心组件BeanDefinitionReader使用与底层解析
核心组件AnnotatedBeanDefinitionReader使用与底层解析
核心组件XmlBeanDefinitionReader使用与底层解析
核心组件ClassPathBeanDefinitionScanner使用与底层解析
BeanDefinition合并的使用与底层解析
核心组件BeanFactory使用与底层解析
核心组件DefaultListableBeanFactory使用与底层解析
核心组件ApplicationContext使用与底层解析
ApplicationContext国际化功能的使用与底层解析
ApplicationContext资源加载功能的使用与底层解析
ApplicationContext获取运行时环境功能的使用与底层解析
ApplicationContext事件发布功能的使用与底层解析
类型转化之PropertyEditor的使用与底层解析
类型转化之ConversionService的使用与底层解析
类型转化之TypeConverter的使用与底层解析
Order比较器OrderComparator的使用与底层解析
Order比较器AnnotationAwareOrderComparator的使用与底层解析
BeanDefinition
BeanDefinition是非常非常核心的一个概念,一个BeanDefinition表示一个Bean定义,Spring会根据BeanDefinition来创建具体的Bean对象。
BeanDefinition中有些常用的属性:
beanClass:
scope:
lazyInit
initMethodName
destroyMethodName
...
当我们使用<bean/>、@Bean、@Component等方式定义Bean时,Spring底层就会解析这些标签和注解生成对应的BeanDefinition对象。
上面这些方式可以理解成声明式的定义Bean
我们也可以通过代码的方式(编程式)直接定义和注册BeanDefinition,比如:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
BeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(UserService.class);
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
beanDefinition.setInitMethodName("test"); // 需要在UserService中定义一个test()方法
applicationContext.registerBeanDefinition("userService", beanDefinition);
System.out.println(applicationContext.getBean(UserService.class));
System.out.println(applicationContext.getBean(UserService.class));
System.out.println(applicationContext.getBean(UserService.class));
}
}BeanDefinitionReader
BeanDefinitionReader在我们使用Spring时用得少,但在Spring源码中用得多,相当于Spring源码的基础设施,顾名思义,BeanDefinitionReader是用来解析读取BeanDefinition对象的,主要有两个,一个是BeanDefinitionReader 用于从外部资源(如XML文件)读取配置,一个是 AnnotatedBeanDefinitionReader 用于在程序内部直接注册配置类。
值得一提的是,BeanDefinitionReader是接口主要的实现类是XmlBeanDefinitionReader,而AnnotatedBeanDefinitionReader是单独的类,它没有实现BeanDefinitionReader接口
AnnotatedBeanDefinitionReader
作用是解析某个类上的注解信息从而得到BeanDefinition对象。
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
// 读取的BeanDefinition可能要放入容器,因此构造器要传入容器,只不过容器实现了BeanDefinitionRegistry接口
AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(applicationContext);
// 相当于去解析传入的类,生成BeanDefinition
annotatedBeanDefinitionReader.register(UserService.class);
System.out.println(applicationContext.getBean(UserService.class));
}
}但注意,此时UserService不需要加@Component注解,@Component注解和后面要讲的包扫描才有关系。(调试register方法)
会解析类上的:@Conditional、@Scope、@Primary、@Lazy、@Fallback(Spring6新增注解)、@DependsOn、@Role、@Description
其实这里还可以想到容器对象本身也应该可以注册,AnnotationConfigApplicationContext确实也有一个register方法,它内部就是用的AnnotatedBeanDefinitionReader的注册,就是一个委派。
上面的代码还有个趣的是传入的MyConfig其实最后也会默认被BeanDefinitionReader注册。所以MyConfig也是一个bean。可以验证一下:
// 这三行就相当于 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(MyConfig.class);
applicationContext.refresh();
System.out.println(applicationContext.getBean("myConfig"));这个地方看AnnotationConfigApplicationContext的构造器就明白了
XmlBeanDefinitionReader
AnnotatedBeanDefinitionReader是用来解析类的,XmlBeanDefinitionReader是用来解析xml文件的。
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
// 与AnnotatedBeanDefinitionReader同理,也要传入一个容器
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
// 解析XML文件
beanDefinitionReader.loadBeanDefinitions("spring.xml");
System.out.println(applicationContext.getBean("userService"));
System.out.println(applicationContext.getBean("userService"));
System.out.println(applicationContext.getBean("userService"));
}
}<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.hexon.service.UserService" name="userService"></bean>
</beans>ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner是扫描器,@ComponentScan注解的底层实现就是用的它,它作用是能对某个包路径进行扫描,从而得到BeanDefinition对象,默认情况下,它只能把加了@Component注解的类生成对应的BeanDefinition对象。
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(applicationContext);
scanner.scan("com.hexon");
applicationContext.refresh(); // 容器必须refresh才能使用
System.out.println(applicationContext.getBean("userService"));
}
}此时UserService必须加上@Component注解。
为什么也可以在scan方法之前调用refresh?
Spring 容器必须调用
refresh()方法完成初始化后才能正常使用,这是获取 Bean 的前提条件。虽然技术上可以将
refresh()放在scan()之前调用,但最佳实践仍然是将其置于后面。这是因为:
scan()方法的作用是扫描指定包路径,将符合条件的组件解析为 BeanDefinition 注册到容器中如果先调用
refresh()(且未传入配置类),容器基于当前已注册的 BeanDefinition 初始化 Bean后续通过
scan()扫描注册的 BeanDefinition 不会立即创建 Bean 实例,只有在首次调用getBean()时才会触发实例化,这实际上变成了一种延迟加载的行为因此,为了确保所有 Bean 都能在容器启动时统一初始化,推荐遵循“先扫描注册,后刷新容器”的标准流程。
容器本身也可以scan,委派方式调用scanner实现的。
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.scan("com.hexon");
applicationContext.refresh();
System.out.println(applicationContext.getBean("userService"));
}甚至可以直接传包路径:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.hexon");
// applicationContext.refresh(); // 注意不能重复refresh
System.out.println(applicationContext.getBean("userService"));我们也可以自定义注解进行扫描:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyComponent {
}此时我们的业务类UserService也换成使用我们自己定义的@MyComponent注解:
@MyComponent
public class UserService {
public void test() {
System.out.println("UserService#test....");
}
}下面执行测试:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.scan("com.hexon");
applicationContext.refresh();
System.out.println(applicationContext.getBean("userService"));
}
}此时会报错,因为UserService只加了@MyComponent注解,Spring无法感知。有一种方法处理就是在@MyComponent注解上加上@Component注解,就能解决问题,这个是Spring默认的处理逻辑所支持的,它会判断注解上的注解。
另外一种做法,就是利用扫描器的机制,添加一个Filter:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注意这里必须使用ClassPathBeanDefinitionScanner,容器是没有addIncludeFilter方法的
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(applicationContext);
scanner.addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// MetadataReader类的元数据读取器,暂时先理解成一个类定义信息的抽象
return metadataReader.getAnnotationMetadata().hasAnnotation(MyComponent.class.getName());
}
});
scanner.scan("com.hexon");
applicationContext.refresh();
System.out.println(applicationContext.getBean("userService"));
}
}此时@MyComponent注解的定义:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
// 没有@Component也能工作
public @interface MyComponent {
}此时再定义一个OrderService类:
@Component
public class OrderService {
}容器启动后获取orderService这个名称的Bean,也一样可以,说明并没有影响原本的@Component的工作机制。因为@Component是在默认的Filter里面处理的,这个只要看new ClassPathBeanDefinitionScanner(applicationContext)这个构造器,跟下源码就能看出来includeFilters是一个集合,可以有多个。
BeanDefinition合并
在后续看源码的过程中,Spring在利用BeanDefinition创建Bean对象时,会判断一个BeanDefinition是否存在父BeanDefinition,如果存在则先进行合并,就像父子类一样,子类会继承或覆盖父类的属性,子BeanDefinition会继承或覆盖父BeanDefinition的属性。
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
AnnotatedGenericBeanDefinition parentBeanDefinition = new AnnotatedGenericBeanDefinition(UserService.class);
parentBeanDefinition.setScope("prototype");
context.registerBeanDefinition("parentUserService", parentBeanDefinition);
// 下面的这个Definition未定义多例,但是它继承了父的,所以也是多例
AnnotatedGenericBeanDefinition childBeanDefinition = new AnnotatedGenericBeanDefinition(UserService.class);
childBeanDefinition.setParentName("parentUserService");
context.registerBeanDefinition("childUserService", childBeanDefinition);
context.refresh();
System.out.println(context.getBean("childUserService"));
System.out.println(context.getBean("childUserService"));
System.out.println(context.getBean("childUserService"));
}
}这里的类说明下:
GenericBeanDefinition:推荐用这个,它有两个子类
AnnotatedGenericBeanDefinition:即annotatedBeanDefinitionReader.register(UserService.class)生成的BeanDefinition的类型
ScannedGenericBeanDefinition:即aClassPathBeanDefinitionScanner生成的BeanDefinition的类型
ChildBeanDefinition:注释上写了,大多数情况不需要用它,只用GenericBeanDefinition
RootBeanDefinition:合并过的BeanDefinition的类型
整个BeanDefinition的结构体系是很复杂的,这里只是点一下
BeanFactory和ApplicationContext
BeanFactory表示Bean工厂,所以很明显,BeanFactory会负责创建Bean,并且提供获取Bean的API。
而ApplicationContext是BeanFactory的一种,在Spring源码中,是这么定义的:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
...
}ApplicationContext继承了ListableBeanFactory和HierarchicalBeanFactory,而ListableBeanFactory和HierarchicalBeanFactory都继承了BeanFactory,所以我们可以认为ApplicationContext继承了BeanFactory,所以ApplicationContext也是BeanFactory的一种,拥有BeanFactory的所有功能。
不过ApplicationContext比BeanFactory更加强大,ApplicationContext还继承了其他接口,也就表示ApplicationContext还拥有其他功能,比如MessageSource表示国际化,ApplicationEventPublisher表示事件发布,EnvironmentCapable表示获取环境变量,等等,后面会马上介绍这些功能的使用。
在Spring的源码实现中,当我们new一个ApplicationContext时,其底层会new一个BeanFactory出来,当使用ApplicationContext的某些方法时,比如getBean(),底层调用的是BeanFactory的getBean()方法。
在Spring源码中,BeanFactory接口存在一个非常重要的实现类是:DefaultListableBeanFactory,也是非常核心的。具体重要性,随着后续课程会感受更深。
具体在父类GenericApplicationContext的构造器中构造
所以,我们也可以直接来使用DefaultListableBeanFactory:
public class Main {
public static void main(String[] args) {
// spring容器 bean对象、beanDefinition、beanPostProcessor
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan("com.hexon");
System.out.println(beanFactory.getBean("userService"));
}
}@Component
public class UserService {
public void test() {
System.out.println("UserService#test....");
}
}或者直接注册BeanDefinition:
public class Main {
public static void main(String[] args) {
// spring容器 bean对象、beanDefinition、beanPostProcessor
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 这里UserService可以不加@Component
BeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);
System.out.println(beanFactory.getBean("userService"));
}
}学习到这里,可以看一下DefaultListableBeanFactory类的继承图,假如你自己要实现一个Bean工厂,就可以根据实现接口来定制自己的Bean工厂所支持的行为:

值得一提的是HierarchicalBeanFactory接口,实现它可以实现BeanFactory之前的父子关系,有点类似类加载双亲委派。
获取运行时环境
这里获取环境变量其实是容器的一个附加功能,可以通过容器对象获取environment对象:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
ConfigurableEnvironment environment = applicationContext.getEnvironment();这么理解,原本内置的有两个默认的环境变量域,但是也可以自己加入:
System.out.println(environment.getSystemEnvironment()); // 操作系统环境变量
System.out.println(environment.getSystemProperties()); // jvm 环境变量
for (PropertySource<?> propertySource : environment.getPropertySources()) {
System.out.println(propertySource);
}JVM参数可以这么配置:
另外,可以在配置类上添加注解引入配置文件:
@ComponentScan("com.hexon")
@PropertySource("classpath:application.properties")
public class MyConfig {
}这里就多了一个可以查找环境变量的域。
值得一提的是,Environment接口可以直接注入,Spring默认会有一个这个类型的Bean。所以,用法就有很多种:
@Component
public class UserService {
@Value("${k1}") // 如果找不到会把${k1}字符串常量注入,所以需要添加对应的propertySource,但SpringBoot中可能更"智能"
private String name;
public void test() {
System.out.println(name);
}
}你也可以直接@Autowired注入Environment对象再使用,也可以用ApplicationContextAware回调获取容器再获取Environment对象,看你自己怎么方便。
国际化
先定义配置文件:

默认有个本地的,再添加一个英语的;另外IDEA设置一下工程的编码
message.properties的内容为:
name=丹尼message_en.properties的内容为:
name=danny再增加一个配置Bean:
@ComponentScan("com.hexon")
public class MyConfig {
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("message");
return messageSource;
}
}有了这个Bean,你可以在你任意想要进行国际化的地方使用该MessageSource。
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println(applicationContext.getMessage("name", null, Locale.ENGLISH));
System.out.println(applicationContext.getMessage("name", null, Locale.getDefault()));
}
}这个在业务类中也同样像Environment一样可以先获取到容器再用,但也可以直接注入MessageSource类型使用。
另外,这个地方注意下中文乱码的问题,把工程编码,properties文件编码,CMD编码都改成UTF-8
Win10我是这么设置的
按
Win + R,输入intl.cpl进入"管理"选项卡
点击"更改系统区域设置"
勾选"Beta 版:使用 Unicode UTF-8 提供全球语言支持"
点击"确定"并重启电脑
资源加载
ApplicationContext还拥有资源加载的功能,具体来说是因为ApplicationContext实现了ResourcePatternResolver接口。
比如,可以直接利用ApplicationContext获取某个文件的内容:
public class Main {
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
Resource resource = context.getResource("file:///D:\\2025\\springsource\\spring-framework\\spring-demo\\src\\main\\resources\\spring.xml");
System.out.println(resource.contentLength());
}
}也可以直接获取某个远程的资源:
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
Resource resource = context.getResource("https://www.baidu.com");
System.out.println(resource.contentLength());
System.out.println(resource.getURL());
}还可以一次性获取多个:
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
Resource[] resources = context.getResources("classpath:com/hexon/*.class");
for (Resource resource : resources) {
System.out.println(resource.contentLength());
System.out.println(resource.getFilename());
}
}这个在类扫描的时候就有用到
事件发布
先定义一个事件监听器
@Bean
public ApplicationListener<PayloadApplicationEvent<String>> applicationListener() {
return new ApplicationListener<>() {
@Override
public void onApplicationEvent(PayloadApplicationEvent<String> event) {
System.out.println("接收事件:" + event.getPayload());
}
};
}然后使用容器对象发布一个事件:
public class Main {
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
context.publishEvent("hello event");
}
}类型转化
在Spring、SpringBoot、Spring Cloud中,有时根据beanName得到的Bean对象为A类型,但是需要的确实B类型,此时Spring就会找容器中是否有支持A类型转成B类型的类型转换器,如果有就会做类型转换,如果没有就会报错了。
先来看一段代码:
// User类定义
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}// UserService类定义
@Component
public class UserService {
// 直接把字符串注入User对象
@Value("danny")
private User user;
public void test() {
System.out.println(user.getName());
}
}// 配置类
@ComponentScan("com.hexon")
public class MyConfig {
}// 测试
public class Main {
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
context.getBean(UserService.class).test();
}
}运行结果:
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.hexon.User': no matching editors or conversion strategy found这个代码报错是正常的,但是从错误信息中我们可以看到,Spring默认似乎在找寻editor或者转换策略。
PropertyEditor
这其实是JDK中提供的类型转化工具类,可以把一个String转成其他类型,比如一下自定义PropertyEditor就是支持把一个String转成User对象。
public class StringToUserPropertyEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
User user = new User();
user.setName(text);
setValue(user);
}
}直接测试:
public class Main {
public static void main(String[] args) throws IOException {
StringToUserPropertyEditor propertyEditor = new StringToUserPropertyEditor();
propertyEditor.setAsText("danny");
User user = (User) propertyEditor.getValue();
System.out.println(user.getName()); // danny
}
}向Spring中注册PropertyEditor:
@Bean
public CustomEditorConfigurer customEditorConfigurer() {
Map<Class<?>, Class<? extends PropertyEditor>> propertyEditorMap = new HashMap<>();
propertyEditorMap.put(User.class, StringToUserPropertyEditor.class);
// CustomEditorConfigurer是一个BeanFactoryPostProcessor
CustomEditorConfigurer customEditorConfigurer = new CustomEditorConfigurer();
customEditorConfigurer.setCustomEditors(propertyEditorMap);
return customEditorConfigurer;
}添加到配置类中。BeanFactoryPostProcessor后面介绍。
测试:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
applicationContext.getBean(UserService.class).test();
}
}此时能成功转换,说明Spring找到了StringToUserPropertyEditor
Spring中内置了很多PropertyEditor,比如StringArrayPropertyEditor、PathEditor等。
但PropertyEditor属于 JavaBeans 时代的老机制,除非维护老项目,不推荐再使用。
ConversionService
PropertyEditor是GUI时代的产物了,理论上它也不是只能将String转其他类型,可以Object转Object。现代的Spring应该,已经使用Converter了,ConversionService则更强大,下面是一些概念的对比。
先来看一个最简单的例子:
// @Component // 纯SpringFramework情况下这个不需要加,你还是要自己添加到ConversionService中
public class StringToUserConverter implements Converter<String, User> {
@Override
public User convert(String source) {
User user = new User();
user.setId(Long.valueOf(source));
user.setName("User-" + source);
return user;
}
}这里用@Component在纯 Spring(XML/Java Config)中是不会生效的,Spring 不会扫描 Converter 并自动放入 ConversionService,Spring 也不会自动创建默认的 ConversionService。因此还要自己注册到 ConversionService,这配置类中添加下面代码:
@Bean
public ConversionService conversionService() {
FormattingConversionService service = new FormattingConversionService();
service.addConverter(new StringToUserConverter());
return service;
}此时再次测试就可以正常工作了:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
applicationContext.getBean(UserService.class).test();
}
}值得一提的是,这个如果在SpringMVC中(非SpringBoot),也是要自己启用转换服务的:
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToUserConverter());
}
}而在SpringBoot中它会自动装配,你只要定义一个Converter的Bean。
下面来看更强大的ConditionalGenericConverter:
/**
* 类型转换
* Converter 是简单版,只处理固定类型转换。
* ConditionalGenericConverter 是高级版,支持动态条件、支持复杂类型判断,所以必须实现三个方法。
**/
public class StringToUserConverter implements ConditionalGenericConverter {
// 判断“现在这个情况,我要不要处理”
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return sourceType.getType().equals(String.class) && targetType.getType().equals(User.class);
}
// 告诉 Spring:我能做什么类型的转换
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, User.class));
}
// 实际转换逻辑
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
User user = new User();
user.setName((String)source);
return user;
}
}直接使用:
public static void main(String[] args) {
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToUserConverter());
User user = conversionService.convert("danny", User.class);
System.out.println(user.getName());
}如何向Spring中注册ConversionService:
@Bean
public ConversionServiceFactoryBean conversionService() {
ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
conversionServiceFactoryBean.setConverters(Collections.singleton(new StringToUserConverter()));
return conversionServiceFactoryBean;
}ConversionServiceFactoryBean看他注释和源码就知道它会创建一个DefaultConversionService,会带一些默认的Converter。
TypeConverter
整合了PropertyEditor和ConversionService的功能,Spring内部用的就是它
public class Main {
public static void main(String[] args) {
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToUserConverter());
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
typeConverter.registerCustomEditor(User.class, new StringToUserPropertyEditor());
typeConverter.setConversionService(conversionService);
User value = typeConverter.convertIfNecessary("danny", User.class);
System.out.println(value.getName()); // 两个都有的情况,默认会走Editor
}
}OrderComparator和AnnotationAwareOrderComparator
OrderComparator是Spring所提供的一种比较器,会根据@Order注解或Ordered接口来进行比较,从而可以用来排序。
比如:
public class A implements Ordered {
@Override
public int getOrder() {
return 2;
}
}public class B implements Ordered {
@Override
public int getOrder() {
return 1;
}
}public class Main {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
list.add(new A());
list.add(new B());
// 按order值升序排序
list.sort(new OrderComparator());
System.out.println(list); // B A
}
}源码分析:OrderComparator#doCompare
private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
boolean p1 = (o1 instanceof PriorityOrdered); // PriorityOrdered 是一个标记接口,实现了代码优先级最高
boolean p2 = (o2 instanceof PriorityOrdered);
// 只要有一个实现,另一个没有实现,则实现的优先级高,越大
// p1 < p2
if (p1 && !p2) {
return -1;
}
// p2 < p1
else if (p2 && !p1) {
return 1;
}
// 都实现或者都没有实现PriorityOrdered就获取Ordered的值比较
int i1 = getOrder(o1, sourceProvider);
int i2 = getOrder(o2, sourceProvider);
return Integer.compare(i1, i2);
}另外,Spring中还提供了一个OrderComparator的子类:AnnotationAwareOrderComparator,它支持用@Order来指定order值。比如:
@Order(2)
public class A {
}@Order(1)
public class B {
}public class Main {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
list.add(new A());
list.add(new B());
// 按order值升序排序
list.sort(new AnnotationAwareOrderComparator()); // 注意:要用AnnotationAwareOrderComparator
System.out.println(list); // B A
}
}源码分析:
AnnotationAwareOrderComparator是继承了OrderComparator,OrderComparator实现了JDK的Comparator接口,所以list.sort会默认调用compare方法,而compare方法又调用了doCompare方法,所以最终还是先执行OrderComparator#doCompare,然后就会调用getOrder方法,getOrder方法中的sourceProvider是null,所以调用重载的getOrder方法,然后就调用到了findOrder方法因为this是AnnotationAwareOrderComparator类型,也就是调用AnnotationAwareOrderComparator#findOrder方法。通过阅读AnnotationAwareOrderComparator#findOrder方法的源码,就可以知道,如果实现了Ordered接口,就优先取这个接口方法返回的order值了,也就是说注解的值就没有效果了。再就调用AnnotationAwareOrderComparator#findOrderFromAnnotation方法。后面的细节我暂时没有看明白,反正先知道,如果实现了Ordered接口,那么@Order注解的值就没有效果。