Spring 学习源码的基础 核心原理与核心概念

博文目录


核心原理

AnnotationConfigApplicationContext

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();

我们很少按照上面的方式使用 Spring, 而是使用 Spring MVC, 或者 Spring Boot, 但是它们本质上都是基于这种方式的, 都需要在内部去创建一个 ApplicationContext

  • SpringBoot 创建的是 AnnotationConfigApplicationContext
  • SpringMVC 创建的是 XmlWebApplicationContext, 和 ClassPathXmlApplicationContext 类似都是基于 xml 的

AnnotationConfigApplicationContext 是研究学习的主类

IoC 容器加载流程

IoC容器加载流程可以分成两个步骤

  • 准备, 注册一些底层基础架构工具类, 在容器初始化流程中使用, 然后注册配置类, 然后刷新容器
  • 扫描, 将配置的各种 Bean 解析成为 BeanDefinition, 存入 BeanDefinitionMap
  • 遍历 BeanDefinitionMap, 生产单例, 并缓存到单例池 singletonObjects

Spring 中如何创建一个对象

new AnnotationConfigApplicationContext(Config.class) 的过程就是 IoC 容器的加载流程, 扫描生成 BeanDefinition, 遍历生成单例 Bean. 最后人为调用 getBean(beanName) 去缓存中拿到对应的 Bean

Bean 的创建过程 (生命周期)

class -> 反射newInstance -> 原始对象 -> 依赖注入(属性赋值) -> 一堆Aware -> 初始化前(@PostConstruct) -> 初始化(InitailizedBean) -> 初始化后(AOP) -> 代理对象(代理对象.target=原始对象) -> Bean

  • 实例化, 通过反射调用类的某个构造方法创建对象, 如果有多个构造方法, 会进行选择, 叫做推断构造方法
  • 依赖注入(属性赋值), 遍历对象内的字段, 有 @Autowired 的自动赋值
  • Aware 织入, 判断对象是否是(即类是否实现了) BeanNameAware, ApplicationContextAware 等各种 Aware, 是的话就强转调用结构定义的 set 方法
  • @PostConstruct 处理, 判断是否有方法有该注解, 有的话则执行该方法
  • InitializingBean 处理, 判断对象是否是该接口的实例, 是的话则执行接口定义的 afterPropertiesSet 方法
  • 自定义的 init-method 方法
  • 判断是否需要 AOP, 不需要时返回当前对象, 需要时则生成代理对象, 并返回
  • 如果是单例 Bean 则存入单例池

单例 原型

Bean 的作用域分为单例和原型两种

  • 单例: 每次 getBean 拿到的都是同一个对象, 首次创建后就会缓存到单例池
  • 原型: 每次 getBean 都会重新创建一次, 不会放到单例池

懒加载的 Bean 在 IoC 容器加载时不会被创建和缓存, 在使用时才会创建和缓存

推断构造方法

实例化时默认使用无参构造器(写了有参默认就没有无参了, 除非显式定义)

有无参构造器就使用无参构造器, 没无参构造器则判断有几个有参构造器, 只有一个的话就使用这一个有参构造器, 有多个的话因为不能确认使用哪个, 所以报错找不到默认无参构造器

有多个有参构造器的话, 也可以加 @Autowired 来指定使用哪个有参构造器

有参构造器的参数对象哪里来?

  • 先根据类型到容器中找, 如果只有一个则直接使用, 如果有多个则根据名称来过滤, 如果又匹配名称的则直接使用, 如果没有匹配的, 则报错

依赖注入

先按类型到容器中过滤, 匹配的如果只有一个则直接使用, 多个则再按名称匹配, 有匹配到的直接使用, 没有则报错

AOP 动态代理

在创建 Bean 的最后一步, 会判断是否需要做 AOP, 需要则做动态代理

判断是否需要 AOP 的大致流程

  • 找出所有切面 Bean
  • 遍历其中每个方法, 判断是否写了 @Before, @After 等注解
  • 如果写了, 则判断其所对应的 PointCut 和当前创建的 Bean 是否匹配
  • 如果匹配, 则说明当前 Bean 需要做 AOP

CGLib 做 AOP 的大致流程

  • 生成继承原类的代理类, 代理类持有原类对象 target. 注意, 原类对象是经过 Bean 创建流程的, 包括依赖注入, 初始化等流程
  • 代理类中覆盖原类中的方法, 最终会调用到 target 的对应方法, 但是会在前后把切面逻辑都加上, 大致如下
  • 最后创建 Bean 返回的是持有原始对象的代理对象
public class UserService {
	@Autowired
	private OrderService orderService;
	public void test() {
		sout;
	}
}
class UserServiceProxy extend UserService {
	UserService target;
	public void test() {
		// @Before 的逻辑
		target.test();
		// @After 的逻辑
	}
}

代理类继承自原始类, 所以原始类的字段在代理类中也有, 但是 Spring 并不会为代理类做依赖注入, 因为没有必要

代理对象仅仅用于强化原始对象的某方法, 如果在切面逻辑中需要用到原始对象的依赖注入的字段, 也可以通过 JoinPoint.getTarget() 拿到原始对象来操作, 而原始对象中各字段已经做过依赖注入了

事务

某方法添加了 @Transactional 注解后, 会在 AOP 阶段给本类生成代理类和代理对象

事务代理对象执行方法的流程

  • 判断当前方法上有没有 @Transactional 注解
  • 有的话由事务管理器创建一个数据库连接 (此连接不是来自连接池?)
  • 设置连接的 autoCommit 为 false (无事务注解时, 连接是从连接池获取, 每执行一条 SQL 自动提交一次)
  • 执行方法, 执行方法中的 SQL
  • 调用连接的提交或回滚方法

事务注解排至失效的原因

事务是否会失效, 就看执行 @Transactional 注解方法的是哪个对象

  • 代理对象: 不会失效
  • 原始对象: 失效, 因为执行的就是普通方法, 没有代理对象做的强化了

最常见的例子, 类中有两个事务方法 a 和 b, 而 a 中会调用 b, 单独执行 a 和 b 事务都不会失效, 但是在 a 中执行 b 时, b 的事务注解上的配置会失效

因为执行 a 的流程是这样的, 拿到类的代理对象, 执行其 a, 先走切面逻辑, 创建连接, 设置不自动提交, 然后才执行 target.a 即原始对象的 a 方法, 此时的主体是原始对象而非代理对象. 执行到 b 方法时, 本质是 this.b, 主体还是原始对象, 并没有切面逻辑, 所以在 a 里面的 b 方法的事务注解配置都会失效

当然还有很多其他原因, 需要具体分析

其他的 AOP 失效很多也是一样的原因, 都是自调用导致的

有解决办法, 就是自引用, 类中依赖注入自身 self, 此时的 self 是代理对象, 在 a 中调用 b 的时候, 用 self.b, 这样主体是代理对象, 有切面强化逻辑, b 的事务配置就会生效了

为何下方加了 @Configuration 事务才能生效

@Configuration
public class Config {
	@Bean
	public TransactionManager transationManager() {
		return new DataSourceTransactionManager(dataSource());
	}
	@Bean
	public JdbcTemplate jdbcTemplate() {
		return new JdbcTemplate(dataSource())
	}
	@Bean
	public DataSource dataSource() {
		return new DataSource();
	}
}
  • 不加时, 调用两次 dataSource() 生成两个不同的数据源, 最终事务管理器和模板使用了不同的数据源
  • 加时, 会有特殊的处理, dataSource() 会被认为是一个 Bean, 传入两者的是同一个对象

在 JdbcTemplate 中获取连接时, 会检查当前是否为事务环境, 是的话会从 TransactionSynchronizationManager.getResource(dataSource); 中获取线程绑定的连接, 即事务管理器创建的那个连接, 需要使用同一个数据源对象才能拿到同一个连接, 这样事务管理器的提交和回滚操作才会对 JdbcTemplate 生效

核心概念

Spring 源码里有很多抽象和工具, 需要提前有一定了解, 读源码时能轻松一些

BeanDefinition

BeanDefinition 用来记录 Bean 配置的各种信息

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

定义 Bean 的方式有申明式和编程式两种, 通过各种方式定义的 Bean 最终都会被解析为 BeanDefinition 并缓存起来

  • 申明式:
    • < bean/>
    • @Bean
    • @Component(@Service,@Controller)
  • 编程式
    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"));
    

BeanDefinitionReader

用于根据某些规则将资源解析成为 BeanDefinition

AnnotatedBeanDefinitionReader

可以将某个类解析成为 BeanDefinition, 包括类上的注解(@Conditional,@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description)

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(context);

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

System.out.println(context.getBean("user"));

XmlBeanDefinitionReader

可以解析 < bean/> 标签配置的 Bean

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
int i = reader.loadBeanDefinitions("spring.xml");

System.out.println(context.getBean("user"));

ClassPathBeanDefinitionScanner

扫描器, 但是它的作用和 BeanDefinitionReader 类似, 它可以进行扫描, 扫描某个包路径, 对扫描到的类进行解析

如果扫描到的类上存在 @Component 注解, 那么就会把这个类解析为一个 BeanDefinition

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

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

System.out.println(context.getBean("userService"));

BeanFactory

Spring 容器的根接口, Bean 工厂, 负责创建 Bean 和获取 Bean, 提供各种 getBean() 方法的定义

DefaultListableBeanFactory

BeanFactory 有一个最核心的实现类 DefaultListableBeanFactory, 可以直接当作 BeanFactory 来使用, 可以替代 ApplicationContext 来使用, 就是功能会少一点而已

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

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

beanFactory.registerBeanDefinition("user", beanDefinition);

System.out.println(beanFactory.getBean("user"));

DefaultListableBeanFactory

DefaultListableBeanFactory 的架构体系如上, 有很多接口(能力)和类

  • AliasRegistry: 支持别名功能, 定义了 alias 的注册/获取/判断/移除等功能, 在这里支持一个 BeanDefinition / Bean 有多个名称的功能
  • SimpleAliasRegistry, 维护着 Map<String, String> aliasMap, 在这里是 alias 与 beanName 的多对一关系, 便于从别名找到原名(别名也能起别名), 当然也可以从原名找到所有别名
  • BeanDefinitionRegistry: 定义了 BeanDefinition 的注册/获取/存在/移除等功能
  • SingletonBeanRegistry: 定义了单例 Bean 的注册/存在/获取/获取数量/获取名称等功能
  • BeanFactory: 定义了 Bean 的获取/存在/获取别名/判断作用域等功能
  • ListableBeanFactory: 扩展了 BeanFactory, 提供了方法用于枚举和检索容器中的 bean 实例, 可以方便地获取所有 bean 的名称、按类型获取 bean、按条件检索 bean 等操作
  • HierarchicalBeanFactory: 扩展了 BeanFactory, 提供了层次化的容器结构和继承机制, 每个子容器可以独立管理自己的 bean 定义和实例。子容器可以继承父容器中定义的 bean,并可以在子容器中覆盖或扩展父容器中的 bean 定义。可以实现更好的模块化和组织化,并灵活管理和定制 bean 的定义和作用域
  • AutowireCapableBeanFactory: 扩展了 BeanFactory, 提供了自动装配 bean 的能力, 可以实现依赖注入、自动装配和解耦等功能
  • ConfigurableBeanFactory, 除了 BeanFactory Bean 之外,还提供用于配置 BeanFactory 的工具. 添加了设置父BeanFactory、类加载器(表示可以指定某个类加载器进行类的加载)、设置Spring EL表达式解析器(表示该BeanFactory可以解析EL表达式)、设置类型转化服务(表示该BeanFactory可以进行类型转化)、可以添加BeanPostProcessor(表示该BeanFactory支持Bean的后置处理器),可以合并BeanDefinition,可以销毁某个Bean等等功能
  • ConfigurableListableBeanFactory: 除了 ConfigurableBeanFactory 之外, 它还提供了分析和修改 Bean 定义以及预实例化单例的工具
  • DefaultSingletonBeanRegistry: 主要用于管理和维护单例 bean 的注册和获取, singletonObjects 在这里
  • FactoryBeanRegistrySupport: 主要用于支持 FactoryBean 的注册和获取, factoryBeanObjectCache 在这里
  • AbstractBeanFactory: 功能已经很全面了, 但是不能自动装配和获取 beanNames, beanPostProcessors 在这里
  • AbstractAutowireCapableBeanFactory: 拥有了自动装配的功能
  • DefaultListableBeanFactory: 是 Spring 容器的一个关键组件,负责管理 BeanDefinition 的注册、合并和查找,以及 Bean 的创建、装配和销毁。它提供了丰富的功能和灵活的配置选项,是 Spring 应用程序中常用的 BeanFactory 实现之一, beanDefinitionMap 在这里

ApplicationContext

BeanFactory 有一个最核心的子接口 ApplicationContext, 其定义如下

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

在这里插入图片描述

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

ApplicationContext 的定位是 Spring 的应用上下文, 负责管理和组织应用程序的各个部分. 从代码层面来说, ApplicationContext 是一个 BeanFactory, 从架构层面来说, ApplicationContext 是比 BeanFactory 更加高级的存在, 它统御 BeanFactory, EnvironmentCapable, MessageSource 等这些组件完成相应的功能, BeanFactory 只是它的一个零件而已

照着这个思路来看, GenericApplicationContext 不继承 DefaultListableBeanFactory 而是将之作为一个属性, 从 BeanFactory 继承来的功能全部委托其持有的 DefaultListableBeanFactory 来执行, 就是非常合理的事情了

ApplicationContext 接口继承了 ListableBeanFactory 和 HierarchicalBeanFactory, 但它的定位是一个高位 BeanFactory, 只是聚焦于 BeanFactory 一定程度的基础功能即可, 并不需要中低层更强大的更加细节的全部功能

ApplicationContext 有两个重要的实现类

  • AnnotationConfigApplicationContext
  • ClassPathXmlApplicationContext

AnnotationConfigApplicationContext

在这里插入图片描述

  • Lifecycle: 定义启动/停止生命周期控制方法的通用接口
  • ConfigurableApplicationContext: 增加了,添加事件监听器、添加BeanFactoryPostProcessor、设置Environment,获取ConfigurableListableBeanFactory等功能
  • AbstractApplicationContext: 实现通用上下文功能, 大名鼎鼎的 refresh 方法就在这里了
  • GenericApplicationContext: 通用的应用上下文具体实现类
  • AnnotationConfigRegistry: 用于注释配置应用程序上下文, 可以单独注册某个类为 BeanDefinition(可以处理该类上的 @Configuration @Bean 注解)
  • AnnotationConfigApplicationContext: 是一种方便且强大的应用上下文实现,适用于注解驱动的开发方式。它通过扫描和处理注解,实现了自动化的 Bean 注册和装配,减少了繁琐的 XML 配置

ClassPathXmlApplicationContext

在这里插入图片描述
同样继承了 AbstractApplicationContext,但是相对于AnnotationConfigApplicationContext 而言,功能没有AnnotationConfigApplicationContext 强大,比如不能注册 BeanDefinition

国际化 MessageSource

在这里插入图片描述

# messages.properties
test = 你好啊
# messages_en_US.properties
test = Hello
# messages_zh_CN.properties
test = 你好
@Configuration
public class MessageSourceConfig {

	@Bean
	public MessageSource messageSource() {
		ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
		resourceBundleMessageSource.setDefaultEncoding("UTF-8");
		resourceBundleMessageSource.setBasename("i18n.messages");
		return resourceBundleMessageSource;
	}

}
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
		System.out.println(Locale.getDefault()); // zh_CN
		System.out.println(context.getMessage("test", null, Locale.getDefault())); // 你好
		System.out.println(context.getMessage("test", null, new Locale("zh_CN"))); // 你好
		System.out.println(context.getMessage("test", null, new Locale("en_US"))); // Hello
		System.out.println(context.getMessage("test", null, new Locale("de_DE"))); // 你好, 不存在, 走默认
	}

资源加载 getResource / getResources

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);

Resource resource1 = context.getResource("file:/C:/coder/develop/workspace/idea/mrathena/spring/spring-analyze-test/src/main/java/com/coder/UserService.java");
System.out.println(resource1.contentLength());
System.out.println(resource1.getFilename());

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

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

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

获取运行时环境

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);

Map<String, Object> systemEnvironment = context.getEnvironment().getSystemEnvironment();
for (Map.Entry<String, Object> entry : systemEnvironment.entrySet()) {
	System.out.println(entry.getKey() + "\t\t" + entry.getValue());
}

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

Map<String, Object> systemProperties = context.getEnvironment().getSystemProperties();
for (Map.Entry<String, Object> entry : systemProperties.entrySet()) {
	System.out.println(entry.getKey() + "\t\t" + entry.getValue());
}

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

// 会包含所有的属性源, 包括上面的 SystemEnvironment 和 SystemProperties
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
for (PropertySource<?> propertySource : propertySources) {
	System.out.println(propertySource);
}

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("Path"));

注意: 可以使用 @PropertySource("classpath:spring.properties") 的方式将某个 properties 文件中的参数添加到运行时环境中

事件发布

@ComponentScan
@Configuration
public class ApplicationListenerTest {

	@Bean
	public ApplicationListener<ApplicationEvent> applicationListener() {
		return event -> {
			System.out.println();
            System.out.println("接收到了一个事件\t\t" + event.getClass());
            System.out.println(event);
			if (event instanceof PayloadApplicationEvent) {
				System.out.println(((PayloadApplicationEvent) event).getPayload());
			}
        };
	}

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationListenerTest.class);
		context.publishEvent("测试事件");
	}

}

类型转化

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

以下面的 User 和 UserHolder 为例

public class User {
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "User{" + "name='" + name + '\'' + '}';
	}
}
@Component
public class UserHolder {
	@Value("xxx")
	private User user;
	public User getUser() {
		return user;
	}
}

PropertyEditor

Spring 属性编辑器 PropertyEditor

PropertyEditor 是 JDK 自带的接口, 主要用途就是将用户在图形见面中输入的字符串转换为对应类型的值(对象). 类似于一个 convertor, 但是要注意 PropertyEditor 对象有一个字段 value, 会保存解析后的值, 所以如果在多线程环境下使用同一个 PropertyEditor 对象会有同步问题

XML 配置 Bean 或者 @Value(“xxx”) 或者 properties 文件中的属性赋值到 Bean 中的时候, 属性字段值很多是靠 文本类型的字符串 设置的, 但是属性的类型却可能是 String, Integer, File, Class, Properties, Resource 等类型, PropertyEditor 就是用来把文本型的值转换为对应类型值的工具, 其关键方法是 void setAsText(String text)

基本使用流程是, 调用 setAsText 把传入的 text 解析为对应类型的值, 内部调用 setValue 把该值保存在 PropertyEditor 对象内部, 可通过 getValue 方法把该值取出, 然后调用目标对象的对应 set 方法把值赋值给目标对象的属性

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);
	}
	public static void main(String[] args) {
		StringToUserPropertyEditor propertyEditor = new StringToUserPropertyEditor();
		propertyEditor.setAsText("1");
		User value = (User) propertyEditor.getValue();
		System.out.println(value);
	}
}
@ComponentScan
@Configuration
public class Main {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
		UserHolder bean = context.getBean(UserHolder.class);
		System.out.println(bean.getUser());
	}

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

}

ConversionService

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

public class StringToUserConverter implements ConditionalGenericConverter {
	@Override
	public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
		return sourceType.getType().equals(String.class) && targetType.getType().equals(User.class);
	}
	@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 value = conversionService.convert("1", User.class);
		System.out.println(value);
	}
}
@ComponentScan
@Configuration
public class Main {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
		UserHolder bean = context.getBean(UserHolder.class);
		System.out.println(bean.getUser());
	}

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

}

TypeConverter

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

public class StringToUserTypeConverter {

	public static void main(String[] args) {
		convert1();
		convert2();
	}

	public static void convert1() {
		SimpleTypeConverter typeConverter = new SimpleTypeConverter();
		// 使用 StringToUserPropertyEditor
		typeConverter.registerCustomEditor(User.class, new StringToUserPropertyEditor());
		User value = typeConverter.convertIfNecessary("1", User.class);
		System.out.println(value);
	}

	public static void convert2() {
		SimpleTypeConverter typeConverter = new SimpleTypeConverter();
		// 使用 ConversionService
		GenericConversionService genericConversionService = new GenericConversionService();
		genericConversionService.addConverter(new StringToUserConverter());
		typeConverter.setConversionService(genericConversionService);
		User value = typeConverter.convertIfNecessary("1", User.class);
		System.out.println(value);
	}

}

OrderComparator

  • OrderComparator是Spring所提供的一种比较器,可以给实现Ordered接口的对象进行排序。
  • AnnotationAwareOrderComparator是OrderComparator的子类, 在支持Ordered接口的基础上扩展支持了@Order注解
public class Ordered1 implements Ordered {
	@Override
	public int getOrder() {
		return 1;
	}
	@Override
	public String toString() {
		return this.getClass().getSimpleName();
	}
}

public class Ordered2 implements Ordered {
	@Override
	public int getOrder() {
		return 2;
	}
	@Override
	public String toString() {
		return this.getClass().getSimpleName();
	}
}

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

@Order(4)
public class Order4 {
	@Override
	public String toString() {
		return this.getClass().getSimpleName();
	}
}
public static void main(String[] args) {
		Ordered a = new Ordered1();
		Ordered b = new Ordered2();
		OrderComparator comparator = new OrderComparator();
		System.out.println(comparator.compare(a, b)); // -1
		System.out.println(comparator.compare(b, a)); // 1
		List<Ordered> list = new ArrayList<>();
		list.add(b);
		list.add(a);
		list.sort(comparator);
		System.out.println(list); // [Ordered1, Ordered2]

		Order3 c = new Order3();
		Order4 d = new Order4();
		System.out.println(comparator.compare(c, d)); // 0
		System.out.println(comparator.compare(d, c)); // 0
		List<Object> list2 = new ArrayList<>();
		list2.add(d);
		list2.add(c);
		list2.add(b);
		list2.add(a);
		list2.sort(comparator);
		System.out.println(list2); // [Ordered1, Ordered2, Order4, Order3]

		AnnotationAwareOrderComparator annotationAwareOrderComparator = new AnnotationAwareOrderComparator();
		System.out.println(annotationAwareOrderComparator.compare(a, b)); // -1
		System.out.println(annotationAwareOrderComparator.compare(b, a)); // 1
		System.out.println(annotationAwareOrderComparator.compare(c, d)); // -1
		System.out.println(annotationAwareOrderComparator.compare(d, c)); // 1
		list2.sort(annotationAwareOrderComparator);
		System.out.println(list2); // [Ordered1, Ordered2, Order3, Order4]
	}

BeanPostProcessor

Bean 的后置处理器

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

BeanPostProcessor 的子接口可以扩展其他功能, 如 InstantiationAwareBeanPostProcessor 额外支持了在任意一个Bean的实例化前以及实例化后去执行一些逻辑

Spring 通常在某些时机, 获取到全部 BeanPostProcessor, 然后拿到其中的某些子类, 调用其某些方法干涉创建 Bean 的过程

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

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("初始化前: " + beanName);
		return bean;
	}
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("初始化后: " + beanName);
		return bean;
	}
}
> Task :spring-analyze-test:Main.main()
初始化前: main
初始化后: main
初始化前: userService
初始化后: userService
com.coder.base.BeanPostProcessor.UserService@41ee392b

BeanFactoryPostProcessor

BeanFactory 的后置处理器, 和BeanPostProcessor类似, BeanPostProcessor是干涉Bean的创建过程, BeanFactoryPostProcessor是干涉BeanFactory的创建过程

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		System.out.println("初始化后: " + beanFactory.getClass().getSimpleName());
	}
}
> Task :spring-analyze-test:Main.main()
初始化后: DefaultListableBeanFactory

FactoryBean

通常 Bean 都是由 Spring 来创建的, 通过 BeanPostProsessor 可以干预创建过程

通过 FactoryBean 可以完全由我们自己来创建 Bean 而不是经过 Spring, 不是通过完整的 Bean 生命周期创建出来的, 只会经过 初始化后(AOP) 流程, 没有 依赖注入 流程. 但这种方式创建的 Bean 也会被 Spring 管理起来

通过@Bean也可以自己生成一个对象作为Bean,和FactoryBean的区别是什么呢?其实在很多场景下他俩是可以替换的,但是@Bean定义的Bean是会经过完整的Bean创建的生命周期的

ExcludeFilter IncludeFilter

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

比如以下配置, 表示排除扫描路径中的 UserService, 即是它有 @Component 注解

@ComponentScan(excludeFilters = {
		@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = UserService.class)
})
@Configuration
public class Application {

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

@ComponentScan(includeFilters = {
		@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = UserService.class)
})
@Configuration
public class Application {

FilterType 分为:

  • ANNOTATION:表示是否包含某个注解
  • ASSIGNABLE_TYPE:表示是否是某个类
  • ASPECTJ:表示否是符合某个Aspectj表达式
  • REGEX:表示是否符合某个正则表达式
  • CUSTOM:自定义

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

MetadataReader、ClassMetadata、AnnotationMetadata

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

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

public static void main(String[] args) {

		try {

			SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();

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

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

			System.out.println(classMetadata.getClassName());

			// 获取一个AnnotationMetadata,并获取类上的注解信息
			AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
			for (String annotationType : annotationMetadata.getAnnotationTypes()) {
				System.out.println(annotationType);
			}

		} catch (Throwable cause) {
			cause.printStackTrace();
		}

	}
> Task :spring-analyze-test:Main.main()
com.coder.main.UserService
org.springframework.stereotype.Component

需要注意的是,SimpleMetadataReader去解析类时,使用的是 ASM 技术。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值