Spring

容器与Bean

容器接口

BeanFactory接口,典型功能有:

  • getBean

ApplicationContext接口,是BeanFactory的子接口。它扩展了BeanFactory接口的功能,如:

  • 国际化
  • 通配符方式获取一组Resource资源
  • 整合Environment环境(能通过它获取各种来源的配置信息)
  • 事件发布与监听,实现组件之间的解耦

BeanFactory与ApplicationContext

BeanFactory功能

Ctrl + F12查看BeanFactory方法

BeanFactory接口的默认实现org.springframework.beans.factory.support.DefaultListableBeanFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Slf4j
@SpringBootApplication
public class SpringDemoApplication {

@SneakyThrows
public static void main(String[] args) {
// Ctrl + Alt + U 查看类图
// 选择 Java Classes Diagrams
ConfigurableApplicationContext context = SpringApplication.run(SpringDemoApplication.class, args);
/*
1. 到底什么是 BeanFactory
- 它是 ApplicationContext 的父接口
- 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能
*/
System.out.println(context);


/*
2. BeanFactory 能干点啥
- 表面上只有 getBean
- 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能, 都由它的实现类提供
org.springframework.beans.factory.support.DefaultListableBeanFactory
*/
// 通过反射获取所有的单例对象
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
map.entrySet().stream().filter(k -> k.getKey().startsWith("component"))
.forEach(k -> System.out.println(k.getKey() + "=" + k.getValue()));
}

}

ApplicationContext功能

ApplicationContext接口主要拓展了以下四个接口

  • MessageSource处理国际化资源的能力
  • ResourcePatternResolver通配符匹配资源的能力,比如:磁盘路径、类路径
  • ApplicationEventPublisher发布事件对象
  • EnvironmentCapable处理Spring中的环境信息,可以读取系统中的环境信息、yaml文件、properties文件

注意

  • 如果 jdk > 8, 运行时请添加--add-opens java.base/java.lang=ALL-UNNAMED,这是因为这些版本的jdk默认不允许跨module反射
  • 事件发布还可以异步,请自行查阅 @EnableAsync@Async的用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@SpringBootApplication
public class SpringDemoApplication {

@SneakyThrows
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringDemoApplication.class, args);

// 国际化资源,正常web应用中通过请求头Accept-Language设置
System.out.println(context.getMessage("hi", null, Locale.CHINA));
System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
System.out.println(context.getMessage("hi", null, Locale.JAPAN));

// 根据通配符获取一组资源
// classpath*:表示在jar包下寻找
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
for (Resource resource : resources) {
System.out.println(resource);
}

// 获取相关配置信息,环境变量、配置文件
// 不区分大小写
ConfigurableEnvironment environment = context.getEnvironment();
System.out.println(environment.getProperty("java_home"));
System.out.println(environment.getProperty("server.port"));

// 发布事件
// context.publishEvent(new UserRegisteredEvent(context));
context.getBean(Component1.class).register();
}

国际化文件均在src/resources目录下

  • messages.properties(空)

  • messages_en.properties

    1
    hi=Hello
  • messages_ja.properties

    1
    hi=こんにちは
  • messages_zh.properties

    1
    hi=你好

总结💡

通过这个示例结合debug查看ApplicationContext对象的内部结构,了解到:

  1. 到底什么是BeanFactory

    • 它是ApplicationContext的父接口
    • 它才是Spring的核心容器, 主要的ApplicationContext实现都【组合】了它的功能,【组合】是指ApplicationContext的一个重要成员变量就是BeanFactory
  2. BeanFactory能干点啥

    • 表面上只有getBean
    • 实际上控制反转、基本的依赖注入、直至Bean的生命周期的各种功能,都由它的实现类提供
    • 例子中通过反射查看了它的成员变量singletonObjects,内部包含了所有的单例 bean
  3. ApplicationContextBeanFactory多点啥

    • ApplicationContext组合并扩展了BeanFactory的功能
    • 国际化、通配符方式获取一组Resource资源、整合Environment 环境、事件发布与监听
    • 代码之间解耦途径,事件解耦

容器实现

  • DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转依赖注入功能,都是它来实现
  • ClassPathXmlApplicationContext,从类路径查找 XML 配置文件,创建容器(旧)
  • FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)
  • XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)
  • AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)
  • AnnotationConfigApplicationContext,Spring boot 中非 web 环境容器(新)
  • AnnotationConfigServletWebServerApplicationContext,Spring boot 中 servlet web 环境容器(新)
  • AnnotationConfigReactiveWebServerApplicationContext,Spring boot 中 reactive web 环境容器(新)

另外要注意的是,后面这些带有 ApplicationContext的类都是 ApplicationContext接口的实现,但它们是组合DefaultListableBeanFactory的功能,并非继承而来

BeanFactory实现

BeanFactory接口最主要的实现是org.springframework.beans.factory.support.DefaultListableBeanFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
@Slf4j
public class TestBeanFactory {

public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义(class, scope, 初始化, 销毁)
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config", beanDefinition);

// 给 BeanFactory 添加一些常用的后处理器,只是添加到了BeanFactory
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

// BeanFactory 后处理器主要功能,补充了一些 bean 定义,解析@Bean
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
.forEach(beanFactoryPostProcessor -> beanFactoryPostProcessor.postProcessBeanFactory(beanFactory));

// Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
// Bean后处理器执行顺序
.sorted(Objects.requireNonNull(beanFactory.getDependencyComparator()))
.forEach(beanPostProcessor -> {
// 建立BeanFactory与后处理器之间的联系
System.out.println(">>>>" + beanPostProcessor);
beanFactory.addBeanPostProcessor(beanPostProcessor);
});

for (String definitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(definitionName);
}
// 提前创建好所有单例,不会等用的时候在创建
beanFactory.preInstantiateSingletons();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ");
// System.out.println(beanFactory.getBean(Bean1.class).getBean2());
System.out.println(beanFactory.getBean(Bean1.class).getInter());
/*
a. beanFactory 不会做的事
1. 不会主动调用 BeanFactory 后处理器
2. 不会主动添加 Bean 后处理器
3. 不会主动初始化单例
4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }
b. bean 后置处理器会有排序的逻辑,都实现了Order接口,数字小的会排在前面
*/

System.out.println("CommonAnnotationBeanPostProcessor(@Resource)排序值:" + (Ordered.LOWEST_PRECEDENCE - 3));
System.out.println("AutowiredAnnotationBeanPostProcessor(@Autowired)排序值:" + (Ordered.LOWEST_PRECEDENCE - 2));
}

@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}

@Bean
public Bean2 bean2() {
return new Bean2();
}

@Bean
public Bean3 bean3() {
return new Bean3();
}

@Bean
public Bean4 bean4() {
return new Bean4();
}
}

interface Inter {

}

static class Bean3 implements Inter {

}

static class Bean4 implements Inter {

}

static class Bean1 {
public Bean1() {
log.debug("构造 Bean1()");
}

@Autowired
private Bean2 bean2;

public Bean2 getBean2() {
return bean2;
}

@Autowired
@Resource(name = "bean4")
private Inter bean3;

public Inter getInter() {
return bean3;
}
}

static class Bean2 {
public Bean2() {
log.debug("构造 Bean2()");
}
}
}

小结💡

  • beanFactory可以通过registerBeanDefinition注册一个bean definition对象
    • 我们平时使用的配置类、xml、组件扫描等方式都是生成bean definition对象注册到beanFactory 当中
    • bean definition描述了这个bean的创建蓝图:scope 是什么、用构造还是工厂创建、初始化销毁方法是什么,等等
  • beanFactory需要手动调用beanFactory后处理器对它做增强
    • 例如通过解析 @Bean@ComponentScan等注解,来补充一些bean definition
  • beanFactory需要手动添加bean后处理器,以便对后续bean的创建过程提供增强
    • 例如@Autowired@Resource等注解的解析都是bean后处理器完成的
    • bean后处理的添加顺序会对解析结果有影响,见上述代码中同时加@Autowired,@Resource 的例子
  • beanFactory需要手动调用方法来初始化单例
  • beanFactory需要额外设置才能解析${}#{}

ApplicationContext实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;

/**
* @Author XiaoYu
* @Description 常见 ApplicationContext 实现
* @Datetime 2022-05-27 15:55:49
*/
@Slf4j
public class TestApplicationContext {

public static void main(String[] args) {
// testClassPathXmlApplicationContext();
// testFileSystemXmlApplicationContext();

/*DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取之前。。。。");
for (String definitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(definitionName);
}
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// ClassPathXmlApplicationContext 是 ClassPathResource
// FileSystemXmlApplicationContext 是 FileSystemResource
reader.loadBeanDefinitions(new ClassPathResource("spring.xml"));
System.out.println("读取之后。。。。");
for (String definitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(definitionName);
}*/
// testAnnotationConfigApplicationContext();
testAnnotationConfigServletWebServerApplicationContext();
}

// ⬇️较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
private static void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
for (String definitionName : context.getBeanDefinitionNames()) {
System.out.println(definitionName);
}

System.out.println(context.getBean(Bean2.class).getBean1());
}

// ⬇️基于磁盘路径下 xml 格式的配置文件来创建
private static void testFileSystemXmlApplicationContext() {
FileSystemXmlApplicationContext context =
new FileSystemXmlApplicationContext("src\\main\\resources\\spring.xml");
for (String definitionName : context.getBeanDefinitionNames()) {
System.out.println(definitionName);
}

System.out.println(context.getBean(Bean2.class).getBean1());
}

// ⬇️较为经典的容器, 基于 java 配置类来创建
private static void testAnnotationConfigApplicationContext() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
for (String definitionName : context.getBeanDefinitionNames()) {
System.out.println(definitionName);
}

System.out.println(context.getBean(Bean2.class).getBean1());
}

// ⬇️较为经典的容器, 基于 java 配置类来创建, 用于 web 环境
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String definitionName : context.getBeanDefinitionNames()) {
System.out.println(definitionName);
}
}

@Configuration
static class WebConfig {
@Bean
public ServletWebServerFactory servletWebServerFactory(){
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
// 注册DispatcherServlet到Tomcat容器
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean("/hello")
public Controller controller1() {
return (request, response) -> {
response.getWriter().print("hello");
return null;
};
}
}

@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}

@Bean
public Bean2 bean2(Bean1 bean1) {
Bean2 bean2 = new Bean2();
bean2.setBean1(bean1);
return bean2;
}
}

static class Bean1 {
}

static class Bean2 {

private Bean1 bean1;

public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}

public Bean1 getBean1() {
return bean1;
}
}
}

小结💡

  1. 常见的ApplicationContext容器实现
  2. 内嵌容器、DispatcherServlet的创建方法、作用

Bean的生命周期

Bean生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class CustomBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

private static final Logger log = LoggerFactory.getLogger(CustomBeanPostProcessor.class);

@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");
}

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
return null;
}

@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
// return false;
}
return true;
}

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
return pvs;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
return bean;
}
}
graph LR

创建 --> 依赖注入
依赖注入 --> 初始化
初始化 --> 可用
可用 --> 销毁

创建前后的增强

  • postProcessBeforeInstantiation
    • 这里返回的对象若不为 null 会替换掉原本的 bean,并且仅会走postProcessAfterInitialization流程
  • postProcessAfterInstantiation
    • 这里如果返回 false 会跳过依赖注入阶段

依赖注入前的增强

  • postProcessProperties
    • @Autowired@Value@Resource

初始化前后的增强

  • postProcessBeforeInitialization
    • 这里返回的对象会替换掉原本的 bean
    • @PostConstruct@ConfigurationProperties
  • postProcessAfterInitialization
    • 这里返回的对象会替换掉原本的 bean
    • 如代理增强

销毁之前的增强

  • postProcessBeforeDestruction
    • @PreDestroy

小结💡

  1. Spring Bean生命周期各个阶段
  2. 模板设计模式, 指大流程已经固定好了, 通过接口回调(bean 后处理器)在一些关键点前后提供扩展

模板方法设计模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.util.ArrayList;
import java.util.List;

/**
* 对拓展功能抽象拓展
*/
public class TestMethodTemplate {

public static void main(String[] args) {
CustomBeanFactory beanFactory = new CustomBeanFactory();
beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Autowired"));
beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));
beanFactory.getBean();
}

// 模板方法 Template Method Pattern
static class CustomBeanFactory {
public Object getBean() {
Object bean = new Object();
System.out.println("构造 " + bean);
System.out.println("依赖注入 " + bean); // @Autowired, @Resource
for (BeanPostProcessor processor : processors) {
processor.inject(bean);
}
System.out.println("初始化 " + bean);
return bean;
}

private List<BeanPostProcessor> processors = new ArrayList<>();

public void addBeanPostProcessor(BeanPostProcessor processor) {
processors.add(processor);
}
}

static interface BeanPostProcessor {
public void inject(Object bean); // 对依赖注入阶段的扩展
}
}

Bean后处理器排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.Order;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* bean 后处理的的排序
*/
public class TestProcessOrder {

public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

List<BeanPostProcessor> list = new ArrayList<>(Arrays.asList(new P1(), new P2(), new P3(), new P4(), new P5()));
list.sort(beanFactory.getDependencyComparator());

list.forEach(processor-> processor.postProcessBeforeInitialization(new Object(), ""));
}

@Order(1)
static class P1 implements BeanPostProcessor {
private static final Logger log = LoggerFactory.getLogger(P1.class);

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
log.debug("postProcessBeforeInitialization @Order(1)");
return bean;
}
}

@Order(2)
static class P2 implements BeanPostProcessor {
private static final Logger log = LoggerFactory.getLogger(P2.class);

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
log.debug("postProcessBeforeInitialization @Order(2)");
return bean;
}

}

static class P3 implements BeanPostProcessor, PriorityOrdered {
private static final Logger log = LoggerFactory.getLogger(P3.class);

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
log.debug("postProcessBeforeInitialization PriorityOrdered");
return bean;
}

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

static class P4 implements BeanPostProcessor, Ordered {
private static final Logger log = LoggerFactory.getLogger(P4.class);

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
log.debug("postProcessBeforeInitialization Ordered");
return bean;
}

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

static class P5 implements BeanPostProcessor {
private static final Logger log = LoggerFactory.getLogger(P5.class);

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
log.debug("postProcessBeforeInitialization");
return bean;
}
}
}

小结💡

  1. 实现了PriorityOrdered接口的优先级最高
  2. 实现了Ordered接口与加了@Order注解的平级, 按数字升序
  3. 其它的排在最后

Bean后处理器

后处理器作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;

/**
* bean 后处理器的作用
*/
public class A04 {
public static void main(String[] args) {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();

// ⬇️用原始方法注册三个 bean
context.registerBean("bean1", Bean1.class);
context.registerBean("bean2", Bean2.class);
context.registerBean("bean3", Bean3.class);
context.registerBean("bean4", Bean4.class);

context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value

context.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroy

ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory()); // @ConfigurationProperties

// ⬇️初始化容器
context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例

System.out.println(context.getBean(Bean4.class));

// ⬇️销毁容器
context.close();
}
}

小结💡

  1. @Autowired等注解的解析属于bean生命周期阶段(依赖注入, 初始化)的扩展功能,这些扩展功能由bean后处理器来完成
  2. 每个后处理器各自增强什么功能
    • AutowiredAnnotationBeanPostProcessor解析@Autowired@Value
    • CommonAnnotationBeanPostProcessor解析@Resource@PostConstruct@PreDestroy
    • ConfigurationPropertiesBindingPostProcessor解析@ConfigurationProperties
  3. 另外ContextAnnotationAutowireCandidateResolver负责获取@Value的值,解析 @Qualifier、泛型、@Lazy等

@Autowired Bean后处理器运行分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.env.StandardEnvironment;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {

@SneakyThrows
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2()); // 该方法不会走,创建过程、依赖注入、初始化
beanFactory.registerSingleton("bean3", new Bean3());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器

// 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(beanFactory);

Bean1 bean1 = new Bean1();
// System.out.println(bean1);
// processor.postProcessProperties(null, bean1, "bean1"); // 执行依赖注入@Autowired @Value
// System.out.println(bean1);

// Method method = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
// method.setAccessible(true);
// InjectionMetadata metadata = (InjectionMetadata) method.invoke(processor, "baen1", Bean1.class, null); // 获取Bean1上加了@Value,@Autowired的成员变量,方法参数信息
// System.out.println(metadata);

// 2. 调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值
// metadata.inject(bean1, "bean1", null);
// System.out.println(bean1);

// 3. 如何按类型查找值
// 查找属性
Field bean3 = Bean1.class.getDeclaredField("bean3");
DependencyDescriptor descriptor1 = new DependencyDescriptor(bean3, false);
Object o1 = beanFactory.doResolveDependency(descriptor1, null, null, null);
System.out.println(o1);

// 查找方法
Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
DependencyDescriptor descriptor2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
Object o2 = beanFactory.doResolveDependency(descriptor2, null, null, null);
System.out.println(o2);

// 值注入方法
Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
DependencyDescriptor descriptor3 = new DependencyDescriptor(new MethodParameter(setHome, 0), false);
Object o3 = beanFactory.doResolveDependency(descriptor3, null, null, null);
System.out.println(o3);
}
}

小结💡

  1. AutowiredAnnotationBeanPostProcessor.findAutowiringMetadata用来获取某个bean上加了 @Value,@Autowired的成员变量,方法参数的信息,表示为InjectionMetadata
  2. InjectionMetadata可以完成依赖注入
  3. InjectionMetadata内部根据成员变量,方法参数封装为DependencyDescriptor类型
  4. 有了DependencyDescriptor,就可以利用beanFactory.doResolveDependency方法进行基于类型的查找

BeanFactory后处理器

BeanFactory后处理器的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
* BeanFactory 后处理器的作用
*/
public class A05 {

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

// ⬇️GenericApplicationContext 是一个【干净】的容器,没有添加好的处理器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource
context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
bd.getPropertyValues().add("basePackage", "com.xiao.learning.chapter05.mapper");
});


// ⬇️初始化容器
context.refresh();

for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}

// ⬇️销毁容器
context.close();
}
}
  • ConfigurationClassPostProcessor可以解析
    • @ComponentScan
    • @Bean
    • @Import
    • @ImportResource
  • MapperScannerConfigurer可以解析
    • Mapper 接口

小结💡

  1. @ComponentScan, @Bean, @Mapper等注解的解析属于核心容器(即 BeanFactory)的扩展功能
  2. 这些扩展功能由不同的BeanFactory后处理器来完成,其实主要就是补充了一些 bean 定义

org.springframework.boot.context.annotation.ImportCandidates

模拟解析@ComponentScan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override // context.refresh 调用
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) {

}

@Override
@SneakyThrows
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 模拟Spring解析 @ComponentScan注解
// 获取ComponentScan注解
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
// 获取字节码源信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
// 生成Bean的名字
BeanNameGenerator generator = new AnnotationBeanNameGenerator();
if (componentScan != null) {
for (String p : componentScan.basePackages()) {
// System.out.println(p);
// com.xiao.learning.chapter05.component -> classpath*:com/xiao/learning/chapter05/component/**/*.class
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
// System.err.println(path);
// 获取类路径下二进制资源
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
for (Resource resource : resources) {
// System.out.println(resource);
MetadataReader reader = factory.getMetadataReader(resource);
// 获取类源信息
// System.out.println("类名:" + reader.getClassMetadata().getClassName());
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
// System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
// 如何当前类加了@Controller注解,那么就会对Controller注解进一步扫描,发现Controller注解其实还是@Component注解
// System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));

if (annotationMetadata.hasAnnotation(Component.class.getName()) || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
// Bean对象的定义
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();

// 生成Bean的名字,是根据Bean定义里面的一些信息去生成名字
String beanName = generator.generateBeanName(beanDefinition, registry);
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
}
}

将自定义的ComponentScanPostProcessor后处理器注册到Bean工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class A05 {

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

// ⬇️GenericApplicationContext 是一个【干净】的容器,没有添加好的处理器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource
// context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
// bd.getPropertyValues().add("basePackage", "com.xiao.learning.chapter05.mapper");
// });

// 将ComponentScanPostProcessor后置处理器注册到Bean工厂
context.registerBean(ComponentScanPostProcessor.class);

// ⬇️初始化容器
context.refresh();

for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}

// ⬇️销毁容器
context.close();
}
}

小结💡

  1. Spring操作元数据的工具类CachingMetadataReaderFactory
  2. 通过注解元数据(AnnotationMetadata)获取直接或间接标注的注解信息
  3. 通过类元数据(ClassMetadata)获取类名,AnnotationBeanNameGenerator生成 bean 名
  4. 解析元数据是基于ASM技术

模拟解析@Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;

import java.util.Map;
import java.util.Set;

public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

}

@Override
@SneakyThrows
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
// 模拟解析@Bean
// 读取类的信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
// 该方法不走类加载的,效率比反射高
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/xiao/learning/chapter05/Config.class"));
// 获取到Config类的源信息之后,我们接下来获取哪些方法被@Bean注解标注了
// 获取Config这个类的信息,接下里获取所有被@Bean注解标注的方法信息
Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata method : methods) {
// System.out.println(method);

// 返回@Bean注解的属性map集合
Map<String, Object> map = method.getAnnotationAttributes(Bean.class.getName());
String initMethod = map.get("initMethod").toString();

// 定义了Config配置类的工厂方法
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(method.getMethodName(), "config");
// @Bean标注的方法的参数解析方式
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
if (initMethod.length() > 0) {
// 配置初始化方法,执行DruidDataSource的init方法
builder.setInitMethodName(initMethod);
}
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

beanFactory.registerBeanDefinition(method.getMethodName(), beanDefinition);
}
}
}

将自定义的AtBeanPostProcessor后处理器注册到Bean工厂

1
context.registerBean(AtBeanPostProcessor.class);

小结💡

  1. 进一步熟悉注解元数据(AnnotationMetadata)获取方法上注解信息

模拟解析@Mapper接口

Spring底层添加Mapper Bean的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   @Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}

@Bean
public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}

@Bean
public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}

自定义Mapper后置处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;

import java.io.IOException;

public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:com/xiao/learning/chapter05/mapper/**/*.class");
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
for (Resource resource : resources) {
MetadataReader reader = factory.getMetadataReader(resource);
ClassMetadata classMetadata = reader.getClassMetadata();
// 判断是否是接口
if (classMetadata.isInterface()) {
AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
// 创建MapperFactoryBean对象的时候,需要给构造方法传递一个参数,即当前遍历的对象
.addConstructorArgValue(classMetadata.getClassName())
// 按照类型寻找SqlSessionFactoryBean对象的Bean
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
AbstractBeanDefinition beanDefinition2 = BeanDefinitionBuilder
.genericBeanDefinition(classMetadata.getClassName())
.getBeanDefinition();
// 生成Bean的名字,是根据Bean定义里面的一些信息去生成名字,
// 如果用beanDefinition1去生成的话,当前的Bean为MapperFactoryBean,BeanName是mapperFactoryBean后续的都是mapperFactoryBean,会覆盖
// beanDefinition2是通过当前类的名字去生成BeanName,当前类为@Mapper注解标注的类,但是beanDefinition2不会添加到容器中,只是为了生成名字
String beanName = generator.generateBeanName(beanDefinition2, beanFactory);
beanFactory.registerBeanDefinition(beanName, beanDefinition1);
}
}
} catch (IOException e) {
e.printStackTrace();
}

}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

}
}

将自定义的MapperPostProcessor后处理器注册到Bean工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import org.springframework.context.support.GenericApplicationContext;

import java.io.IOException;

/**
* BeanFactory 后处理器的作用
*/
public class A05 {

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

// ⬇️GenericApplicationContext 是一个【干净】的容器,没有添加好的处理器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource
// context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
// bd.getPropertyValues().add("basePackage", "com.xiao.learning.chapter05.mapper");
// });

// context.registerBean(ComponentScanPostProcessor.class); // 解析@Component
context.registerBean(AtBeanPostProcessor.class); // 解析@Bean
context.registerBean(MapperPostProcessor.class); // 解析@Mapper


// ⬇️初始化容器
context.refresh();

for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}

// ⬇️销毁容器
context.close();
}
}

收获💡

  1. Mapper 接口被 Spring 管理的本质:实际是被作为MapperFactoryBean注册到容器中
  2. Spring 的特殊做法,根据接口生成的BeanDefinition仅为根据接口名生成 bean 名

Aware接口

Aware接口及InitializingBean 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {

private static final Logger log = LoggerFactory.getLogger(MyBean.class);

@Override
public void setBeanName(String name) {
log.info("实现BeanNameAware【内置】接口的setBeanName方法初始化");
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("实现ApplicationContextAware【内置】接口的setApplicationContext方法初始化);
}

@Override
public void afterPropertiesSet() throws Exception {
log.info("实现InitializingBean【内置】接口的afterPropertiesSet方法初始化");
}

@Autowired
public void aaa(ApplicationContext applicationContext) {
log.info("使用@Autowired【拓展】注解进行初始化");
}

@PostConstruct
public void init() {
log.info("使用@PostConstruct【拓展】注解进行初始化");
}
}

// =============执行结果====================
[INFO ] 14:03:18.394 [main] com.xiao.learning.chapter06.MyBean - 使用@Autowired【拓展】注解进行初始化
[INFO ] 14:03:18.400 [main] com.xiao.learning.chapter06.MyBean - 实现BeanNameAware【内置】接口的setBeanName方法初始化
[INFO ] 14:03:18.401 [main] com.xiao.learning.chapter06.MyBean - 实现ApplicationContextAware【内置】接口的setApplicationContext方法初始化
[INFO ] 14:03:18.404 [main] com.xiao.learning.chapter06.MyBean - 使用@PostConstruct【拓展】注解进行初始化
[INFO ] 14:03:18.404 [main] com.xiao.learning.chapter06.MyBean - 实现InitializingBean【内置】接口的afterPropertiesSet方法初始化

运行类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

/*
Aware 接口及 InitializingBean 接口
*/
public class A06 {

public static void main(String[] args) {
/*
1. Aware 接口用于注入一些与容器相关信息, 例如
a. BeanNameAware 注入 bean 的名字
b. BeanFactoryAware 注入 BeanFactory 容器
c. ApplicationContextAware 注入 ApplicationContext 容器
d. EmbeddedValueResolverAware ${}

*/
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myBean", MyBean.class);
context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // 使Autowired注解生效
context.registerBean(CommonAnnotationBeanPostProcessor.class); // 使PostConstruct注解生效

/*
2.b、c、d 的功能用 @Autowired 就能实现啊, 为啥还要用 Aware 接口呢
简单地说:
a. @Autowired 的解析需要用到 bean 后处理器, 属于扩展功能
b. 而 Aware 接口属于内置功能, 不加任何扩展, Spring 就能识别
某些情况下, 扩展功能会失效, 而内置功能不会失效

例1: 你会发现用 Aware 注入 ApplicationContext 成功, 而 @Autowired 注入 ApplicationContext 失败
*/
context.refresh(); // 1. beanFactory 后处理器, 2. 添加 bean 后处理器, 3. 初始化单例
context.close();
}
}

小结💡

  1. Aware 接口提供了一种【内置】 的注入手段,例如
    • BeanNameAware 注入 bean 的名字
    • BeanFactoryAware 注入 BeanFactory 容器
    • ApplicationContextAware 注入 ApplicationContext 容器
    • EmbeddedValueResolverAware 注入 ${} 解析器
  2. InitializingBean 接口提供了一种【内置】的初始化手段
  3. 对比
    • 内置的注入和初始化不受扩展功能的影响,总会被执行
    • 而扩展功能受某些情况影响可能会失效
    • 因此 Spring 框架内部的类常用内置注入和初始化

配置类@Autowired 失效分析

Java 配置类不包含 BeanFactoryPostProcessor 的情况

sequenceDiagram 
participant ac as ApplicationContext
participant bfpp as BeanFactoryPostProcessor
participant bpp as BeanPostProcessor
participant config as Java配置类
ac ->> bfpp : 1. 执行 BeanFactoryPostProcessor
ac ->> bpp : 2. 注册 BeanPostProcessor
ac ->> +config : 3. 创建和初始化
bpp ->> config : 3.1 依赖注入扩展(如 @Value 和 @Autowired)
bpp ->> config : 3.2 初始化扩展(如 @PostConstruct)
ac ->> config : 3.3 执行 Aware 及 InitializingBean
config -->> -ac : 3.4 创建成功

Java 配置类包含 BeanFactoryPostProcessor 的情况,因此要创建其中的 BeanFactoryPostProcessor 必须提前创建 Java 配置类,而此时的 BeanPostProcessor 还未准备好,导致 @Autowired 等注解失效

sequenceDiagram 
participant ac as ApplicationContext
participant bfpp as BeanFactoryPostProcessor
participant bpp as BeanPostProcessor
participant config as Java配置类
ac ->> +config : 3. 创建和初始化
ac ->> config : 3.1 执行 Aware 及 InitializingBean
config -->> -ac : 3.2 创建成功

ac ->> bfpp : 1. 执行 BeanFactoryPostProcessor
ac ->> bpp : 2. 注册 BeanPostProcessor



对应代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration
public class MyConfig1 {

private static final Logger log = LoggerFactory.getLogger(MyConfig1.class);

@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
log.info("注入 ApplicationContext");
}

@PostConstruct
public void init() {
log.info("初始化");
}

@Bean // beanFactory 后处理器
public BeanFactoryPostProcessor processor1() {
return beanFactory -> log.info("执行 processor1");
}

}

注意

解决方法:

  • 用内置依赖注入和初始化取代扩展依赖注入和初始化
  • 用静态工厂方法代替实例工厂方法,避免工厂对象提前被创建

初始化与销毁

演示 - 初始化销毁顺序

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Bean1 implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);

@PostConstruct
public void init1() {
log.debug("使用@PostConstruct初始化");
}

@Override
public void afterPropertiesSet() throws Exception {
log.debug("实现InitializingBean接口的afterPropertiesSet方法初始化");
}

public void init3() {
log.debug("使用@Bean注解的initMethod初始化");
}
}

// ==========打印结果================
[DEBUG] 13:54:39.675 [main] com.xiao.learning.chapter07.Bean1 - 使用@PostConstruct初始化
[DEBUG] 13:54:39.675 [main] com.xiao.learning.chapter07.Bean1 - 实现InitializingBean接口的afterPropertiesSet方法初始化
[DEBUG] 13:54:39.675 [main] com.xiao.learning.chapter07.Bean1 - 使用@Bean注解的initMethod初始化

销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Bean2 implements DisposableBean {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);

@PreDestroy
public void destroy1() {
log.debug("使用@PreDestroy注解销毁");
}

@Override
public void destroy() throws Exception {
log.debug("实现DisposableBean接口的destroy方法销毁");
}

public void destroy3() {
log.debug("使用@Bean注解的destroyMethod销毁");
}
}

// ====================执行结果===================
[DEBUG] 13:57:31.691 [main] com.xiao.learning.chapter07.Bean2 - 使用@PreDestroy注解销毁
[DEBUG] 13:57:31.691 [main] com.xiao.learning.chapter07.Bean2 - 实现DisposableBean接口的destroy方法销毁
[DEBUG] 13:57:31.691 [main] com.xiao.learning.chapter07.Bean2 - 使用@Bean注解的destroyMethod销毁

小结💡

Spring 提供了多种初始化手段,除了上述代码讲的@PostConstruct@Bean(initMethod) 之外,还可以实现 InitializingBean接口来进行初始化,如果同一个 bean 用了以上手段声明了 3 个初始化方法,那么它们的执行顺序是

  1. @PostConstruct 标注的初始化方法
  2. InitializingBean 接口的初始化方法
  3. @Bean(initMethod) 指定的初始化方法

与初始化类似,Spring 也提供了多种销毁手段,执行顺序为

  1. @PreDestroy 标注的销毁方法
  2. DisposableBean 接口的销毁方法
  3. @Bean(destroyMethod) 指定的销毁方法

Scope

在当前版本的Spring和Spring Boot程序中,支持五种Scope

  • singleton,容器启动时创建(未设置延迟),容器关闭时销毁
  • prototype,每次使用时创建,不会自动销毁,需要调用DefaultListableBeanFactory.destroyBean(bean) 销毁
  • request,每次请求用到此 bean 时创建,请求结束时销毁
  • session,每个会话用到此 bean 时创建,会话结束时销毁
  • application,web 容器用到此 bean 时创建,容器停止时销毁

有些文章提到有 globalSession 这一 Scope,也是陈旧的说法,目前 Spring 中已废弃

但要注意,如果在singleton 注入其它 scope 都会有问题,解决方法有

  • @Lazy
  • @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
  • ObjectFactory
  • ApplicationContext.getBean

演示 - request, session, application 作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Scope("application")
@Component
public class BeanForApplication {
private static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);

@PreDestroy
public void destroy() {
log.debug("@Scope的application作用域销毁");
}
}

@Scope("request")
@Component
public class BeanForRequest {
private static final Logger log = LoggerFactory.getLogger(BeanForRequest.class);

@PreDestroy
public void destroy() {
log.debug("@Scope的request作用域销毁");
}

}

@Scope("session")
@Component
public class BeanForSession {
private static final Logger log = LoggerFactory.getLogger(BeanForSession.class);

@PreDestroy
public void destroy() {
log.debug("@Scope的session作用域销毁");
}
}

控制层代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@RestController
public class MyController {

@Lazy
@Autowired
private BeanForRequest beanForRequest;

@Lazy
@Autowired
private BeanForSession beanForSession;

@Lazy
@Autowired
private BeanForApplication beanForApplication;

@GetMapping(value = "/test", produces = "text/html")
public String test(HttpServletRequest request, HttpSession session) {
ServletContext sc = request.getServletContext();
String sb = "<ul>" +
"<li>" + "request scope:" + beanForRequest + "</li>" +
"<li>" + "session scope:" + beanForSession + "</li>" +
"<li>" + "application scope:" + beanForApplication + "</li>" +
"</ul>";
return sb;
}

}
  • 打开不同的浏览器, 刷新 http://localhost:8080/test 即可查看效果
  • 如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED

小结💡

  1. 有几种 scope
  2. 在 singleton 中使用其它几种 scope 的方法
  3. 其它 scope 的销毁时机
    • 可以将通过 server.servlet.session.timeout=30s 观察 session bean 的销毁
    • ServletContextScope 销毁机制疑似实现有误

分析 - singleton 注入其它 scope 失效

以单例注入多例为例

有一个单例对象 E

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component
public class E {
private static final Logger log = LoggerFactory.getLogger(E.class);

private F1 f1;

public E() {
log.info("E()");
}

@Autowired
public void setF(F1 f1) {
this.f1 = f1;
log.info("setF1(F1 f1) {}", f1.getClass());
}

public F1 getF1() {
return f1;
}
}

要注入的对象 F 期望是多例

1
2
3
4
5
6
7
8
9
@Component
@Scope("prototype")
public class F1 {
private static final Logger log = LoggerFactory.getLogger(F1.class);

public F1() {
log.info("F1()");
}
}

测试

1
2
3
4
5
E e = context.getBean(E.class);
log.info("{}", e.getF1().getClass());
log.info("{}", e.getF1());
log.info("{}", e.getF1());
log.info("{}", e.getF1());

输出

1
2
3
4
class com.xiao.learning.chapter08.sub.F1 
com.xiao.learning.chapter08.sub.F1@762ef0ea
com.xiao.learning.chapter08.sub.F1@762ef0ea
com.xiao.learning.chapter08.sub.F1@762ef0ea

发现它们是同一个对象,而不是期望的多例对象

对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F,因此 E 用的始终是第一次依赖注入的 F

graph LR

e1(e 创建)
e2(e set 注入 f)

f1(f 创建)

e1-->f1-->e2

解决

  • 仍然使用 @Lazy 生成代理
  • 代理对象虽然还是同一个,但当每次使用代理对象的任意方法时,由代理创建新的 f 对象
graph LR

e1(e 创建)
e2(e set 注入 f代理)

f1(f 创建)
f2(f 创建)
f3(f 创建)

e1-->e2
e2--使用f方法-->f1
e2--使用f方法-->f2
e2--使用f方法-->f3

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class E {

@Autowired
@Lazy
public void setF(F f) {
this.f = f;
log.info("setF(F f) {}", f.getClass());
}

// ...
}

注意

  • @Lazy 加在也可以加在成员变量上,但加在 set 方法上的目的是可以观察输出,加在成员变量上就不行了
  • @Autowired 加在 set 方法的目的类似

输出

1
2
3
4
5
6
[INFO ] 16:32:04.451 [main] com.xiao.learning.chapter08.sub.E   - E() 
[INFO ] 16:32:04.617 [main] com.xiao.learning.chapter08.sub.E - setF1(F1 f1) class com.xiao.learning.chapter08.sub.F1$$EnhancerBySpringCGLIB$$af868791
[INFO ] 16:32:04.670 [main] com.xiao.learning.chapter08.A08_1 - class com.xiao.learning.chapter08.sub.F1$$EnhancerBySpringCGLIB$$af868791
[INFO ] 16:32:04.670 [main] com.xiao.learning.chapter08.A08_1 - com.xiao.learning.chapter08.sub.F1@424e1977
[INFO ] 16:32:04.680 [main] com.xiao.learning.chapter08.A08_1 - com.xiao.learning.chapter08.sub.F1@117e949d
[INFO ] 16:32:04.681 [main] com.xiao.learning.chapter08.A08_1 - com.xiao.learning.chapter08.sub.F1@5f8edcc5

从输出日志可以看到调用 setF 方法时,f 对象的类型是代理类型

演示 - 4种解决方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Component
public class E {

@Lazy // 第一种通过@Lazy注解
@Autowired
private F1 f1;

@Autowired
private F2 f2;

@Autowired
private ObjectFactory<F3> f3;

@Autowired
private ApplicationContext context;

public F1 getF1() {
return f1;
}

public F2 getF2() {
return f2;
}

public F3 getF3() {
return f3.getObject(); // 第三种通过ObjectFactory工厂的getObject获取
}

public F4 getF4() {
return context.getBean(F4.class); // 第四种通过ApplicationContext上下文的getBean获取
}
}
// 第二种通过@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}

  • 如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED

小结💡

  1. 单例注入其它 scope 的四种解决方法
    • @Lazy
    • @Scope(value = “prototype”, proxyMode = ScopedProxyMode.TARGET_CLASS)
    • ObjectFactory
    • ApplicationContext
  2. 解决方法虽然不同,但理念上殊途同归: 都是推迟其它 scope bean 的获取

AOP

AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能

除此以外,aspectj 提供了两种另外的 AOP 底层实现:

  • 第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中

  • 第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能

  • 作为对比,之前学习的代理是运行时生成新的字节码

简单比较的话:

  • aspectj 在编译和加载时,修改目标字节码,性能较高
  • aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强
  • 但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行

AOP实现之ajc编译器

pom.xml新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!-- 新增jar -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<!-- 新增ajc编译器 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>8</source>
<target>8</target>
<showWeaveInfo>true</showWeaveInfo>
<verbose>true</verbose>
<Xlint>ignore</Xlint>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<goals>
<!-- use this goal to weave all your main classes -->
<goal>compile</goal>
<!-- use this goal to weave all your test classes -->
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>

MyAspect类

1
2
3
4
5
6
7
8
9
10
@Aspect // ⬅️注意此切面并未被 Spring 管理
public class MyAspect {

private static final Logger log = LoggerFactory.getLogger(MyAspect.class);

@Before("execution(* com.xiao.learning.chapter09.service.MyService.foo())")
public void before() {
log.info("before()");
}
}

MyService类

1
2
3
4
5
6
7
8
9
10
@Service
public class MyService {

private static final Logger log = LoggerFactory.getLogger(MyService.class);

// 静态方法编译器也可以增强
public void foo() {
log.info("foo()");
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@SpringBootApplication
public class A09 {

private static final Logger log = LoggerFactory.getLogger(A09.class);

public static void main(String[] args) {
// ConfigurableApplicationContext context = SpringApplication.run(A09.class, args);
// MyService service = context.getBean(MyService.class);
//
// log.debug("service class: {}", service.getClass());
// service.foo();
//
// context.close();

// 未使用Spring也可以增强,字节码文件被增强了
new MyService().foo();
}
}

反编译后的MyAspect字节码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Aspect
public class MyAspect {
private static final Logger log = LoggerFactory.getLogger(MyAspect.class);

static {
try {
ajc$postClinit();
} catch (Throwable var1) {
ajc$initFailureCause = var1;
}

}

public MyAspect() {
}

@Before("execution(* com.xiao.learning.chapter09.service.MyService.foo())")
public void before() {
log.info("before()");
}

public static MyAspect aspectOf() {
if (ajc$perSingletonInstance == null) {
throw new NoAspectBoundException("com.xiao.learning.chapter09.aop.MyAspect", ajc$initFailureCause);
} else {
return ajc$perSingletonInstance;
}
}

public static boolean hasAspect() {
return ajc$perSingletonInstance != null;
}
}

反编译后的MyService字节码

1
2
3
4
5
6
7
8
9
10
11
12
@Service
public class MyService {
private static final Logger log = LoggerFactory.getLogger(MyService.class);

public MyService() {
}

public void foo() {
MyAspect.aspectOf().before();
log.info("foo()");
}
}

小结💡

  1. 编译器也能修改 class 实现增强
  2. 编译器增强能突破代理仅能通过方法重写增强的限制:可以对构造方法、静态方法等实现增强

注意

  • 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
  • 一定要用 maven 的 compile 来编译, idea 不会调用 ajc 编译器

AOP实现之agent类加载

MyAspect类

1
2
3
4
5
6
7
8
9
10
11
@Aspect // ⬅️注意此切面并未被 Spring 管理
public class MyAspect {

private static final Logger log = LoggerFactory.getLogger(MyAspect.class);

@Before("execution(* com.xiao.learning.chapter10.service.MyService.*())")
public void before() {
log.info("before()");
}
}

MyService类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class MyService {

private static final Logger log = LoggerFactory.getLogger(MyService.class);

final public void foo() {
log.info("foo()");
this.bar(); // 对于CGLIB这种代理类来说,是不会走增强的,但是这里会走
}

public void bar() {
log.info("bar()");
}
}

在resources下新增META-INF\aop.xml文件

1
2
3
4
5
6
7
8
9
<aspectj>
<aspects>
<aspect name="com.xiao.learning.chapter10.aop.MyAspect"/>
<weaver options="-verbose -showWeaveInfo">
<include within="com.xiao.learning.chapter10.service.MyService"/>
<include within="com.xiao.learning.chapter10.aop.MyAspect"/>
</weaver>
</aspects>
</aspectj>

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
注意几点
1. 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
2. 运行时需要在 VM options 里加入 -javaagent:D:/software/working/Java/Maven/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
把其中 D:/software/working/Java/Maven/repository 改为你自己 maven 仓库起始地址
3. 在resources下新增META-INF\aop.xml文件
*/
@SpringBootApplication
public class A10 {

private static final Logger log = LoggerFactory.getLogger(A10.class);

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A10.class, args);
MyService service = context.getBean(MyService.class);

// ⬇️MyService 并非代理, 但 foo 方法也被增强了, 做增强的 java agent, 在加载类时, 修改了 class 字节码
log.debug("service class: {}", service.getClass());
service.foo();

// context.close();
}
}

通过阿里巴巴的arthas进行反编译,因为它这个增强是运行时增强的

1
2
jad  com.xiao.learning.chapter10.aop.MyAspect
jad com.xiao.learning.chapter10.service.MyService

反编译后的MyAspect字节码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

@Aspect
public class MyAspect {
private static final Logger log = LoggerFactory.getLogger(MyAspect.class);
private static /* synthetic */ Throwable ajc$initFailureCause;
public static /* synthetic */ MyAspect ajc$perSingletonInstance;

@Before(value="execution(* com.xiao.learning.chapter10.service.MyService.*())")
public void before() {
/*15*/ log.info("before()");
}

static {
try {
MyAspect.ajc$perSingletonInstance = new MyAspect();
}
catch (Throwable throwable) {
ajc$initFailureCause = throwable;
}
}

public static boolean hasAspect() {
/* 1*/ return ajc$perSingletonInstance != null;
}

public static MyAspect aspectOf() {
/* 1*/ if (ajc$perSingletonInstance == null) {
throw new NoAspectBoundException("com.xiao.learning.chapter10.aop.MyAspect", ajc$initFailureCause);
}
return ajc$perSingletonInstance;
}
}

反编译后的MyAspect字节码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class MyService {
private static final Logger log = LoggerFactory.getLogger(MyService.class);

public final void foo() {
/*13*/ MyAspect.aspectOf().before();
log.info("foo()");
/*14*/ this.bar();
}

public void bar() {
/*18*/ MyAspect.aspectOf().before();
log.info("bar()");
}
}

小结💡

  1. 类加载时可以通过 agent 修改 class 实现增强

AOP 实现之proxy

演示 - jdk 动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class JdkProxyDemo {

interface Foo {
void foo();
}

static final class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
}

// jdk 只能针对接口代理
// cglib
public static void main(String[] param) throws IOException {
// 目标对象
Target target = new Target();

ClassLoader loader = JdkProxyDemo.class.getClassLoader(); // 用来加载在运行期间动态生成的字节码
Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, (p, method, args) -> {
System.out.println("before...");
// 目标.方法(参数)
// 方法.invoke(目标, 参数);
Object result = method.invoke(target, args);
System.out.println("after....");
return result; // 让代理也返回目标方法执行的结果
});

System.out.println(proxy.getClass());

proxy.foo();

System.in.read();
}
}

运行结果

1
2
3
4
class com.xiao.learning.chapter11.$Proxy0 // 通过阿里巴巴的arthas工具查看,使用命令 jad com.xiao.learning.chapter11.$Proxy0
proxy before...
target foo
proxy after...

代理类生成的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* com.xiao.learning.chapter11.JdkProxyDemo$Foo
*/
package com.xiao.learning.chapter11;

import com.xiao.learning.chapter11.JdkProxyDemo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0
extends Proxy
implements JdkProxyDemo.Foo {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;

public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}

static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.xiao.learning.chapter11.JdkProxyDemo$Foo").getMethod("foo", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}

public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

public final void foo() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}

小结💡

  • jdk 动态代理要求目标必须实现接口,生成的代理类实现相同接口,因此代理与目标之间是平级兄弟关系

演示 - cglib 代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;

public class CglibProxyDemo {

static class Target {
public void foo() {
System.out.println("target foo");
}
}

public static void main(String[] param) {
// 目标对象
Target target = new Target();
// 代理对象
Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (p, method, args, methodProxy) -> {
System.out.println("proxy before...");
// Object result = method.invoke(target, args); // 用方法反射调用目标

// methodProxy他可以避免反射调用
Object result = methodProxy.invoke(target, args); // 内部没有用反射,需要目标(Spring使用这一种)
// 另一种调用方法,不需要目标对象实例
// Object result = methodProxy.invokeSuper(p, args); // 内部没有用反射,需要代理
System.out.println("proxy after...");
return result;
});
// 调用代理
proxy.foo();
}
}

运行结果与 jdk 动态代理相同

小结💡

  • cglib 不要求目标实现接口,它生成的代理类是目标的子类,因此代理与目标之间是子父关系
  • 限制⛔:根据上述分析final类无法被cglib 增强

jdk动态代理进阶

演示 - 模拟 jdk 动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class A12 {

interface Foo {
void foo();
int bar();
}

static class Target implements Foo {
public void foo() {
System.out.println("target foo");
}

public int bar() {
System.out.println("target bar");
return 100;
}
}

public static void main(String[] param) {
// ⬇️1. 创建代理,这时传入 InvocationHandler
Foo proxy = new $Proxy0(new InvocationHandler() {
// ⬇️5. 进入 InvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
// ⬇️6. 功能增强
System.out.println("before...");
// ⬇️7. 反射调用目标方法
return method.invoke(new Target(), args);
}
});
// ⬇️2. 调用代理方法
proxy.foo();
proxy.bar();
}
}

模拟代理实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

// ⬇️这就是 jdk 代理类的源码, 秘密都在里面
public class $Proxy0 extends Proxy implements A12.Foo {

public $Proxy0(InvocationHandler h) {
super(h);
}
// ⬇️3. 进入代理方法
public void foo() {
try {
// ⬇️4. 回调 InvocationHandler
h.invoke(this, foo, new Object[0]);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

@Override
public int bar() {
try {
Object result = h.invoke(this, bar, new Object[0]);
return (int) result;
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

static Method foo;
static Method bar;
static {
try {
foo = A12.Foo.class.getMethod("foo");
bar = A12.Foo.class.getMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
}

jdk字节码代理生成原理

小结💡

代理一点都不难,无非就是利用了多态、反射的知识

  1. 方法重写可以增强逻辑,只不过这【增强逻辑】千变万化,不能写死在代理内部
  2. 通过接口回调将【增强逻辑】置于代理类之外
  3. 配合接口方法反射(是多态调用),就可以再联动调用目标方法
  4. 会用 arthas 的 jad 工具反编译代理类
  5. 限制⛔:代理增强是借助多态来实现,因此成员变量、静态方法、final 方法均不能通过代理实现

演示 - 方法反射优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestMethodInvoke {
public static void main(String[] args) throws Exception {
Method foo = TestMethodInvoke.class.getMethod("foo", int.class);
for (int i = 1; i <= 17; i++) {
show(i, foo);
foo.invoke(null, i);
}
System.in.read();
}

// 方法反射调用时, 底层 MethodAccessor 的实现类
private static void show(int i, Method foo) throws Exception {
Method getMethodAccessor = Method.class.getDeclaredMethod("getMethodAccessor");
getMethodAccessor.setAccessible(true);
Object invoke = getMethodAccessor.invoke(foo);
if (invoke == null) {
System.out.println(i + ":" + null);
return;
}
Field delegate = Class.forName("sun.reflect.DelegatingMethodAccessorImpl").getDeclaredField("delegate");
delegate.setAccessible(true);
System.out.println(i + ":" + delegate.get(invoke));
}

public static void foo(int i) {
System.out.println(i + ":" + "foo");
}
}

第十七次生成的对象源码

使用arthas的jad sun.reflect.GeneratedMethodAccessor2命令反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package sun.reflect;

import com.xiao.learning.TestMethodInvoke;
import java.lang.reflect.InvocationTargetException;
import sun.reflect.MethodAccessorImpl;

public class GeneratedMethodAccessor2
extends MethodAccessorImpl {
/*
* Loose catch block
*/
public Object invoke(Object object, Object[] objectArray) throws InvocationTargetException {
char c;
block9: {
if (objectArray.length != 1) {
throw new IllegalArgumentException();
}
Object object2 = objectArray[0];
if (object2 instanceof Byte) {
c = ((Byte)object2).byteValue();
break block9;
}
if (object2 instanceof Character) {
c = ((Character)object2).charValue();
break block9;
}
if (object2 instanceof Short) {
c = (char)((Short)object2).shortValue();
break block9;
}
if (object2 instanceof Integer) {
c = (char)((Integer)object2).intValue();
break block9;
}
throw new IllegalArgumentException();
}
try {
// 没有使用反射调用
TestMethodInvoke.foo((int)c);
return null;
}
catch (Throwable throwable) {
throw new InvocationTargetException(throwable);
}
catch (ClassCastException | NullPointerException runtimeException) {
throw new IllegalArgumentException(super.toString());
}
}
}

小结💡

  1. 前 16 次反射性能较低
  2. 第 17 次调用会生成代理类,优化为非反射调用
  3. 会用 arthas 的 jad 工具反编译第 17 次调用生成的代理类

cglib 代理进阶

演示 - 模拟 cglib 代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class TestCGLIB {

public static void main(String[] args) {
Proxy proxy = new Proxy();
Target target = new Target();

proxy.setMethodInterceptor(new MethodInterceptor() {
@Override
public Object intercept(Object p, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
// 调用目标方法(三种)
// return method.invoke(target, args); // 反射调用
// return methodProxy.invoke(target, args); // 内部无反射, 结合目标用
return methodProxy.invokeSuper(p, args); // 内部无反射, 结合代理用
}
});

proxy.save();
proxy.save(1);
proxy.save(2L);
}
}

代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;

public class Proxy extends Target {

private MethodInterceptor methodInterceptor;

public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}

static Method save0;
static Method save1;
static Method save2;
static MethodProxy save0Proxy;
static MethodProxy save1Proxy;
static MethodProxy save2Proxy;
static {
try {
save0 = Target.class.getMethod("save");
save1 = Target.class.getMethod("save", int.class);
save2 = Target.class.getMethod("save", long.class);
save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");
save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}

// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带原始功能的方法
public void saveSuper() {
super.save();
}
public void saveSuper(int i) {
super.save(i);
}
public void saveSuper(long j) {
super.save(j);
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法
@Override
public void save() {
try {
methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

@Override
public void save(int i) {
try {
methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

@Override
public void save(long j) {
try {
methodInterceptor.intercept(this, save2, new Object[]{j}, save2Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}

目标类

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Target {
public void save() {
System.out.println("save()");
}

public void save(int i) {
System.out.println("save(int)");
}

public void save(long j) {
System.out.println("save(long)");
}
}

小结💡

和 jdk 动态代理原理查不多

  1. 回调的接口换了一下,InvocationHandler 改成了 MethodInterceptor
  2. 调用目标时有所改进,见下面代码片段
    1. method.invoke 是反射调用,必须调用到足够次数才会进行优化
    2. methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)
    3. methodProxy.invokeSuper 也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象

cglib 避免反射调用

演示 - cglib 如何避免反射

methodProxy.invoke原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import org.springframework.cglib.core.Signature;

// 模拟实现org.springframework.cglib.reflect.FastClass,主要实现了两个方法
public class TargetFastClass {
static Signature s0 = new Signature("save", "()V");
static Signature s1 = new Signature("save", "(I)V");
static Signature s2 = new Signature("save", "(J)V");

// 获取目标方法的编号
/*
Target
save() 0
save(int) 1
save(long) 2
signature 包括方法名字、参数返回值
*/
public int getIndex(Signature signature) {
if (s0.equals(signature)) {
return 0;
} else if (s1.equals(signature)) {
return 1;
} else if (s2.equals(signature)) {
return 2;
}
return -1;
}

// 根据方法编号, 正常调用目标对象方法
public Object invoke(int index, Object target, Object[] args) {
if (index == 0) {
((Target) target).save();
return null;
} else if (index == 1) {
((Target) target).save((int) args[0]);
return null;
} else if (index == 2) {
((Target) target).save((long) args[0]);
return null;
} else {
throw new RuntimeException("无此方法");
}
}

public static void main(String[] args) {
TargetFastClass fastClass = new TargetFastClass();
int index = fastClass.getIndex(new Signature("save", "(I)V"));
System.out.println(index);
fastClass.invoke(index, new Target(), new Object[]{100});
}
}

methodProxy.invokeSuper原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import org.springframework.cglib.core.Signature;

// 模拟实现org.springframework.cglib.reflect.FastClass,主要实现了两个方法
public class ProxyFastClass {
static Signature s0 = new Signature("saveSuper", "()V");
static Signature s1 = new Signature("saveSuper", "(I)V");
static Signature s2 = new Signature("saveSuper", "(J)V");

// 获取代理方法的编号
/*
Proxy
saveSuper() 0
saveSuper(int) 1
saveSuper(long) 2
signature 包括方法名字、参数返回值
*/
public int getIndex(Signature signature) {
if (s0.equals(signature)) {
return 0;
} else if (s1.equals(signature)) {
return 1;
} else if (s2.equals(signature)) {
return 2;
}
return -1;
}

// 根据方法编号, 正常调用目标对象方法
public Object invoke(int index, Object proxy, Object[] args) {
if (index == 0) {
((Proxy) proxy).saveSuper();
return null;
} else if (index == 1) {
((Proxy) proxy).saveSuper((int) args[0]);
return null;
} else if (index == 2) {
((Proxy) proxy).saveSuper((long) args[0]);
return null;
} else {
throw new RuntimeException("无此方法");
}
}

public static void main(String[] args) {
ProxyFastClass fastClass = new ProxyFastClass();
int index = fastClass.getIndex(new Signature("saveSuper", "()V"));
System.out.println(index);

fastClass.invoke(index, new Proxy(), new Object[0]);
}
}

小结💡

  1. 当调用MethodProxyinvokeinvokeSuper方法时, 会动态生成两个类
    • ProxyFastClass配合代理对象一起使用, 避免反射
    • TargetFastClass配合目标对象一起使用, 避免反射 (Spring 用的这种)
  2. TargetFastClass 记录了 Target 中方法与编号的对应关系
    • save(long) 编号 2
    • save(int) 编号 1
    • save() 编号 0
    • 首先根据方法名和参数个数、类型, 用 switch 和 if 找到这些方法编号
    • 然后再根据编号去调用目标方法, 又用了一大堆 switch 和 if, 但避免了反射
  3. ProxyFastClass 记录了 Proxy 中方法与编号的对应关系,不过 Proxy 额外提供了下面几个方法
    • saveSuper(long) 编号 2,不增强,仅是调用 super.save(long)
    • saveSuper(int) 编号 1,不增强, 仅是调用 super.save(int)
    • saveSuper() 编号 0,不增强, 仅是调用 super.save()
    • 查找方式与 TargetFastClass 类似
  4. 为什么有这么麻烦的一套东西呢?
    • 避免反射, 提高性能, 代价是一个代理类配两个 FastClass 类, 代理类中还得增加仅调用 super 的一堆方法
    • 用编号处理方法对应关系比较省内存, 另外, 最初获得方法顺序是不确定的, 这个过程没法固定死

jdk 和 cglib 在 Spring 中的统一

Spring 中对切点、通知、切面的抽象如下

  • 切点:接口 Pointcut,典型实现 AspectJExpressionPointcut
  • 通知:典型接口为 MethodInterceptor 代表环绕通知
  • 切面:Advisor,包含一个 Advice 通知,PointcutAdvisor 包含一个 Advice 通知和一个 Pointcut
classDiagram

class Advice
class MethodInterceptor
class Advisor
class PointcutAdvisor

Pointcut <|-- AspectJExpressionPointcut
Advice <|-- MethodInterceptor
Advisor <|-- PointcutAdvisor
PointcutAdvisor o-- "一" Pointcut
PointcutAdvisor o-- "一" Advice

<<interface>> Advice
<<interface>> MethodInterceptor
<<interface>> Pointcut
<<interface>> Advisor
<<interface>> PointcutAdvisor

代理相关类图

  • AopProxyFactory 根据 proxyTargetClass 等设置选择 AopProxy 实现
  • AopProxy 通过 getProxy 创建代理对象
  • 图中 Proxy 都实现了 Advised 接口,能够获得关联的切面集合与目标(其实是从 ProxyFactory 取得)
  • 调用代理方法时,会借助 ProxyFactory 将通知统一转为环绕通知:MethodInterceptor
classDiagram

Advised <|-- ProxyFactory
ProxyFactory o-- Target
ProxyFactory o-- "多" Advisor

ProxyFactory --> AopProxyFactory : 使用
AopProxyFactory --> AopProxy
Advised <|-- 基于CGLIB的Proxy
基于CGLIB的Proxy <-- ObjenesisCglibAopProxy : 创建
AopProxy <|-- ObjenesisCglibAopProxy
AopProxy <|-- JdkDynamicAopProxy
基于JDK的Proxy <-- JdkDynamicAopProxy : 创建
Advised <|-- 基于JDK的Proxy

class AopProxy {
   +getProxy() Object
}

class ProxyFactory {
	proxyTargetClass : boolean
}

class ObjenesisCglibAopProxy {
	advised : ProxyFactory
}

class JdkDynamicAopProxy {
	advised : ProxyFactory
}

<<interface>> Advised
<<interface>> AopProxyFactory
<<interface>> AopProxy

演示 - 底层切点、通知、切面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

public class Test {
public static void main(String[] args) {
/*
两个切面概念
aspect =
通知1(advice) + 切点1(pointcut)
通知2(advice) + 切点2(pointcut)
通知3(advice) + 切点3(pointcut)
...
advisor = 更细粒度的切面,包含一个通知和切点
*/

// 1. 备好切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
// 2. 备好通知
MethodInterceptor advice = invocation -> {
System.out.println("before...");
Object result = invocation.proceed(); // 调用目标
System.out.println("after...");
return result;
};
// 3. 备好切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

/*
4. 创建代理
a. proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
b. proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
c. proxyTargetClass = true, 总是使用 cglib 实现
*/
Target1 target = new Target1();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
factory.setInterfaces(target.getClass().getInterfaces()); // 告诉ProxyFactory,目前类Target1实现了哪些接口
factory.setProxyTargetClass(true);

I1 proxy = (I1) factory.getProxy();
System.out.println("代理类型:" + proxy.getClass());
proxy.foo();
proxy.bar();
}

interface I1 {
void foo();

void bar();
}

static class Target1 implements I1 {
public void foo() {
System.out.println("target1 foo");
}

public void bar() {
System.out.println("target1 bar");
}
}

static class Target2 {
public void foo() {
System.out.println("target2 foo");
}

public void bar() {
System.out.println("target2 bar");
}
}
}

小结💡

  1. 底层的切点实现
  2. 底层的通知实现
  3. 底层的切面实现
  4. ProxyFactory 用来创建代理
    • 如果指定了接口,且 proxyTargetClass = false,使用 JdkDynamicAopProxy
    • 如果没有指定接口,或者 proxyTargetClass = true,使用 ObjenesisCglibAopProxy
      • 例外:如果目标是接口类型或已经是 Jdk 代理,使用 JdkDynamicAopProxy

注意

  • 要区分本章节提到的 MethodInterceptor,它与之前 cglib 中用的的 MethodInterceptor 是不同的接口

切点匹配

演示 - 切点匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;

public class TangentPointMatching {
public static void main(String[] args) throws NoSuchMethodException {
// AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();
// pt1.setExpression("execution(* bar())");
// System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class));
// System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class));

// AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();
// pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
// System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));
// System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));

StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 检查方法上是否加了 Transactional 注解
MergedAnnotations annotations = MergedAnnotations.from(method);
if (annotations.isPresent(Transactional.class)) {
return true;
}
// 查看类上是否加了 Transactional 注解
annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
return annotations.isPresent(Transactional.class);
}
};

System.out.println(pt3.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class));
System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));
System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class));

/*
a. 底层切点实现是如何匹配的: 调用了 aspectj 的匹配方法
b. 比较关键的是它实现了 MethodMatcher 接口, 用来执行方法的匹配
*/
}


static class T1 {
@Transactional
public void foo() {
}

public void bar() {
}
}

@Transactional
static class T2 {
public void foo() {
}
}

@Transactional
interface I3 {
void foo();
}

static class T3 implements I3 {
public void foo() {
}
}
}

小结💡

  1. 常见 aspectj 切点用法
  2. aspectj 切点的局限性,实际的 @Transactional 切点实现

从 @Aspect 到 Advisor

演示 - 代理创建器

注意

  • AnnotationAwareAspectJAutoProxyCreator类的findEligibleAdvisors方法与wrapIfNecessary是受保护的,为了方便调用直接在同包下测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package org.springframework.aop.framework.autoproxy;

import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.Order;

import java.util.List;

public class Test {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1", Aspect1.class);
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
// BeanPostProcessor
// 创建 -> (*) 依赖注入 -> 初始化 (*)

context.refresh();
// for (String beanName : context.getBeanDefinitionNames()) {
// System.out.println("BeanName = " + beanName);
// }

/*
第一个重要方法 findEligibleAdvisors 找到有【资格】的 Advisors
a. 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如下例中的 advisor3
b. 有【资格】的 Advisor 另一部分是高级的, 由本章的主角解析 @Aspect 后获得
*/
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> advisors = creator.findEligibleAdvisors(Target2.class, "target2");
// for (Advisor advisor : advisors) {
// System.out.println(advisor);
// }

/*
第二个重要方法 wrapIfNecessary
a. 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理
*/
Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
System.out.println(o1.getClass());
Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
System.out.println(o2.getClass());

((Target1) o1).foo();
/*
a. 自动代理后处理器 AnnotationAwareAspectJAutoProxyCreator 会帮我们创建代理
b. 通常代理创建的活在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行
c. 高级的 @Aspect 切面会转换为低级的 Advisor 切面, 理解原理, 大道至简
*/
}

static class Target1 {
public void foo() {
System.out.println("target1 foo");
}
}

static class Target2 {
public void bar() {
System.out.println("target2 bar");
}
}

@Aspect // 高级切面类
@Order(1) // 设置优先级
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}

@Before("execution(* foo())")
public void before2() {
System.out.println("aspect1 before2...");
}
}

@Configuration
static class Config {
@Bean // 低级切面
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
advisor.setOrder(2); // 设置优先级
return advisor;
}

@Bean
public MethodInterceptor advice3() {
return invocation -> {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
};
}
}

}

小结💡

  1. AnnotationAwareAspectJAutoProxyCreator 的作用
    • 将高级 @Aspect 切面统一为低级 Advisor 切面
    • 在合适的时机创建代理
  2. findEligibleAdvisors 找到有【资格】的 Advisors
    • 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如上述代码中的 advisor3
    • 有【资格】的 Advisor 另一部分是高级的, 由解析 @Aspect 后获得
  3. wrapIfNecessary
    • 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理
    • 它的调用时机通常在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行

演示 - 代理创建时机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package org.springframework.aop.framework.autoproxy;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

import javax.annotation.PostConstruct;

public class Test {

public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(Config.class);
context.refresh();
context.close();
// 创建 -> (*) 依赖注入 -> 初始化 (*)
/*
学到了什么
a. 代理的创建时机
1. 初始化之后 (无循环依赖时)
2. 实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存
b. 依赖注入与初始化不应该被增强, 仍应被施加于原始对象
*/
}

@Configuration
static class Config {
@Bean // 解析 @Aspect、产生代理
public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}

@Bean // 解析 @Autowired
public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
return new AutowiredAnnotationBeanPostProcessor();
}

@Bean // 解析 @PostConstruct
public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
return new CommonAnnotationBeanPostProcessor();
}

@Bean
public Advisor advisor(MethodInterceptor advice) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, advice);
}

@Bean
public MethodInterceptor advice() {
return (MethodInvocation invocation) -> {
System.out.println("before...");
return invocation.proceed();
};
}

@Bean
public Bean1 bean1() {
return new Bean1();
}

@Bean
public Bean2 bean2() {
return new Bean2();
}
}

static class Bean1 {
public void foo() {

}

public Bean1() {
System.out.println("Bean1()");
}

// 注释这一段在初始化之后创建代理,也就是没有循环依赖
@Autowired
public void setBean2(Bean2 bean2) {
System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
}

@PostConstruct
public void init() {
System.out.println("Bean1 init()");
}
}

static class Bean2 {
public Bean2() {
System.out.println("Bean2()");
}

@Autowired
public void setBean1(Bean1 bean1) {
System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
}

@PostConstruct
public void init() {
System.out.println("Bean2 init()");
}
}
}

运行结果

**初始化之后 (无循环依赖时) **

1
2
3
4
5
6
7
8
9
10
11
[INFO ] 15:56:06.200 [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'org.springframework.aop.framework.autoproxy.A17_1$Config' of type [org.springframework.aop.framework.autoproxy.A17_1$Config$$EnhancerBySpringCGLIB$$1e72de84] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 
[TRACE] 15:56:06.272 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Did not attempt to auto-proxy infrastructure class [org.springframework.aop.Advisor]
[TRACE] 15:56:06.276 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Did not attempt to auto-proxy infrastructure class [org.aopalliance.intercept.MethodInterceptor]
Bean1()
Bean1 init()
// 初始化之后 (无循环依赖时)
[TRACE] 15:56:06.418 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors
Bean2()
// Bean1为代理对象
Bean2 setBean1(bean1) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean1$$EnhancerBySpringCGLIB$$c81ac9e0
Bean2 init()

实例创建后, 依赖注入前 (有循环依赖时)

1
2
3
4
5
6
7
8
9
10
11
12
[INFO ] 16:00:23.316 [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'org.springframework.aop.framework.autoproxy.A17_1$Config' of type [org.springframework.aop.framework.autoproxy.A17_1$Config$$EnhancerBySpringCGLIB$$1e72de84] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 
[TRACE] 16:00:23.366 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Did not attempt to auto-proxy infrastructure class [org.springframework.aop.Advisor]
[TRACE] 16:00:23.369 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Did not attempt to auto-proxy infrastructure class [org.aopalliance.intercept.MethodInterceptor]
Bean1()
Bean2()
// 实例创建后, 依赖注入前 (有循环依赖时)
[TRACE] 16:00:23.502 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors
// // Bean1为代理对象
Bean2 setBean1(bean1) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean1$$EnhancerBySpringCGLIB$$aff7e7d7
Bean2 init()
Bean1 setBean2(bean2) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean2
Bean1 init()

小结💡

  1. 代理的创建时机
    • 初始化之后 (无循环依赖时)
    • 实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存
  2. 依赖注入与初始化不应该被增强, 仍应被施加于原始对象

演示 - @Before 对应的低级通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package org.springframework.aop.framework.autoproxy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectInstanceFactory;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class Test {

static class Aspect {
@Before("execution(* foo())")
public void before1() {
System.out.println("before1");
}

@Before("execution(* foo())")
public void before2() {
System.out.println("before2");
}

public void after() {
System.out.println("after");
}

public void afterReturning() {
System.out.println("afterReturning");
}

public void afterThrowing() {
System.out.println("afterThrowing");
}

public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("around...before");
return pjp.proceed();
} finally {
System.out.println("around...after");
}
}
}

static class Target {
public void foo() {
System.out.println("target foo");
}
}

@SuppressWarnings("all")
public static void main(String[] args) throws Throwable {

AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
// 高级切面转低级切面类
List<Advisor> list = new ArrayList<>();
for (Method method : Aspect.class.getDeclaredMethods()) {
if (method.isAnnotationPresent(Before.class)) {
// 解析切点
String expression = method.getAnnotation(Before.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}
for (Advisor advisor : list) {
System.out.println(advisor);
}
}
}

小结💡

  1. @Before 前置通知会被转换为原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息
    1. 通知代码从哪儿来
    2. 切点是什么(这里为啥要切点, 后面解释)
    3. 通知对象如何创建, 本例共用同一个 Aspect 对象
  2. 类似的还有
    1. AspectJAroundAdvice(环绕通知)
    2. AspectJAfterReturningAdvice
    3. AspectJAfterThrowingAdvice(环绕通知)
    4. AspectJAfterAdvice(环绕通知)

静态通知调用

代理对象调用流程如下(以 JDK 动态代理实现为例)

  • 从 ProxyFactory 获得 Target 和环绕通知链,根据他俩创建 MethodInvocation,简称 mi
  • 首次执行 mi.proceed() 发现有下一个环绕通知,调用它的 invoke(mi)
  • 进入环绕通知1,执行前增强,再次调用 mi.proceed() 发现有下一个环绕通知,调用它的 invoke(mi)
  • 进入环绕通知2,执行前增强,调用 mi.proceed() 发现没有环绕通知,调用 mi.invokeJoinPoint() 执行目标方法
  • 目标方法执行结束,将结果返回给环绕通知2,执行环绕通知2 的后增强
  • 环绕通知2继续将结果返回给环绕通知1,执行环绕通知1 的后增强
  • 环绕通知1返回最终的结果

图中不同颜色对应一次环绕通知或目标的调用起始至终结

sequenceDiagram
participant Proxy
participant ih as InvocationHandler
participant mi as MethodInvocation
participant Factory as ProxyFactory
participant mi1 as MethodInterceptor1
participant mi2 as MethodInterceptor2
participant Target

Proxy ->> +ih : invoke()
ih ->> +Factory : 获得 Target
Factory -->> -ih : 
ih ->> +Factory : 获得 MethodInterceptor 链
Factory -->> -ih : 
ih ->> +mi : 创建 mi
mi -->> -ih : 
rect rgb(200, 223, 255)
ih ->> +mi : mi.proceed()
mi ->> +mi1 : invoke(mi)
mi1 ->> mi1 : 前增强
rect rgb(200, 190, 255)
mi1 ->> mi : mi.proceed()
mi ->> +mi2 : invoke(mi)
mi2 ->> mi2 : 前增强
rect rgb(150, 190, 155)
mi2 ->> mi : mi.proceed()
mi ->> +Target : mi.invokeJoinPoint()
Target ->> Target : 
Target -->> -mi2 : 结果
end
mi2 ->> mi2 : 后增强
mi2 -->> -mi1 : 结果
end
mi1 ->> mi1 : 后增强
mi1 -->> -mi : 结果
mi -->> -ih : 
end
ih -->> -Proxy : 

演示 - 通知调用过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package org.springframework.aop.framework;

import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.*;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class Test {

static class Aspect {
@Before("execution(* foo())")
public void before1() {
System.out.println("before1");
}

@Before("execution(* foo())")
public void before2() {
System.out.println("before2");
}

public void after() {
System.out.println("after");
}

@AfterReturning("execution(* foo())")
public void afterReturning() {
System.out.println("afterReturning");
}

@AfterThrowing("execution(* foo())")
public void afterThrowing(Exception e) {
System.out.println("afterThrowing " + e.getMessage());
}

@Around("execution(* foo())")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("around...before");
return pjp.proceed();
} finally {
System.out.println("around...after");
}
}
}

static class Target {
public void foo() {
System.out.println("target foo");
}
}

@SuppressWarnings("all")
public static void main(String[] args) throws Throwable {

AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
// 1. 高级切面转低级切面类
List<Advisor> list = new ArrayList<>();
for (Method method : Aspect.class.getDeclaredMethods()) {
if (method.isAnnotationPresent(Before.class)) {
// 解析切点
String expression = method.getAnnotation(Before.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
} else if (method.isAnnotationPresent(AfterReturning.class)) {
// 解析切点
String expression = method.getAnnotation(AfterReturning.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
} else if (method.isAnnotationPresent(Around.class)) {
// 解析切点
String expression = method.getAnnotation(Around.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}
for (Advisor advisor : list) {
System.out.println(advisor);
}

/*
@Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息
a. 通知代码从哪儿来
b. 切点是什么
c. 通知对象如何创建, 本例共用同一个 Aspect 对象
类似的通知还有
1. AspectJAroundAdvice (环绕通知)
2. AspectJAfterReturningAdvice
3. AspectJAfterThrowingAdvice (环绕通知)
4. AspectJAfterAdvice (环绕通知)
*/

// 2. 通知统一转换为环绕通知 MethodInterceptor
/*

其实无论 ProxyFactory 基于哪种方式创建代理, 最后干活(调用 advice)的是一个 MethodInvocation 对象
a. 因为 advisor 有多个, 且一个套一个调用, 因此需要一个调用链对象, 即 MethodInvocation
b. MethodInvocation 要知道 advice 有哪些, 还要知道目标, 调用次序如下

将 MethodInvocation 放入当前线程
|-> before1 ----------------------------------- 从当前线程获取 MethodInvocation
| |
| |-> before2 -------------------- | 从当前线程获取 MethodInvocation
| | | |
| | |-> target ------ 目标 advice2 advice1
| | | |
| |-> after2 --------------------- |
| |
|-> after1 ------------------------------------
c. 从上图看出, 环绕通知才适合作为 advice, 因此其他 before、afterReturning 都会被转换成环绕通知
d. 统一转换为环绕通知, 体现的是设计模式中的适配器模式
- 对外是为了方便使用要区分 before、afterReturning
- 对内统一都是环绕通知, 统一用 MethodInterceptor 表示

此步获取所有执行时需要的 advice (静态)
a. 即统一转换为 MethodInterceptor 环绕通知, 这体现在方法名中的 Interceptors 上
b. 适配如下
- MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
- AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor
*/
Target target = new Target();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); // 准备把 MethodInvocation 放入当前线程
proxyFactory.addAdvisors(list);

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
for (Object o : methodInterceptorList) {
System.out.println(o);
}

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 3. 创建并执行调用链 (环绕通知 + 目标)
MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList
);
methodInvocation.proceed();
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.springframework.aop.framework.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.before2()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAfterReturningAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.afterReturning()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.before1()]; aspect name '']
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
org.springframework.aop.interceptor.ExposeInvocationInterceptor@5bcea91b
org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.springframework.aop.framework.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name ''
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@5f3a4b84
org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor@27f723
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@670b40af
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
around...before
before2
before1
target foo
afterReturning
around...after

小结💡

代理方法执行时会做如下工作

  1. 通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知
    • MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
    • AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor
    • 这体现的是适配器设计模式
  2. 所谓静态通知,体现在上面方法的 Interceptors 部分,这些通知调用时无需再次检查切点,直接调用即可
  3. 结合目标与环绕通知链,创建 MethodInvocation 对象,通过它完成整个调用

演示 - 模拟 MethodInvocation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/*
模拟调用链过程, 是一个简单的递归过程
1. proceed() 方法调用链中下一个环绕通知
2. 每个环绕通知内部继续调用 proceed()
3. 调用到没有更多通知了, 就调用目标方法
*/
public class Test {

static class Target {
public void foo() {
System.out.println("Target.foo()");
}
}

static class Advice1 implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Advice1.before()");
Object result = invocation.proceed();// 调用下一个通知或目标
System.out.println("Advice1.after()");
return result;
}
}

static class Advice2 implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Advice2.before()");
Object result = invocation.proceed();// 调用下一个通知或目标
System.out.println("Advice2.after()");
return result;
}
}


static class CustomInvocation implements MethodInvocation {
private Object target; // 1
private Method method;
private Object[] args;
List<MethodInterceptor> methodInterceptorList; // 2
private int count = 1; // 调用次数

public CustomInvocation(Object target, Method method, Object[] args, List<MethodInterceptor> methodInterceptorList) {
this.target = target;
this.method = method;
this.args = args;
this.methodInterceptorList = methodInterceptorList;
}

@Override
public Method getMethod() {
return method;
}

@Override
public Object[] getArguments() {
return args;
}

@Override
public Object proceed() throws Throwable { // 调用每一个环绕通知, 调用目标
if (count > methodInterceptorList.size()) {
// 调用目标, 返回并结束递归
return method.invoke(target, args);
}
// 逐一调用通知, count + 1
MethodInterceptor methodInterceptor = methodInterceptorList.get(count++ - 1);
return methodInterceptor.invoke(this);
}

@Override
public Object getThis() {
return target;
}

@Override
public AccessibleObject getStaticPart() {
return method;
}
}

public static void main(String[] args) throws Throwable {
Target target = new Target();
List<MethodInterceptor> list = Arrays.asList(
new Advice1(),
new Advice2()
);
CustomInvocation invocation = new CustomInvocation(target, Target.class.getMethod("foo"), new Object[0], list);
invocation.proceed();
}
}

小结💡

  1. proceed() 方法调用链中下一个环绕通知
  2. 每个环绕通知内部继续调用 proceed()
  3. 调用到没有更多通知了, 就调用目标方法

MethodInvocation 的编程技巧在实现拦截器、过滤器时能用上

动态通知调用

演示 - 带参数绑定的通知方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package org.springframework.aop.framework.autoproxy;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

import java.lang.reflect.Field;
import java.util.List;

public class Test {

@Aspect
static class CustomAspect {
@Before("execution(* foo(..))") // 静态通知调用,不带参数绑定,执行时不需要切点
public void before1() {
System.out.println("before1");
}

@Before("execution(* foo(..)) && args(x)") // 动态通知调用,需要参数绑定,执行时还需要切点对象
public void before2(int x) {
System.out.printf("before2(%d)%n", x);
}
}

static class Target {
public void foo(int x) {
System.out.printf("target foo(%d)%n", x);
}
}

@Configuration
static class CustomConfig {
@Bean
AnnotationAwareAspectJAutoProxyCreator proxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}

@Bean
public CustomAspect myAspect() {
return new CustomAspect();
}
}

public static void main(String[] args) throws Throwable {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(CustomConfig.class);
context.refresh();

AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> list = creator.findEligibleAdvisors(Target.class, "target");

Target target = new Target();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisors(list);
Object proxy = factory.getProxy(); // 获取代理

List<Object> interceptorList = factory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo", int.class), Target.class);
for (Object o : interceptorList) {
showDetail(o);
}

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
ReflectiveMethodInvocation invocation = new ReflectiveMethodInvocation(
proxy, target, Target.class.getMethod("foo", int.class), new Object[]{100}, Target.class, interceptorList
) {};

invocation.proceed();

/*
a. 有参数绑定的通知调用时还需要切点,对参数进行匹配及绑定
b. 复杂程度高, 性能比无参数绑定的通知调用低
*/
}

public static void showDetail(Object o) {
try {
Class<?> clazz = Class.forName("org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher");
if (clazz.isInstance(o)) {
Field methodMatcher = clazz.getDeclaredField("methodMatcher");
methodMatcher.setAccessible(true);
Field methodInterceptor = clazz.getDeclaredField("interceptor");
methodInterceptor.setAccessible(true);
System.out.println("环绕通知和切点:" + o);
System.out.println("\t切点为:" + methodMatcher.get(o));
System.out.println("\t通知为:" + methodInterceptor.get(o));
} else {
System.out.println("普通环绕通知:" + o);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

小结💡

  1. 通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知
  2. 所谓动态通知,体现在上面方法的 DynamicInterceptionAdvice 部分,这些通知调用时因为要为通知方法绑定参数,还需再次利用切点表达式
  3. 动态通知调用复杂程度高,性能较低

WEB

RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter

RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter 俩是一对,分别用来

  • 处理 @RequestMapping 映射
  • 调用控制器方法、并处理方法参数与方法返回值

DispatcherServlet 初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Configuration
@ComponentScan
@PropertySource("classpath:application.properties")
@EnableConfigurationProperties({WebMvcProperties.class, ServerProperties.class})
public class WebConfig {
// ⬅️内嵌 web 容器工厂
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory(ServerProperties serverProperties) {
return new TomcatServletWebServerFactory(serverProperties.getPort());
}

// ⬅️创建 DispatcherServlet
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}

// ⬅️注册 DispatcherServlet, Spring MVC 的入口
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(
DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties) {
DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
// 该方法可以在设置Tomcat在启动的时候初始化DispatcherServlet,而不是在第一次请求的时候初始化,默认是-1不是在启动时初始化
// 该方法的参数是一个大于零的数字,数字越小的优先级越高,越大优先级越低
registrationBean.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
return registrationBean;
}
}

启动类

1
2
3
4
5
6
7
8
public class WebTest {
private static final Logger log = LoggerFactory.getLogger(WebTest.class);

public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
}
}

通过Debug启动断点在DispatcherServlet的onRefresh方法

默认的初始化org.springframework.web.servlet.DispatcherServlet#onRefresh

1
2
3
4
5
6
7
8
9
10
11
12
// 调用下面九种初始化方式
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); // 表单上传的初始化
initLocaleResolver(context); // 国际化语言的解析,多种实现,如请求头Accept-Language,Cookie等等
initThemeResolver(context);
initHandlerMappings(context); // 路径映射器
initHandlerAdapters(context); // 控制器方法,如参数解析、返回值处理
initHandlerExceptionResolvers(context); // 解析异常
initRequestToViewNameTranslator(context);
initViewResolvers(context); // 视图解析
initFlashMapManager(context);
}

下面讲解其中的一个初始化,其他的大同小异org.springframework.web.servlet.DispatcherServlet#initHandlerMappings

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// 是否检测所有的路径映射器,所有指的是会在Spring中的父子容器中寻找所有HandlerMapping
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
// 如果容器中有,就用容器里面的
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
// 如果容器中有,就用容器里面的
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}

// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
// 如果没有的话,就在类路径下读取一个叫做DispatcherServlet.properties的文件
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}

for (HandlerMapping mapping : this.handlerMappings) {
if (mapping.usesPathPatterns()) {
this.parseRequestPath = true;
break;
}
}
}

RequestMappingHandlerMapping

1
2
3
4
5
6
7
// 在WebConfig添加一个Bean
// 如果用 DispatcherServlet 初始化时默认添加的组件, 并不会作为 bean, 而是一个成员变量,给测试带来困扰
// ⬅️1. 加入RequestMappingHandlerMapping
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class WebTest {
private static final Logger log = LoggerFactory.getLogger(WebTest.class);

public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);

// 作用 解析 @RequestMapping 以及派生注解,生成路径与控制器方法的映射关系, 在初始化时就生成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);

// 获取映射结果
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
handlerMethods.forEach((k, v) -> System.out.println(k + "=" + v));

// 请求来了,获取控制器方法 返回处理器执行链对象
HandlerExecutionChain chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/test1"));
System.out.println(chain);
}
}

RequestMappingHandlerAdapter

1
2
3
4
5
6
// ⬅️2. 继续加入RequestMappingHandlerAdapter, 会替换掉 DispatcherServlet 默认的 4 个 HandlerAdapter
// RequestMappingHandlerAdapter的invokeHandlerMethod方法是受保护的,我们定义一个它的子类,调高访问修饰权限,子类的方法直接调用父类的方法,或者可以把WebTest这个类定义在和RequestMappingHandlerAdapter同包下
@Bean
public CustomRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
return new CustomRequestMappingHandlerAdapter();
}

控制层代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@Controller
public class Controller1 {

private static final Logger log = LoggerFactory.getLogger(Controller1.class);

@GetMapping("/test1")
public ModelAndView test1() throws Exception {
log.info("test1()");
return null;
}

@PostMapping("/test2")
public ModelAndView test2(@RequestParam("name") String name) {
log.info("test2({})", name);
return null;
}

@PutMapping("/test3")
public ModelAndView test3(@Token String token) {
log.info("test3({})", token);
return null;
}

@RequestMapping("/test4")
// @ResponseBody
@Yml
public User test4() {
log.info("test4");
return new User("张三", 18);
}

public static class User {
private String name;
private int age;

public User(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}

public void setName(String name) {
this.name = name;
}

public void setAge(int age) {
this.age = age;
}
}

public static void main(String[] args) {
String str = new Yaml().dump(new User("张三", 18));
System.out.println(str);
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class WebTest {
private static final Logger log = LoggerFactory.getLogger(WebTest.class);

public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);

// 作用 解析 @RequestMapping 以及派生注解,生成路径与控制器方法的映射关系, 在初始化时就生成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);

// 获取映射结果
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
handlerMethods.forEach((k, v) -> System.out.println(k + "=" + v));

// 请求来了,获取控制器方法 返回处理器执行链对象
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test2");
request.setParameter("name", "张三");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain chain = handlerMapping.getHandler(request);
System.out.println(chain);

System.out.println(">>>>>>>>>>>>>>>>>>>>>");
// HandlerAdapter 作用: 调用控制器方法
CustomRequestMappingHandlerAdapter handlerAdapter = context.getBean(CustomRequestMappingHandlerAdapter.class);
handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());
}
}

参数和返回解析器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class WebTest {
private static final Logger log = LoggerFactory.getLogger(WebTest.class);

public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);

// 作用 解析 @RequestMapping 以及派生注解,生成路径与控制器方法的映射关系, 在初始化时就生成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);

// 获取映射结果
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
handlerMethods.forEach((k, v) -> System.out.println(k + "=" + v));

// 请求来了,获取控制器方法 返回处理器执行链对象
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test2");
request.setParameter("name", "张三");
// request.addHeader("token", "某个令牌");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain chain = handlerMapping.getHandler(request);
System.out.println(chain);

System.out.println(">>>>>>>>>>>>>>>>>>>>>");
// HandlerAdapter 作用: 调用控制器方法
CustomRequestMappingHandlerAdapter handlerAdapter = context.getBean(CustomRequestMappingHandlerAdapter.class);
handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());

System.out.println(">>>>>>>>>>>>>>>>>>>>> 所有参数解析器");
for (HandlerMethodArgumentResolver resolver : handlerAdapter.getArgumentResolvers()) {
System.out.println(resolver);
}

System.out.println(">>>>>>>>>>>>>>>>>>>>> 所有返回值解析器");
for (HandlerMethodReturnValueHandler handler : handlerAdapter.getReturnValueHandlers()) {
System.out.println(handler);
}
}
}

打印结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
>>>>>>>>>>>>>>>>>>>>> 所有参数解析器
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver
org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver
org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver
org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver
org.springframework.web.method.annotation.ModelMethodProcessor
org.springframework.web.method.annotation.MapMethodProcessor
org.springframework.web.method.annotation.ErrorsMethodArgumentResolver
org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.PrincipalMethodArgumentResolver
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
>>>>>>>>>>>>>>>>>>>>> 所有返回值解析器
org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler
org.springframework.web.method.annotation.ModelMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler
org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler
org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler
org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler
org.springframework.web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler
org.springframework.web.method.annotation.MapMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor

小结💡

  1. DispatcherServlet 是在第一次被访问时执行初始化, 也可以通过配置修改为 Tomcat 启动后就初始化
  2. 在初始化时会从 Spring 容器中找一些 Web 需要的组件, 如 HandlerMapping、HandlerAdapter 等,并逐一调用它们的初始化
  3. RequestMappingHandlerMapping 初始化时,会收集所有 @RequestMapping 映射信息,封装为 Map,其中
    • key 是 RequestMappingInfo 类型,包括请求路径、请求方法等信息
    • value 是 HandlerMethod 类型,包括控制器方法对象、控制器对象
    • 有了这个 Map,就可以在请求到达时,快速完成映射,找到 HandlerMethod 并与匹配的拦截器一起返回给 DispatcherServlet
  4. RequestMappingHandlerAdapter 初始化时,会准备 HandlerMethod 调用时需要的各个组件,如:
    • HandlerMethodArgumentResolver 解析控制器方法参数
    • HandlerMethodReturnValueHandler 处理控制器方法返回值

自定义参数解析器与返回值处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
// 参数解析
public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
@Override
// 是否支持某个参数
public boolean supportsParameter(MethodParameter parameter) {
Token token = parameter.getParameterAnnotation(Token.class);
return token != null;
}

@Override
// 解析参数
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return webRequest.getHeader("token");
}
}

// 例如经常需要用到请求头中的 token 信息, 用下面注解来标注由哪个参数来获取它
// token=令牌
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Token {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.yaml.snakeyaml.Yaml;

import javax.servlet.http.HttpServletResponse;
// 自定义返回值为Yml格式返回
public class YmlReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Yml yml = returnType.getMethodAnnotation(Yml.class);
return yml != null;
}

@Override // 返回值
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 1. 转换返回结果为 yaml 字符串
String str = new Yaml().dump(returnValue);

// 2. 将 yaml 字符串写入响应
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
response.setContentType("text/plain;charset=utf-8");
response.getWriter().print(str);

// 3. 设置请求已经处理完毕
mavContainer.setRequestHandled(true);
}
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Yml {
}

将添加的自定义参数解析器与返回值处理器加入HandlerAdapter

1
2
3
4
5
6
7
8
9
10
// ⬅️2. 继续加入RequestMappingHandlerAdapter, 会替换掉 DispatcherServlet 默认的 4 个 HandlerAdapter
@Bean
public CustomRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver();
YmlReturnValueHandler ymlReturnValueHandler = new YmlReturnValueHandler();
CustomRequestMappingHandlerAdapter handlerAdapter = new CustomRequestMappingHandlerAdapter();
handlerAdapter.setCustomArgumentResolvers(Collections.singletonList(tokenArgumentResolver));
handlerAdapter.setCustomReturnValueHandlers(Collections.singletonList(ymlReturnValueHandler));
return handlerAdapter;
}

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class WebTest {
private static final Logger log = LoggerFactory.getLogger(WebTest.class);

public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);

// 作用 解析 @RequestMapping 以及派生注解,生成路径与控制器方法的映射关系, 在初始化时就生成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);

// 获取映射结果
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
handlerMethods.forEach((k, v) -> System.out.println(k + "=" + v));

// 请求来了,获取控制器方法 返回处理器执行链对象
MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/test3");
request.setParameter("name", "张三");
request.addHeader("token", "某个令牌");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain chain = handlerMapping.getHandler(request);
System.out.println(chain);

System.out.println(">>>>>>>>>>>>>>>>>>>>>");
// HandlerAdapter 作用: 调用控制器方法
CustomRequestMappingHandlerAdapter handlerAdapter = context.getBean(CustomRequestMappingHandlerAdapter.class);
handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());
// 检查响应
byte[] content = response.getContentAsByteArray();
System.out.println(new String(content, StandardCharsets.UTF_8));
}
}

参数解析器

常见参数解析器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockPart;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;

/*
目标: 解析控制器方法的参数值

常见的参数处理器如下:
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver
org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver
org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver
org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver
org.springframework.web.method.annotation.ModelMethodProcessor
org.springframework.web.method.annotation.MapMethodProcessor
org.springframework.web.method.annotation.ErrorsMethodArgumentResolver
org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.PrincipalMethodArgumentResolver
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
*/
public class ArgumentParsingTest {

public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
// 准备测试 Request
HttpServletRequest request = mockRequest();

// 要点1. 控制器方法被封装为 HandlerMethod
HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));

// 要点2. 准备对象绑定与类型转换
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);

// 要点3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
ModelAndViewContainer container = new ModelAndViewContainer();

// 多个解析器组合
HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
composite.addResolvers(
// 解析器具体的解析类型可以查看实现类的supportsParameter方法
// 对于拥有useDefaultResolution参数的解析器,位置很重要
// false 表示必须有 @RequestParam
// beanFactory 可以解析#{},${}
new RequestParamMethodArgumentResolver(beanFactory, false),
new PathVariableMethodArgumentResolver(), // 将路径变量进行匹配,封装成map集合,放到Request作用域中,进行获取
new RequestHeaderMethodArgumentResolver(beanFactory),
new ServletCookieValueMethodArgumentResolver(beanFactory),
new ExpressionValueMethodArgumentResolver(beanFactory), // 从Spring容器中获取,${}从配置文件,键值对获取,#{}通过SpringEL表达式解析
new ServletRequestMethodArgumentResolver(), // 根据类型进行解析,如HttpServletRequest,HttpServletResponse等
new ServletModelAttributeMethodProcessor(false), // 必须有 @ModelAttribute
new RequestResponseBodyMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter())),
new ServletModelAttributeMethodProcessor(true), // 省略了 @ModelAttribute
new RequestParamMethodArgumentResolver(beanFactory, true) // 省略 @RequestParam
);

// 要点4. 解析每个参数值
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
// 获取参数上的注解
String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(Collectors.joining());
String str = annotations.length() > 0 ? " @" + annotations + " " : " ";
// 设置参数名解析器
parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());

if (composite.supportsParameter(parameter)) {
// 支持此参数
Object returnValue = composite.resolveArgument(parameter, container, new ServletWebRequest(request), factory);
// System.out.println(returnValue.getClass());
System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() + " -----> " + returnValue);
if (container.getModel().size() > 0) {
System.out.println("模型数据为:" + container.getModel());
}
} else {
System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName());
}
}
}

private static HttpServletRequest mockRequest() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("name1", "zhangsan");
request.setParameter("name2", "lisi");
request.addPart(new MockPart("file", "abc", "hello".getBytes(StandardCharsets.UTF_8)));
Map<String, String> map = new AntPathMatcher().extractUriTemplateVariables("/test/{id}", "/test/123");
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, map);
request.setContentType("application/json");
request.setCookies(new Cookie("token", "123456"));
request.setParameter("name", "张三");
request.setParameter("age", "18");
request.setContent("{\"name\": \"李四\", \"age\": 20 }".getBytes(StandardCharsets.UTF_8));

return new StandardServletMultipartResolver().resolveMultipart(request);
}

static class Controller {
public void test(
@RequestParam("name1") String name1, // name1=张三
String name2, // name2=李四
@RequestParam("age") int age, // age=18
@RequestParam(name = "home", defaultValue = "${JAVA_HOME}") String home1, // spring 获取数据
@RequestParam("file") MultipartFile file, // 上传文件
@PathVariable("id") int id, // /test/124 /test/{id}
@RequestHeader("Content-Type") String header,
@CookieValue("token") String token,
@Value("${JAVA_HOME}") String home2, // spring 获取数据 ${} #{}
HttpServletRequest request, // request, response, session ...
@ModelAttribute("abc") User user1, // name=zhang&age=18
User user2, // name=zhang&age=18
@RequestBody User user3 // json
) {
}
}

static class User {
private String name;
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

@Configuration
static class WebConfig {
}
}

小结💡

  1. 初步了解 RequestMappingHandlerAdapter 的调用过程
    1. 控制器方法被封装为 HandlerMethod
    2. 准备对象绑定与类型转换
    3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
    4. 解析每个参数值
  2. 解析参数依赖的就是各种参数解析器,它们都有两个重要方法
    • supportsParameter 判断是否支持方法参数
    • resolveArgument 解析方法参数
  3. 常见参数的解析
    • @RequestParam RequestParamMethodArgumentResolver
    • 省略 @RequestParam RequestParamMethodArgumentResolver
    • @RequestParam(defaultValue) RequestParamMethodArgumentResolver
    • MultipartFile RequestParamMethodArgumentResolver
    • @PathVariable PathVariableMethodArgumentResolver
    • @RequestHeader RequestHeaderMethodArgumentResolver
    • @CookieValue ServletCookieValueMethodArgumentResolver
    • @Value ExpressionValueMethodArgumentResolver
    • HttpServletRequest 等 ServletRequestMethodArgumentResolver
    • @ModelAttribute ServletModelAttributeMethodProcessor
    • 省略 @ModelAttribute ServletModelAttributeMethodProcessor
    • @RequestBody RequestResponseBodyMethodProcessor
  4. 组合模式在 Spring 中的体现
  5. @RequestParam, @CookieValue 等注解中的参数名、默认值, 都可以写成活的, 即从 ${ } #{ }中获取

参数名解析

两种方法获取参数名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ObtainMethodParameterNamesTest {

public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException {
// 1. 反射获取参数名
Method foo = Bean2.class.getMethod("foo", String.class, int.class);
for (Parameter parameter : foo.getParameters()) {
System.out.println(parameter.getName());
}

// 2. 基于 LocalVariableTable 本地变量表
LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(foo);
System.out.println(Arrays.toString(parameterNames));
}

}

小结💡

  1. 如果编译时添加了 -parameters 可以生成参数表, 反射时就可以拿到参数名
  2. 如果编译时添加了 -g 可以生成本地变量表调试信息, 但分为两种情况
    • 普通类, 会包含局部变量表, 用 asm 可以拿到参数名
    • 接口, 不会包含局部变量表, 无法获得参数名
      • 这也是 MyBatis 在实现 Mapper 接口时为何要提供 @Param 注解来辅助获得参数名

对象绑定与类型转换

底层第一套转换接口与实现

classDiagram

Formatter --|> Printer
Formatter --|> Parser

class Converters {
   Set~GenericConverter~
}
class Converter

class ConversionService
class FormattingConversionService

ConversionService <|-- FormattingConversionService
FormattingConversionService o-- Converters

Printer --> Adapter1
Adapter1 --> Converters
Parser --> Adapter2
Adapter2 --> Converters
Converter --> Adapter3
Adapter3 --> Converters

<<interface>> Formatter
<<interface>> Printer
<<interface>> Parser
<<interface>> Converter
<<interface>> ConversionService
  • Printer 把其它类型转为 String
  • Parser 把 String 转为其它类型
  • Formatter 综合 Printer 与 Parser 功能
  • Converter 把类型 S 转为类型 T
  • Printer、Parser、Converter 经过适配转换成 GenericConverter 放入 Converters 集合
  • FormattingConversionService 利用其它们实现转换

底层第二套转换接口

classDiagram

PropertyEditorRegistry o-- "多" PropertyEditor

<<interface>> PropertyEditorRegistry
<<interface>> PropertyEditor
  • PropertyEditor 把 String 与其它类型相互转换
  • PropertyEditorRegistry 可以注册多个 PropertyEditor 对象
  • 与第一套接口直接可以通过 FormatterPropertyEditorAdapter 来进行适配

高层接口与实现

classDiagram
TypeConverter <|-- SimpleTypeConverter
TypeConverter <|-- BeanWrapperImpl
TypeConverter <|-- DirectFieldAccessor
TypeConverter <|-- ServletRequestDataBinder

SimpleTypeConverter --> TypeConverterDelegate
BeanWrapperImpl --> TypeConverterDelegate
DirectFieldAccessor --> TypeConverterDelegate
ServletRequestDataBinder --> TypeConverterDelegate

TypeConverterDelegate --> ConversionService
TypeConverterDelegate --> PropertyEditorRegistry

<<interface>> TypeConverter
<<interface>> ConversionService
<<interface>> PropertyEditorRegistry
  • 它们都实现了 TypeConverter 这个高层转换接口,在转换时,会用到 TypeConverterDelegate 委派ConversionService 与 PropertyEditorRegistry 真正执行转换(Facade 门面模式)
    • 首先看是否有自定义转换器, @InitBinder 添加的即属于这种 (用了适配器模式把 Formatter 转为需要的 PropertyEditor)
    • 再看有没有 ConversionService 转换
    • 再利用默认的 PropertyEditor 转换
    • 最后有一些特殊处理
  • SimpleTypeConverter 仅做类型转换
  • BeanWrapperImpl 为 bean 的属性赋值,当需要时做类型转换,走 Property
  • DirectFieldAccessor 为 bean 的属性赋值,当需要时做类型转换,走 Field
  • ServletRequestDataBinder 为 bean 的属性执行绑定,当需要时做类型转换,根据 directFieldAccess 选择走 Property 还是 Field,具备校验与获取校验结果功能

演示 - 类型转换与数据绑定

SimpleTypeConverter

1
2
3
4
5
6
7
8
9
10
public class TestSimpleConverter {
public static void main(String[] args) {
// 仅有类型转换的功能
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
Integer number = typeConverter.convertIfNecessary("13", int.class);
Date date = typeConverter.convertIfNecessary("1999/03/04", Date.class);
System.out.println(number);
System.out.println(date);
}
}

BeanWrapperImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class TestBeanWrapper {
public static void main(String[] args) {
// 利用反射原理, 为 bean 的属性赋值
Bean target = new Bean();
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
wrapper.setPropertyValue("a", "10");
wrapper.setPropertyValue("b", "hello");
wrapper.setPropertyValue("c", "1999/03/04");
System.out.println(target);
}

static class Bean {
private int a;
private String b;
private Date c;

public int getA() {
return a;
}

public void setA(int a) {
this.a = a;
}

public String getB() {
return b;
}

public void setB(String b) {
this.b = b;
}

public Date getC() {
return c;
}

public void setC(Date c) {
this.c = c;
}

@Override
public String toString() {
return "Bean{" +
"a=" + a +
", b='" + b + '\'' +
", c=" + c +
'}';
}
}
}

DirectFieldAccessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class TestFieldAccessor {
public static void main(String[] args) {
// 利用反射原理, 为 bean 的属性赋值
Bean target = new Bean();
DirectFieldAccessor accessor = new DirectFieldAccessor(target);
accessor.setPropertyValue("a", "10");
accessor.setPropertyValue("b", "hello");
accessor.setPropertyValue("c", "1999/03/04");
System.out.println(target);
}

static class Bean {
private int a;
private String b;
private Date c;
@Override
public String toString() {
return "Bean{" +
"a=" + a +
", b='" + b + '\'' +
", c=" + c +
'}';
}
}
}

DataBinder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class TestDataBinder {

public static void main(String[] args) {
// 执行数据绑定
Bean target = new Bean();
DataBinder dataBinder = new DataBinder(target);
// 设置directFieldAccess属性为true,走的是属性赋值,默认false走的是set方法赋值
dataBinder.initDirectFieldAccess();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("a", "10");
pvs.add("b", "hello");
pvs.add("c", "1999/03/04");
dataBinder.bind(pvs);
System.out.println(target);
}

static class Bean {
private int a;
private String b;
private Date c;

@Override
public String toString() {
return "Bean{" +
"a=" + a +
", b='" + b + '\'' +
", c=" + c +
'}';
}
}
}

ServletRequestDataBinder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class TestServletDataBinder {

public static void main(String[] args) {
// web 环境下数据绑定
Bean target = new Bean();
ServletRequestDataBinder dataBinder = new ServletRequestDataBinder(target);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("a", "10");
request.setParameter("b", "hello");
request.setParameter("c", "1999/03/04");

dataBinder.bind(new ServletRequestParameterPropertyValues(request));

System.out.println(target);
}

static class Bean {
private int a;
private String b;
private Date c;

public int getA() {
return a;
}

public void setA(int a) {
this.a = a;
}

public String getB() {
return b;
}

public void setB(String b) {
this.b = b;
}

public Date getC() {
return c;
}

public void setC(Date c) {
this.c = c;
}

@Override
public String toString() {
return "Bean{" +
"a=" + a +
", b='" + b + '\'' +
", c=" + c +
'}';
}
}
}

小结💡

基本的类型转换与数据绑定用法

  • SimpleTypeConverter
  • BeanWrapperImpl
  • DirectFieldAccessor
  • ServletRequestDataBinder

演示 - 数据绑定工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
public class TestServletDataBinderFactory {
public static void main(String[] args) throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("birthday", "1999|01|02");
request.setParameter("address.name", "西安");

User target = new User();
// "1. 用工厂, 无转换功能"
// ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
// "2. 用 @InitBinder 转换" PropertyEditorRegistry PropertyEditor
// InvocableHandlerMethod method = new InvocableHandlerMethod(new CustomController(), CustomController.class.getMethod("extension", WebDataBinder.class));
// ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(Collections.singletonList(method), null);
// "3. 用 ConversionService 转换" ConversionService Formatter
// FormattingConversionService service = new FormattingConversionService();
// service.addFormatter(new CustomDateFormatter("用 ConversionService 方式扩展转换功能"));
// ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
// initializer.setConversionService(service);
// ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, initializer);
// "4. 同时加了 @InitBinder 和 ConversionService"
// InvocableHandlerMethod method = new InvocableHandlerMethod(new CustomController(), CustomController.class.getMethod("extension", WebDataBinder.class));

// FormattingConversionService service = new FormattingConversionService();
// service.addFormatter(new CustomDateFormatter("用 ConversionService 方式扩展转换功能"));
// ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
// initializer.setConversionService(service);

// ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(Collections.singletonList(method), initializer);
// "5. 使用默认 ConversionService 转换"
ApplicationConversionService service = new ApplicationConversionService();
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
initializer.setConversionService(service);

ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, initializer);

WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
dataBinder.bind(new ServletRequestParameterPropertyValues(request));
System.out.println(target);
}

static class CustomController {
@InitBinder
public void extension(WebDataBinder dataBinder) {
// 扩展 dataBinder 的转换器
dataBinder.addCustomFormatter(new CustomDateFormatter("用 @InitBinder 方式扩展的"));
}
}

public static class User {
@DateTimeFormat(pattern = "yyyy|MM|dd")
private Date birthday;
private Address address;

public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}

public Date getBirthday() {
return birthday;
}

public void setBirthday(Date birthday) {
this.birthday = birthday;
}

@Override
public String toString() {
return "User{" +
"birthday=" + birthday +
", address=" + address +
'}';
}
}

public static class Address {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "Address{" +
"name='" + name + '\'' +
'}';
}
}

public static class CustomDateFormatter implements Formatter<Date> {
private static final Logger log = LoggerFactory.getLogger(CustomDateFormatter.class);
private final String desc;

public CustomDateFormatter(String desc) {
this.desc = desc;
}

@Override
public String print(Date date, Locale locale) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
return sdf.format(date);
}

@Override
public Date parse(String text, Locale locale) throws ParseException {
log.info(">>>>>> 进入了: {}", desc);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
return sdf.parse(text);
}
}
}

小结💡

ServletRequestDataBinderFactory 的用法和扩展点

  1. 可以解析控制器的 @InitBinder 标注方法作为扩展点,添加自定义转换器
    • 控制器私有范围
  2. 可以通过 ConfigurableWebBindingInitializer 配置 ConversionService 作为扩展点,添加自定义转换器
    • 公共范围
  3. 同时加了 @InitBinder 和 ConversionService 的转换优先级
    1. 优先采用 @InitBinder 的转换器
    2. 其次使用 ConversionService 的转换器
    3. 使用默认转换器
    4. 特殊处理(例如有参构造)

演示 - 获取泛型参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.xiao.learning.chapter23.sub;

import org.springframework.core.GenericTypeResolver;
import org.springframework.core.ResolvableType;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class TestGenericType {
public static void main(String[] args) {
// 小技巧
// 1. java api
System.out.println(">>>>>>>>>>>JDK Api>>>>>>>>>>>>");
Type type = StudentDao.class.getGenericSuperclass();
System.out.println(type);

// 判断Type类型是否带有泛型信息,如EmployeeDao就没有泛型信息
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
System.out.println(parameterizedType.getActualTypeArguments()[0]);
}

// 2. spring api 1
System.out.println(">>>>>>>>>>>Spring Api 1>>>>>>>>>>>>");
// resolveTypeArgument方法只获取只有一个参数的
// 多个泛型参数的可以用resolveTypeArguments
Class<?> t = GenericTypeResolver.resolveTypeArgument(TeacherDao.class, BaseDao.class);
System.out.println(t);

// 3. spring api 2
System.out.println(">>>>>>>>>>>Spring Api 2>>>>>>>>>>>>");
System.out.println(ResolvableType.forClass(TeacherDao.class).getSuperType().getGeneric().resolve());
}

static class BaseDao<T> {
T findOne() {
return null;
}
}

static class EmployeeDao extends BaseDao {
}

static class Student {
}

static class StudentDao extends BaseDao<Student> {
}

static class Teacher {
}

static class TeacherDao extends BaseDao<Teacher> {
}
}

@ControllerAdvice 之 @InitBinder

演示 - 准备 @InitBinder

准备 @InitBinder 在整个 HandlerAdapter 调用过程中所处的位置

sequenceDiagram
participant adapter as HandlerAdapter
participant bf as WebDataBinderFactory
participant mf as ModelFactory
participant ihm as ServletInvocableHandlerMethod
participant ar as ArgumentResolvers 
participant rh as ReturnValueHandlers
participant container as ModelAndViewContainer
rect rgb(200, 150, 255)
adapter ->> +bf: 准备 @InitBinder
bf -->> -adapter: 
end
adapter ->> +mf: 准备 @ModelAttribute
mf ->> +container: 添加Model数据
container -->> -mf: 
mf -->> -adapter: 

adapter ->> +ihm: invokeAndHandle
ihm ->> +ar: 获取 args
ar ->> ar: 有的解析器涉及 RequestBodyAdvice
ar ->> container: 有的解析器涉及数据绑定生成Model数据
ar -->> -ihm: args
ihm ->> ihm: method.invoke(bean,args) 得到 returnValue
ihm ->> +rh: 处理 returnValue
rh ->> rh: 有的处理器涉及 ResponseBodyAdvice
rh ->> +container: 添加Model数据,处理视图名,是否渲染等
container -->> -rh: 
rh -->> -ihm: 
ihm -->> -adapter: 
adapter ->> +container: 获取 ModelAndView
container -->> -adapter: 
  • RequestMappingHandlerAdapter 在图中缩写为 HandlerAdapter
  • HandlerMethodArgumentResolverComposite 在图中缩写为 ArgumentResolvers
  • HandlerMethodReturnValueHandlerComposite 在图中缩写为 ReturnValueHandlers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/*
@InitBinder 的来源
*/
public class Test {

private static final Logger log = LoggerFactory.getLogger(Test.class);

public static void main(String[] args) throws Exception {
/*
@InitBinder 的来源有两个
1. @ControllerAdvice 中 @InitBinder 标注的方法,由 RequestMappingHandlerAdapter 在初始化时解析并记录
2. @Controller 中 @InitBinder 标注的方法,由 RequestMappingHandlerAdapter 会在控制器方法首次执行时解析并记录
*/

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);

RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
handlerAdapter.setApplicationContext(context);
handlerAdapter.afterPropertiesSet();

log.info("1. 刚开始...");
showBindMethods(handlerAdapter);

Method getDataBinderFactory = RequestMappingHandlerAdapter.class.getDeclaredMethod("getDataBinderFactory", HandlerMethod.class);
getDataBinderFactory.setAccessible(true);

log.info("2. 模拟调用 Controller1 的 foo 方法时 ...");
getDataBinderFactory.invoke(handlerAdapter, new HandlerMethod(new WebConfig.Controller1(), WebConfig.Controller1.class.getMethod("foo")));
showBindMethods(handlerAdapter);

log.info("3. 模拟调用 Controller2 的 bar 方法时 ...");
getDataBinderFactory.invoke(handlerAdapter, new HandlerMethod(new WebConfig.Controller2(), WebConfig.Controller2.class.getMethod("bar")));
showBindMethods(handlerAdapter);

context.close();
}

@SuppressWarnings("all")
private static void showBindMethods(RequestMappingHandlerAdapter handlerAdapter) throws NoSuchFieldException, IllegalAccessException {
Field initBinderAdviceCache = RequestMappingHandlerAdapter.class.getDeclaredField("initBinderAdviceCache");
initBinderAdviceCache.setAccessible(true);
Map<ControllerAdviceBean, Set<Method>> globalMap = (Map<ControllerAdviceBean, Set<Method>>) initBinderAdviceCache.get(handlerAdapter);
log.info("全局的 @InitBinder 方法 {}", globalMap.values().stream().flatMap(ms -> ms.stream().map(m -> m.getName())).collect(Collectors.toList()));

Field initBinderCache = RequestMappingHandlerAdapter.class.getDeclaredField("initBinderCache");
initBinderCache.setAccessible(true);
Map<Class<?>, Set<Method>> controllerMap = (Map<Class<?>, Set<Method>>) initBinderCache.get(handlerAdapter);
log.info("控制器的 @InitBinder 方法 {}", controllerMap.entrySet().stream().flatMap(e -> e.getValue().stream().map(v -> e.getKey().getSimpleName() + "." + v.getName())).collect(Collectors.toList()));
}

@Configuration
public static class WebConfig {

@ControllerAdvice
static class CustomControllerAdvice {
@InitBinder
public void binder3(WebDataBinder webDataBinder) {
webDataBinder.addCustomFormatter(new CustomDateFormatter("binder3 转换器"));
}
}

@Controller
static class Controller1 {
@InitBinder
public void binder1(WebDataBinder webDataBinder) {
webDataBinder.addCustomFormatter(new CustomDateFormatter("binder1 转换器"));
}

public void foo() {

}
}

@Controller
static class Controller2 {
@InitBinder
public void binder21(WebDataBinder webDataBinder) {
webDataBinder.addCustomFormatter(new CustomDateFormatter("binder21 转换器"));
}

@InitBinder
public void binder22(WebDataBinder webDataBinder) {
webDataBinder.addCustomFormatter(new CustomDateFormatter("binder22 转换器"));
}

public void bar() {

}
}
}

public static class CustomDateFormatter implements Formatter<Date> {
private static final Logger log = LoggerFactory.getLogger(CustomDateFormatter.class);
private final String desc;

public CustomDateFormatter(String desc) {
this.desc = desc;
}

@Override
public String print(Date date, Locale locale) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
return sdf.format(date);
}

@Override
public Date parse(String text, Locale locale) throws ParseException {
log.info(">>>>>> 进入了: {}", desc);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
return sdf.parse(text);
}
}
}

小结💡

  1. RequestMappingHandlerAdapter 初始化时会解析 @ControllerAdvice 中的 @InitBinder 方法
  2. RequestMappingHandlerAdapter 会以类为单位,在该类首次使用时,解析此类的 @InitBinder 方法
  3. 以上两种 @InitBinder 的解析结果都会缓存来避免重复解析
  4. 控制器方法调用时,会综合利用本类的 @InitBinder 方法和 @ControllerAdvice 中的 @InitBinder 方法创建绑定工厂

控制器方法执行流程

图1

classDiagram
class ServletInvocableHandlerMethod {
	+invokeAndHandle(ServletWebRequest,ModelAndViewContainer)
}
HandlerMethod <|-- ServletInvocableHandlerMethod
HandlerMethod o-- bean
HandlerMethod o-- method
ServletInvocableHandlerMethod o-- WebDataBinderFactory
ServletInvocableHandlerMethod o-- ParameterNameDiscoverer
ServletInvocableHandlerMethod o-- HandlerMethodArgumentResolverComposite
ServletInvocableHandlerMethod o-- HandlerMethodReturnValueHandlerComposite

HandlerMethod 需要

  • bean 即是哪个 Controller
  • method 即是 Controller 中的哪个方法

ServletInvocableHandlerMethod 需要

  • WebDataBinderFactory 负责对象绑定、类型转换
  • ParameterNameDiscoverer 负责参数名解析
  • HandlerMethodArgumentResolverComposite 负责解析参数
  • HandlerMethodReturnValueHandlerComposite 负责处理返回值

图2

sequenceDiagram
participant adapter as RequestMappingHandlerAdapter
participant bf as WebDataBinderFactory
participant mf as ModelFactory
participant container as ModelAndViewContainer
adapter ->> +bf: 准备 @InitBinder
bf -->> -adapter: 
adapter ->> +mf: 准备 @ModelAttribute
mf ->> +container: 添加Model数据
container -->> -mf: 
mf -->> -adapter: 

图3

sequenceDiagram
participant adapter as RequestMappingHandlerAdapter
participant ihm as ServletInvocableHandlerMethod
participant ar as ArgumentResolvers
participant rh as ReturnValueHandlers
participant container as ModelAndViewContainer

adapter ->> +ihm: invokeAndHandle
ihm ->> +ar: 获取 args
ar ->> ar: 有的解析器涉及 RequestBodyAdvice
ar ->> container: 有的解析器涉及数据绑定生成模型数据
container -->> ar: 
ar -->> -ihm: args
ihm ->> ihm: method.invoke(bean,args) 得到 returnValue
ihm ->> +rh: 处理 returnValue
rh ->> rh: 有的处理器涉及 ResponseBodyAdvice
rh ->> +container: 添加Model数据,处理视图名,是否渲染等
container -->> -rh: 
rh -->> -ihm: 
ihm -->> -adapter: 
adapter ->> +container: 获取 ModelAndView
container -->> -adapter: 

@ControllerAdvice 之 @ModelAttribute

准备 @ModelAttribute

WebConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@Configuration
public class WebConfig {

@ControllerAdvice
static class MyControllerAdvice {
@ModelAttribute("a")
public String aa() {
return "aa";
}
}

@Controller
static class Controller1 {
@ModelAttribute("b")
public String aa() {
return "bb";
}

@ResponseStatus(HttpStatus.OK)
public ModelAndView foo(@ModelAttribute("u") User user) {
System.out.println("foo");
return null;
}
}

static class User {
private String name;

public void setName(String name) {
this.name = name;
}

public String getName() {

return name;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}

}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class Test {

public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);

RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
adapter.setApplicationContext(context); // 设置上下文容器
adapter.afterPropertiesSet(); // 初始化

MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("name", "张三");
/*
现在可以通过 ServletInvocableHandlerMethod 把这些整合在一起, 并完成控制器方法的调用, 如下
*/
ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(new Controller1(), Controller1.class.getMethod("foo", User.class));

ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);

handlerMethod.setDataBinderFactory(factory);
handlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
handlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers(context));

ModelAndViewContainer container = new ModelAndViewContainer();

// 获取模型工厂方法
Method getModelFactory = RequestMappingHandlerAdapter.class.getDeclaredMethod("getModelFactory", HandlerMethod.class, WebDataBinderFactory.class);
getModelFactory.setAccessible(true);
ModelFactory modelFactory = (ModelFactory) getModelFactory.invoke(adapter, handlerMethod, factory);

// 初始化模型数据
modelFactory.initModel(new ServletWebRequest(request), container, handlerMethod);

handlerMethod.invokeAndHandle(new ServletWebRequest(request), container);

System.out.println(container.getModel());

context.close();

/*
a. 控制器方法是如何调用的
b. 模型数据如何产生
c. advice 之二, @ModelAttribute 补充模型数据
*/
}

public static HandlerMethodArgumentResolverComposite getArgumentResolvers(AnnotationConfigApplicationContext context) {
HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
composite.addResolvers(new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), false), new PathVariableMethodArgumentResolver(), new RequestHeaderMethodArgumentResolver(context.getDefaultListableBeanFactory()), new ServletCookieValueMethodArgumentResolver(context.getDefaultListableBeanFactory()), new ExpressionValueMethodArgumentResolver(context.getDefaultListableBeanFactory()), new ServletRequestMethodArgumentResolver(), new ServletModelAttributeMethodProcessor(false), new RequestResponseBodyMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter())), new ServletModelAttributeMethodProcessor(true), new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), true));
return composite;
}
}

准备 @ModelAttribute 在整个 HandlerAdapter 调用过程中所处的位置

sequenceDiagram
participant adapter as HandlerAdapter
participant bf as WebDataBinderFactory
participant mf as ModelFactory
participant ihm as ServletInvocableHandlerMethod
participant ar as ArgumentResolvers 
participant rh as ReturnValueHandlers
participant container as ModelAndViewContainer

adapter ->> +bf: 准备 @InitBinder
bf -->> -adapter: 
rect rgb(200, 150, 255)
adapter ->> +mf: 准备 @ModelAttribute
mf ->> +container: 添加Model数据
container -->> -mf: 
mf -->> -adapter: 
end
adapter ->> +ihm: invokeAndHandle
ihm ->> +ar: 获取 args
ar ->> ar: 有的解析器涉及 RequestBodyAdvice
ar ->> container: 有的解析器涉及数据绑定生成Model数据
ar -->> -ihm: args
ihm ->> ihm: method.invoke(bean,args) 得到 returnValue
ihm ->> +rh: 处理 returnValue
rh ->> rh: 有的处理器涉及 ResponseBodyAdvice
rh ->> +container: 添加Model数据,处理视图名,是否渲染等
container -->> -rh: 
rh -->> -ihm: 
ihm -->> -adapter: 
adapter ->> +container: 获取 ModelAndView
container -->> -adapter: 

小结💡

  1. RequestMappingHandlerAdapter 初始化时会解析 @ControllerAdvice 中的 @ModelAttribute 方法
  2. RequestMappingHandlerAdapter 会以类为单位,在该类首次使用时,解析此类的 @ModelAttribute 方法
  3. 以上两种 @ModelAttribute 的解析结果都会缓存来避免重复解析
  4. 控制器方法调用时,会综合利用本类的 @ModelAttribute 方法和 @ControllerAdvice 中的 @ModelAttribute 方法创建模型工厂

返回值处理器

常见返回值处理器

WebConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Configuration
public class WebConfig {

@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setDefaultEncoding("utf-8");
configurer.setTemplateLoaderPath("classpath:templates");
return configurer;
}

@Bean // FreeMarkerView 在借助 Spring 初始化时,会要求 web 环境才会走 setConfiguration, 这里想办法去掉了 web 环境的约束
public FreeMarkerViewResolver viewResolver(FreeMarkerConfigurer configurer) {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver() {
@Override
protected AbstractUrlBasedView instantiateView() {
FreeMarkerView view = new FreeMarkerView() {
@Override
protected boolean isContextRequired() {
return false;
}
};
view.setConfiguration(configurer.getConfiguration());
return view;
}
};
resolver.setContentType("text/html;charset=utf-8");
resolver.setPrefix("/");
resolver.setSuffix(".ftl");
resolver.setExposeSpringMacroHelpers(false);
return resolver;
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
/*
目标: 解析控制器方法的返回值
常见的返回值处理器
org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler@4c9e38
org.springframework.web.method.annotation.ModelMethodProcessor@5d1e09bc
org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler@4bdc8b5d
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler@3bcd426c
org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler@5f14a673
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@726a17c4
org.springframework.web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler@5dc3fcb7
org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler@c4c0b41
org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler@76911385
org.springframework.web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler@5467eea4
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@160396db
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@7a799159
org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler@40ab8a8
org.springframework.web.method.annotation.MapMethodProcessor@6ff37443
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@65cc8228
*/
public class Test {
private static final Logger log = LoggerFactory.getLogger(Test.class);

public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
// 1. 测试返回值类型为 ModelAndView
// test1(context);
// 2. 测试返回值类型为 String 时, 把它当做视图名
// test2(context);
// 3. 测试返回值添加了 @ModelAttribute 注解时, 此时需找到默认视图名
// test3(context);
// 4. 测试返回值不加 @ModelAttribute 注解且返回非简单类型时, 此时需找到默认视图名
// test4(context);
// 5. 测试返回值类型为 ResponseEntity 时, 此时不走视图流程
// test5(context);
// 6. 测试返回值类型为 HttpHeaders 时, 此时不走视图流程
// test6(context);
// 7. 测试返回值添加了 @ResponseBody 注解时, 此时不走视图流程
test7(context);

/*
a. 每个返回值处理器能干啥
1) 看是否支持某种返回值
2) 返回值或作为模型、或作为视图名、或作为响应体 ...
b. 组合模式在 Spring 中的体现 + 1
*/
}

private static void test7(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test7");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值

HandlerMethod methodHandle = new HandlerMethod(controller, method);

ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
ServletWebRequest webRequest = new ServletWebRequest(request, response);
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
if (!container.isRequestHandled()) {
renderView(context, container, webRequest); // 渲染视图
} else {
for (String name : response.getHeaderNames()) {
System.out.println(name + "=" + response.getHeader(name));
}
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}
}
}

private static void test6(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test6");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值

HandlerMethod methodHandle = new HandlerMethod(controller, method);

ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
ServletWebRequest webRequest = new ServletWebRequest(request, response);
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
if (!container.isRequestHandled()) {
renderView(context, container, webRequest); // 渲染视图
} else {
for (String name : response.getHeaderNames()) {
System.out.println(name + "=" + response.getHeader(name));
}
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}
}
}

private static void test5(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test5");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值

HandlerMethod methodHandle = new HandlerMethod(controller, method);

ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
ServletWebRequest webRequest = new ServletWebRequest(request, response);
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
if (!container.isRequestHandled()) {
renderView(context, container, webRequest); // 渲染视图
} else {
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}
}
}

private static void test4(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test4");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值

HandlerMethod methodHandle = new HandlerMethod(controller, method);

ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/test4");
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
ServletWebRequest webRequest = new ServletWebRequest(request, new MockHttpServletResponse());
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
renderView(context, container, webRequest); // 渲染视图
}
}

private static void test3(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test3");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值

HandlerMethod methodHandle = new HandlerMethod(controller, method);

ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/test3");
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
ServletWebRequest webRequest = new ServletWebRequest(request, new MockHttpServletResponse());
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
renderView(context, container, webRequest); // 渲染视图
}
}

private static void test2(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test2");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值

HandlerMethod methodHandle = new HandlerMethod(controller, method);

ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
ServletWebRequest webRequest = new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse());
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
renderView(context, container, webRequest); // 渲染视图
}
}

private static void test1(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test1");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值

HandlerMethod methodHandle = new HandlerMethod(controller, method);

ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
ServletWebRequest webRequest = new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse());
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println("模型:" + container.getModel());
System.out.println("视图名称:" + container.getViewName());
renderView(context, container, webRequest); // 渲染视图
}
}

public static HandlerMethodReturnValueHandlerComposite getReturnValueHandler() {
HandlerMethodReturnValueHandlerComposite composite = new HandlerMethodReturnValueHandlerComposite();
composite.addHandler(new ModelAndViewMethodReturnValueHandler());
composite.addHandler(new ViewNameMethodReturnValueHandler());
composite.addHandler(new ServletModelAttributeMethodProcessor(false));
composite.addHandler(new HttpEntityMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter())));
composite.addHandler(new HttpHeadersReturnValueHandler());
composite.addHandler(new RequestResponseBodyMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter())));
composite.addHandler(new ServletModelAttributeMethodProcessor(true));
return composite;
}

@SuppressWarnings("all")
private static void renderView(AnnotationConfigApplicationContext context, ModelAndViewContainer container, ServletWebRequest webRequest) throws Exception {
log.info(">>>>>> 渲染视图");
FreeMarkerViewResolver resolver = context.getBean(FreeMarkerViewResolver.class);
String viewName = container.getViewName() != null ? container.getViewName() : new DefaultRequestToViewNameTranslator().getViewName(webRequest.getRequest());
log.info("没有获取到视图名, 采用默认视图名: {}", viewName);
// 每次渲染时, 会产生新的视图对象, 它并非被 Spring 所管理, 但确实借助了 Spring 容器来执行初始化
View view = resolver.resolveViewName(viewName, Locale.getDefault());
view.render(container.getModel(), webRequest.getRequest(), webRequest.getResponse());
System.out.println(new String(((MockHttpServletResponse) webRequest.getResponse()).getContentAsByteArray(), StandardCharsets.UTF_8));
}

static class Controller {
private static final Logger log = LoggerFactory.getLogger(Controller.class);

public ModelAndView test1() {
log.info("test1()");
ModelAndView mav = new ModelAndView("view1");
mav.addObject("name", "张三");
return mav;
}

public String test2() {
log.info("test2()");
return "view2";
}

@ModelAttribute
// @RequestMapping("/test3")
public User test3() {
log.info("test3()");
return new User("李四", 20);
}

public User test4() {
log.info("test4()");
return new User("王五", 30);
}

public HttpEntity<User> test5() {
log.info("test5()");
return new HttpEntity<>(new User("赵六", 40));
}

public HttpHeaders test6() {
log.info("test6()");
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "text/html");
return headers;
}

@ResponseBody
public User test7() {
log.info("test7()");
return new User("钱七", 50);
}
}

// 必须用 public 修饰, 否则 freemarker 渲染其 name, age 属性时失败
public static class User {
private String name;
private int age;

public User(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "User{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
}

小结💡

  1. 常见的返回值处理器
    • ModelAndView,分别获取其模型和视图名,放入 ModelAndViewContainer
    • 返回值类型为 String 时,把它当做视图名,放入 ModelAndViewContainer
    • 返回值添加了 @ModelAttribute 注解时,将返回值作为模型,放入 ModelAndViewContainer
      • 此时需找到默认视图名
    • 返回值省略 @ModelAttribute 注解且返回非简单类型时,将返回值作为模型,放入 ModelAndViewContainer
      • 此时需找到默认视图名
    • 返回值类型为 ResponseEntity 时
      • 此时走 MessageConverter,并设置 ModelAndViewContainer.requestHandled 为 true
    • 返回值类型为 HttpHeaders 时
      • 会设置 ModelAndViewContainer.requestHandled 为 true
    • 返回值添加了 @ResponseBody 注解时
      • 此时走 MessageConverter,并设置 ModelAndViewContainer.requestHandled 为 true
  2. 组合模式在 Spring 中的体现 + 1

MessageConverter

MessageConverter的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public class Test {
public static void main(String[] args) throws IOException, NoSuchMethodException, HttpMediaTypeNotAcceptableException {
// test1();
// test2();
// test3();
test4();

/*
a. MessageConverter 的作用, @ResponseBody 是返回值处理器解析的, 但具体转换工作是 MessageConverter 做的
b. 如何选择 MediaType
- 首先看 @RequestMapping 上有没有指定
- 其次看 request 的 Accept 头有没有指定
- 最后按 MessageConverter 的顺序, 谁能谁先转换
*/

}

private static void test4() throws IOException, HttpMediaTypeNotAcceptableException, NoSuchMethodException {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
ServletWebRequest webRequest = new ServletWebRequest(request, response);

request.addHeader("Accept", "application/xml");
response.setContentType("application/json");

RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(
Arrays.asList(
new MappingJackson2HttpMessageConverter(), new MappingJackson2XmlHttpMessageConverter()
));
processor.handleReturnValue(
new User("张三", 18),
new MethodParameter(Test.class.getMethod("user"), -1),
new ModelAndViewContainer(),
webRequest
);
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}

@ResponseBody
@RequestMapping(produces = "application/json")
public User user() {
return null;
}

private static void test3() throws IOException {
MockHttpInputMessage message = new MockHttpInputMessage("{\"name\": \"李四\", \"age\": 20 }".getBytes(StandardCharsets.UTF_8));
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
if (converter.canRead(User.class, MediaType.APPLICATION_JSON)) {
Object read = converter.read(User.class, message);
System.out.println(read);
}
}

private static void test2() throws IOException {
MockHttpOutputMessage message = new MockHttpOutputMessage();
MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter();
if (converter.canWrite(User.class, MediaType.APPLICATION_XML)) {
converter.write(new User("李四", 20), MediaType.APPLICATION_XML, message);
System.out.println(message.getBodyAsString());
}
}

public static void test1() throws IOException {
MockHttpOutputMessage message = new MockHttpOutputMessage();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
if (converter.canWrite(User.class, MediaType.APPLICATION_JSON)) {
converter.write(new User("张三", 18), MediaType.APPLICATION_JSON, message);
System.out.println(message.getBodyAsString());
}
}

public static class User {
private String name;
private int age;

@JsonCreator
public User(@JsonProperty("name") String name, @JsonProperty("age") int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}

小结💡

  1. MessageConverter 的作用
    • @ResponseBody 是返回值处理器解析的
    • 但具体转换工作是 MessageConverter 做的
  2. 如何选择 MediaType
    • 首先看 @RequestMapping 上有没有指定
    • 其次看 request 的 Accept 头有没有指定
    • 最后按 MessageConverter 的顺序, 谁能谁先转换

@ControllerAdvice 之 ResponseBodyAdvice

ResponseBodyAdvice 增强

WebConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@Configuration
public class WebConfig {

@ControllerAdvice
static class CustomControllerAdvice implements ResponseBodyAdvice<Object> {
// 满足条件才转换
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
if (returnType.getMethodAnnotation(ResponseBody.class) != null ||
AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null) {
// returnType.getContainingClass().isAnnotationPresent(ResponseBody.class)) {
return true;
}
return false;
}

// 将 User 或其它类型统一为 Result 类型
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof Result) {
return body;
}
return Result.ok(body);
}
}

// @Controller
// @ResponseBody
@RestController
public static class MyController {
public User user() {
return new User("王五", 18);
}
}

public static class User {
private String name;
private int age;

public User(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}
}

Result

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Result {
private int code;
private String msg;
private Object data;

public int getCode() {
return code;
}

public void setCode(int code) {
this.code = code;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}

public Object getData() {
return data;
}

public void setData(Object data) {
this.data = data;
}

@JsonCreator
private Result(@JsonProperty("code") int code, @JsonProperty("data") Object data) {
this.code = code;
this.data = data;
}

private Result(int code, String msg) {
this.code = code;
this.msg = msg;
}

public static Result ok() {
return new Result(200, null);
}

public static Result ok(Object data) {
return new Result(200, data);
}

public static Result error(String msg) {
return new Result(500, "服务器内部错误:" + msg);
}
}

Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class Test {

// {"name":"王五","age":18}
// {"code":xx, "msg":xx, data: {"name":"王五","age":18} }
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(WebConfig.class);

ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(
context.getBean(WebConfig.MyController.class),
WebConfig.MyController.class.getMethod("user")
);
handlerMethod.setDataBinderFactory(new ServletRequestDataBinderFactory(Collections.emptyList(), null));
handlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
handlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers(context));
handlerMethod.setHandlerMethodReturnValueHandlers(getReturnValueHandlers(context));

MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndViewContainer container = new ModelAndViewContainer();
handlerMethod.invokeAndHandle(new ServletWebRequest(request, response), container);

System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}

public static HandlerMethodArgumentResolverComposite getArgumentResolvers(AnnotationConfigApplicationContext context) {
HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
composite.addResolvers(
new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), false),
new PathVariableMethodArgumentResolver(),
new RequestHeaderMethodArgumentResolver(context.getDefaultListableBeanFactory()),
new ServletCookieValueMethodArgumentResolver(context.getDefaultListableBeanFactory()),
new ExpressionValueMethodArgumentResolver(context.getDefaultListableBeanFactory()),
new ServletRequestMethodArgumentResolver(),
new ServletModelAttributeMethodProcessor(false),
new RequestResponseBodyMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter())),
new ServletModelAttributeMethodProcessor(true),
new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), true)
);
return composite;
}

public static HandlerMethodReturnValueHandlerComposite getReturnValueHandlers(AnnotationConfigApplicationContext context) {
// 添加 advice
List<ControllerAdviceBean> annotatedBeans = ControllerAdviceBean.findAnnotatedBeans(context);
List<Object> collect = annotatedBeans.stream().filter(b -> ResponseBodyAdvice.class.isAssignableFrom(b.getBeanType()))
.collect(Collectors.toList());

HandlerMethodReturnValueHandlerComposite composite = new HandlerMethodReturnValueHandlerComposite();
composite.addHandler(new ModelAndViewMethodReturnValueHandler());
composite.addHandler(new ViewNameMethodReturnValueHandler());
composite.addHandler(new ServletModelAttributeMethodProcessor(false));
composite.addHandler(new HttpEntityMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter())));
composite.addHandler(new HttpHeadersReturnValueHandler());
composite.addHandler(new RequestResponseBodyMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter()), collect));
composite.addHandler(new ServletModelAttributeMethodProcessor(true));
return composite;
}
}

ResponseBodyAdvice 增强 在整个 HandlerAdapter 调用过程中所处的位置

sequenceDiagram
participant adapter as HandlerAdapter
participant bf as WebDataBinderFactory
participant mf as ModelFactory
participant ihm as ServletInvocableHandlerMethod
participant ar as ArgumentResolvers 
participant rh as ReturnValueHandlers
participant container as ModelAndViewContainer

adapter ->> +bf: 准备 @InitBinder
bf -->> -adapter: 
adapter ->> +mf: 准备 @ModelAttribute
mf ->> +container: 添加Model数据
container -->> -mf: 
mf -->> -adapter: 
adapter ->> +ihm: invokeAndHandle
ihm ->> +ar: 获取 args
ar ->> ar: 有的解析器涉及 RequestBodyAdvice
ar ->> container: 有的解析器涉及数据绑定生成Model数据
ar -->> -ihm: args
ihm ->> ihm: method.invoke(bean,args) 得到 returnValue
ihm ->> +rh: 处理 returnValue
rect rgb(200, 150, 255)
rh ->> rh: 有的处理器涉及 ResponseBodyAdvice
end
rh ->> +container: 添加Model数据,处理视图名,是否渲染等
container -->> -rh: 
rh -->> -ihm: 
ihm -->> -adapter: 
adapter ->> +container: 获取 ModelAndView
container -->> -adapter: 

小结💡

  1. ResponseBodyAdvice 返回响应体前包装

异常解析器

ExceptionHandlerExceptionResolver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class Test {
public static void main(String[] args) throws NoSuchMethodException {
ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
resolver.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
resolver.afterPropertiesSet();

MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
// 1.测试 json
// HandlerMethod handlerMethod = new HandlerMethod(new Controller1(), Controller1.class.getMethod("foo"));
// Exception e = new ArithmeticException("被零除");
// resolver.resolveException(request, response, handlerMethod, e);
// System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
// 2.测试 mav
// HandlerMethod handlerMethod = new HandlerMethod(new Controller2(), Controller2.class.getMethod("foo"));
// Exception e = new ArithmeticException("被零除");
// ModelAndView mav = resolver.resolveException(request, response, handlerMethod, e);
// System.out.println(mav.getModel());
// System.out.println(mav.getViewName());
// 3.测试嵌套异常
// HandlerMethod handlerMethod = new HandlerMethod(new Controller3(), Controller3.class.getMethod("foo"));
// Exception e = new Exception("e1", new RuntimeException("e2", new IOException("e3")));
// resolver.resolveException(request, response, handlerMethod, e);
// System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));

// 4.测试异常处理方法参数解析
HandlerMethod handlerMethod = new HandlerMethod(new Controller4(), Controller4.class.getMethod("foo"));
Exception e = new Exception("e1");
resolver.resolveException(request, response, handlerMethod, e);
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
/*
a. ExceptionHandlerExceptionResolver 能够重用参数解析器、返回值处理器,实现组件重用
b. 能够支持嵌套异常
*/
}

static class Controller1 {
public void foo() {

}

@ExceptionHandler
@ResponseBody
public Map<String, Object> handle(ArithmeticException e) {
return Collections.singletonMap("error", e.getMessage());
}
}

static class Controller2 {
public void foo() {

}

@ExceptionHandler
public ModelAndView handle(ArithmeticException e) {
return new ModelAndView("test2", Collections.singletonMap("error", e.getMessage()));
}
}

static class Controller3 {
public void foo() {

}

@ExceptionHandler
@ResponseBody
public Map<String, Object> handle(IOException e3) {
return Collections.singletonMap("error", e3.getMessage());
}
}

static class Controller4 {
public void foo() {
}

@ExceptionHandler
@ResponseBody
public Map<String, Object> handler(Exception e, HttpServletRequest request) {
System.out.println(request);
return Collections.singletonMap("error", e.getMessage());
}
}
}

小结💡

  1. 它能够重用参数解析器、返回值处理器,实现组件重用
  2. 它能够支持嵌套异常

@ControllerAdvice 之 @ExceptionHandler

准备 @ExceptionHandler

WebConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
public class WebConfig {
@ControllerAdvice
static class MyControllerAdvice {
@ExceptionHandler
@ResponseBody
public Map<String, Object> handle(Exception e) {
return Collections.singletonMap("error", e.getMessage());
}
}

@Bean
public ExceptionHandlerExceptionResolver resolver() {
ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
resolver.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
return resolver;
}
}

Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Test {
public static void main(String[] args) throws NoSuchMethodException {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();

// ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
// resolver.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter()));
// resolver.afterPropertiesSet();

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
ExceptionHandlerExceptionResolver resolver = context.getBean(ExceptionHandlerExceptionResolver.class);

HandlerMethod handlerMethod = new HandlerMethod(new Controller5(), Controller5.class.getMethod("foo"));
Exception e = new Exception("e1");
resolver.resolveException(request, response, handlerMethod, e);
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}

static class Controller5 {
public void foo() {

}
}
}

小结💡

  1. ExceptionHandlerExceptionResolver 初始化时会解析 @ControllerAdvice 中的 @ExceptionHandler 方法
  2. ExceptionHandlerExceptionResolver 会以类为单位,在该类首次处理异常时,解析此类的 @ExceptionHandler 方法
  3. 以上两种 @ExceptionHandler 的解析结果都会缓存来避免重复解析

Tomcat 异常处理

  • 我们知道 @ExceptionHandler 只能处理发生在 mvc 流程中的异常,例如控制器内、拦截器内,那么如果是 Filter 出现了异常,如何进行处理呢?

  • 在 Spring Boot 中,是这么实现的:

    1. 因为内嵌了 Tomcat 容器,因此可以配置 Tomcat 的错误页面,Filter 与 错误页面之间是通过请求转发跳转的,可以在这里做手脚
    2. 先通过 ErrorPageRegistrarBeanPostProcessor 这个后处理器配置错误页面地址,默认为 /error 也可以通过 ${server.error.path} 进行配置
    3. 当 Filter 发生异常时,不会走 Spring 流程,但会走 Tomcat 的错误处理,于是就希望转发至 /error 这个地址
      • 当然,如果没有 @ExceptionHandler,那么最终也会走到 Tomcat 的错误处理
    4. Spring Boot 又提供了一个 BasicErrorController,它就是一个标准 @Controller,@RequestMapping 配置为 /error,所以处理异常的职责就又回到了 Spring
    5. 异常信息由于会被 Tomcat 放入 request 作用域,因此 BasicErrorController 里也能获取到
    6. 具体异常信息会由 DefaultErrorAttributes 封装好
    7. BasicErrorController 通过 Accept 头判断需要生成哪种 MediaType 的响应
      • 如果要的不是 text/html,走 MessageConverter 流程
      • 如果需要 text/html,走 mvc 流程,此时又分两种情况
        • 配置了 ErrorViewResolver,根据状态码去找 View
        • 没配置或没找到,用 BeanNameViewResolver 根据一个固定为 error 的名字找到 View,即所谓的 WhitelabelErrorView

错误页处理

关键代码

1
2
3
4
5
6
7
8
9
@Bean // ⬅️修改了 Tomcat 服务器默认错误地址, 出错时使用请求转发方式跳转
public ErrorPageRegistrar errorPageRegistrar() {
return webServerFactory -> webServerFactory.addErrorPages(new ErrorPage("/error"));
}

@Bean // ⬅️TomcatServletWebServerFactory 初始化前用它增强, 注册所有 ErrorPageRegistrar
public ErrorPageRegistrarBeanPostProcessor errorPageRegistrarBeanPostProcessor() {
return new ErrorPageRegistrarBeanPostProcessor();
}

小结💡

  1. Tomcat 的错误页处理手段

BasicErrorController

关键代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Bean // ⬅️ErrorProperties 封装环境键值, ErrorAttributes 控制有哪些错误信息
public BasicErrorController basicErrorController() {
ErrorProperties errorProperties = new ErrorProperties();
errorProperties.setIncludeException(true);
return new BasicErrorController(new DefaultErrorAttributes(), errorProperties);
}

@Bean // ⬅️名称为 error 的视图, 作为 BasicErrorController 的 text/html 响应结果
public View error() {
return new View() {
@Override
public void render(
Map<String, ?> model,
HttpServletRequest request,
HttpServletResponse response
) throws Exception {
System.out.println(model);
response.setContentType("text/html;charset=utf-8");
response.getWriter().print("\"<h3>服务器内部错误</h3>\"");
}
};
}

@Bean // ⬅️收集容器中所有 View 对象, bean 的名字作为视图名
public ViewResolver viewResolver() {
return new BeanNameViewResolver();
}

小结💡

  1. Spring Boot 中 BasicErrorController 如何工作

BeanNameUrlHandlerMapping 与 SimpleControllerHandlerAdapter

本组映射器和适配器

关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Bean
public BeanNameUrlHandlerMapping beanNameUrlHandlerMapping() {
return new BeanNameUrlHandlerMapping();
}

@Bean
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
return new SimpleControllerHandlerAdapter();
}

@Bean("/c3")
public Controller controller3() {
return (request, response) -> {
response.getWriter().print("this is c3");
return null;
};
}

小结💡

  1. BeanNameUrlHandlerMapping,以 / 开头的 bean 的名字会被当作映射路径
  2. 这些 bean 本身当作 handler,要求实现 Controller 接口
  3. SimpleControllerHandlerAdapter,调用 handler
  4. 模拟实现这组映射器和适配器

RouterFunctionMapping 与 HandlerFunctionAdapter

本组映射器和适配器

关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Bean
public RouterFunctionMapping routerFunctionMapping() {
return new RouterFunctionMapping();
}

@Bean
public HandlerFunctionAdapter handlerFunctionAdapter() {
return new HandlerFunctionAdapter();
}

@Bean
public RouterFunction<ServerResponse> r1() {
// ⬇️映射条件 ⬇️handler
return route(GET("/r1"), request -> ok().body("this is r1"));
}

小结💡

  1. RouterFunctionMapping, 通过 RequestPredicate 条件映射
  2. handler 要实现 HandlerFunction 接口
  3. HandlerFunctionAdapter, 调用 handler

SimpleUrlHandlerMapping 与 HttpRequestHandlerAdapter

本组映射器和适配器

关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping(ApplicationContext context) {
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
Map<String, ResourceHttpRequestHandler> map
= context.getBeansOfType(ResourceHttpRequestHandler.class);
handlerMapping.setUrlMap(map);
return handlerMapping;
}

@Bean
public HttpRequestHandlerAdapter httpRequestHandlerAdapter() {
return new HttpRequestHandlerAdapter();
}

@Bean("/**")
public ResourceHttpRequestHandler handler1() {
ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler();
handler.setLocations(List.of(new ClassPathResource("static/")));
return handler;
}

@Bean("/img/**")
public ResourceHttpRequestHandler handler2() {
ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler();
handler.setLocations(List.of(new ClassPathResource("images/")));
return handler;
}

小结💡

  1. SimpleUrlHandlerMapping 不会在初始化时收集映射信息,需要手动收集
  2. SimpleUrlHandlerMapping 映射路径
  3. ResourceHttpRequestHandler 作为静态资源 handler
  4. HttpRequestHandlerAdapter, 调用此 handler

静态资源解析优化

关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Bean("/**")
public ResourceHttpRequestHandler handler1() {
ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler();
handler.setLocations(List.of(new ClassPathResource("static/")));
handler.setResourceResolvers(List.of(
// ⬇️缓存优化
new CachingResourceResolver(new ConcurrentMapCache("cache1")),
// ⬇️压缩优化
new EncodedResourceResolver(),
// ⬇️原始资源解析
new PathResourceResolver()
));
return handler;
}

小结💡

  1. 责任链模式体现
  2. 压缩文件需要手动生成

欢迎页

关键代码

1
2
3
4
5
6
7
8
9
10
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext context) {
Resource resource = context.getResource("classpath:static/index.html");
return new WelcomePageHandlerMapping(null, context, resource, "/**");
}

@Bean
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
return new SimpleControllerHandlerAdapter();
}

小结💡

  1. 欢迎页支持静态欢迎页与动态欢迎页
  2. WelcomePageHandlerMapping 映射欢迎页(即只映射 ‘/‘)
    • 它内置的 handler ParameterizableViewController 作用是不执行逻辑,仅根据视图名找视图
    • 视图名固定为 forward:index.html
  3. SimpleControllerHandlerAdapter, 调用 handler
    • 转发至 /index.html
    • 处理 /index.html 又会走上面的静态资源处理流程

映射器与适配器小结

  1. HandlerMapping 负责建立请求与控制器之间的映射关系
    • RequestMappingHandlerMapping (与 @RequestMapping 匹配)
    • WelcomePageHandlerMapping (/)
    • BeanNameUrlHandlerMapping (与 bean 的名字匹配 以 / 开头)
    • RouterFunctionMapping (函数式 RequestPredicate, HandlerFunction)
    • SimpleUrlHandlerMapping (静态资源 通配符 /** /img/**)
    • 之间也会有顺序问题, boot 中默认顺序如上
  2. HandlerAdapter 负责实现对各种各样的 handler 的适配调用
    • RequestMappingHandlerAdapter 处理:@RequestMapping 方法
      • 参数解析器、返回值处理器体现了组合模式
    • SimpleControllerHandlerAdapter 处理:Controller 接口
    • HandlerFunctionAdapter 处理:HandlerFunction 函数式接口
    • HttpRequestHandlerAdapter 处理:HttpRequestHandler 接口 (静态资源处理)
    • 这也是典型适配器模式体现

mvc 处理流程

当浏览器发送一个请求 http://localhost:8080/hello 后,请求到达服务器,其处理流程是:

  1. 服务器提供了 DispatcherServlet,它使用的是标准 Servlet 技术

    • 路径:默认映射路径为 /,即会匹配到所有请求 URL,可作为请求的统一入口,也被称之为前控制器
      • jsp 不会匹配到 DispatcherServlet
      • 其它有路径的 Servlet 匹配优先级也高于 DispatcherServlet
    • 创建:在 Boot 中,由 DispatcherServletAutoConfiguration 这个自动配置类提供 DispatcherServlet 的 bean
    • 初始化:DispatcherServlet 初始化时会优先到容器里寻找各种组件,作为它的成员变量
      • HandlerMapping,初始化时记录映射关系
      • HandlerAdapter,初始化时准备参数解析器、返回值处理器、消息转换器
      • HandlerExceptionResolver,初始化时准备参数解析器、返回值处理器、消息转换器
      • ViewResolver
  2. DispatcherServlet 会利用 RequestMappingHandlerMapping 查找控制器方法

    • 例如根据 /hello 路径找到 @RequestMapping(“/hello”) 对应的控制器方法

    • 控制器方法会被封装为 HandlerMethod 对象,并结合匹配到的拦截器一起返回给 DispatcherServlet

    • HandlerMethod 和拦截器合在一起称为 HandlerExecutionChain(调用链)对象

  3. DispatcherServlet 接下来会:

    1. 调用拦截器的 preHandle 方法
    2. RequestMappingHandlerAdapter 调用 handle 方法,准备数据绑定工厂、模型工厂、ModelAndViewContainer、将 HandlerMethod 完善为 ServletInvocableHandlerMethod
      • @ControllerAdvice 全局增强点1️⃣:补充模型数据
      • @ControllerAdvice 全局增强点2️⃣:补充自定义类型转换器
      • 使用 HandlerMethodArgumentResolver 准备参数
        • @ControllerAdvice 全局增强点3️⃣:RequestBody 增强
      • 调用 ServletInvocableHandlerMethod
      • 使用 HandlerMethodReturnValueHandler 处理返回值
        • @ControllerAdvice 全局增强点4️⃣:ResponseBody 增强
      • 根据 ModelAndViewContainer 获取 ModelAndView
        • 如果返回的 ModelAndView 为 null,不走第 4 步视图解析及渲染流程
          • 例如,有的返回值处理器调用了 HttpMessageConverter 来将结果转换为 JSON,这时 ModelAndView 就为 null
        • 如果返回的 ModelAndView 不为 null,会在第 4 步走视图解析及渲染流程
    3. 调用拦截器的 postHandle 方法
    4. 处理异常或视图渲染
      • 如果 1~3 出现异常,走 ExceptionHandlerExceptionResolver 处理异常流程
        • @ControllerAdvice 全局增强点5️⃣:@ExceptionHandler 异常处理
      • 正常,走视图解析及渲染流程
    5. 调用拦截器的 afterCompletion 方法

Boot

Boot 启动过程

阶段一:SpringApplication 构造

  1. 记录 BeanDefinition 源
  2. 推断应用类型
  3. 记录 ApplicationContext 初始化器
  4. 记录监听器
  5. 推断主启动类

阶段二:执行 run 方法

  1. 得到 SpringApplicationRunListeners,名字取得不好,实际是事件发布器

    • 发布 application starting 事件1️⃣
  2. 封装启动 args

  3. 准备 Environment 添加命令行参数(*)

  4. ConfigurationPropertySources 处理(*)

    • 发布 application environment 已准备事件2️⃣
  5. 通过 EnvironmentPostProcessorApplicationListener 进行 env 后处理(*)

    • application.properties,由 StandardConfigDataLocationResolver 解析
    • spring.application.json
  6. 绑定 spring.main 到 SpringApplication 对象(*)

  7. 打印 banner(*)

  8. 创建容器

  9. 准备容器

    • 发布 application context 已初始化事件3️⃣
  10. 加载 bean 定义

    • 发布 application prepared 事件4️⃣
  11. refresh 容器

    • 发布 application started 事件5️⃣
  12. 执行 runner

    • 发布 application ready 事件6️⃣

    • 这其中有异常,发布 application failed 事件7️⃣

带 * 的有独立的示例

启动过程

阶段一:对应SpringApplication构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@Configuration
public class Test_1 {

public static void main(String[] args) throws Exception {
System.out.println("1. 演示获取 Bean Definition 源");
SpringApplication spring = new SpringApplication(Test_1.class);
spring.setSources(Collections.singleton("classpath:b01.xml"));
System.out.println("2. 演示推断应用类型");
Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
deduceFromClasspath.setAccessible(true);
System.out.println("\t应用类型为:" + deduceFromClasspath.invoke(null));
System.out.println("3. 演示 ApplicationContext 初始化器");
// 默认从配置文件读取,此时的ApplicationContextInitializer尚未refresh
spring.addInitializers(applicationContext -> {
if (applicationContext instanceof GenericApplicationContext) {
GenericApplicationContext gac = (GenericApplicationContext) applicationContext;
gac.registerBean("bean3", Bean3.class);
}
});
System.out.println("4. 演示监听器与事件");
spring.addListeners(event -> System.out.println("\t事件为:" + event.getClass()));
System.out.println("5. 演示主类推断");
Method deduceMainApplicationClass = SpringApplication.class.getDeclaredMethod("deduceMainApplicationClass");
deduceMainApplicationClass.setAccessible(true);
System.out.println("\t主类是:" + deduceMainApplicationClass.invoke(spring));

ConfigurableApplicationContext context = spring.run(args);

// 创建 ApplicationContext
// 调用初始化器 对 ApplicationContext 做扩展
// ApplicationContext.refresh
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
}
context.close();
/*
a. SpringApplication 构造方法中所做的操作
1. 可以有多种源用来加载 bean 定义
2. 应用类型推断
3. 容器初始化器
4. 演示启动各阶段事件
5. 演示主类推断
*/
}

static class Bean1 {

}

static class Bean2 {

}

static class Bean3 {

}

@Bean
public Bean2 bean2() {
return new Bean2();
}

@Bean
public TomcatServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}

阶段二:对应第1步,并演示 7 个事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class Test_2 {
public static void main(String[] args) throws Exception {
long startTime = System.nanoTime();
// 添加 app 监听器
SpringApplication app = new SpringApplication();
app.addListeners(e -> System.out.println("\t监听到事件: " + e.getClass().getName()));

// 获取事件发送器实现类名
List<String> names = SpringFactoriesLoader.loadFactoryNames(SpringApplicationRunListener.class, Test_2.class.getClassLoader());
for (String name : names) {
System.out.println(name);
Class<?> clazz = Class.forName(name);
Constructor<?> constructor = clazz.getConstructor(SpringApplication.class, String[].class);
SpringApplicationRunListener publisher = (SpringApplicationRunListener) constructor.newInstance(app, args);

// 发布事件
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
publisher.starting(bootstrapContext); // spring boot 开始启动
publisher.environmentPrepared(bootstrapContext, new StandardEnvironment()); // 环境信息准备完毕
GenericApplicationContext context = new GenericApplicationContext();
publisher.contextPrepared(context); // 在 spring 容器创建,并调用初始化器之后,发送此事件
publisher.contextLoaded(context); // 所有 bean definition 加载完毕
context.refresh();
publisher.started(context, Duration.ofNanos(System.nanoTime() - startTime)); // spring 容器初始化完成(refresh 方法调用完毕)
publisher.ready(context, Duration.ofNanos(System.nanoTime() - startTime)); // spring boot 启动完毕

publisher.failed(context, new Exception("出错了")); // spring boot 启动出错
}

/*
a. 如何读取 spring.factories 中的配置
b. run 方法内获取事件发布器 (得到 SpringApplicationRunListeners) 的过程, 对应步骤中
1.获取事件发布器
发布 application starting 事件1️⃣
发布 application environment 已准备事件2️⃣
发布 application context 已初始化事件3️⃣
发布 application prepared 事件4️⃣
发布 application started 事件5️⃣
发布 application ready 事件6️⃣
这其中有异常,发布 application failed 事件7️⃣
*/
}
}

阶段二:对应第2、8到12步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// 运行时请添加运行参数 --server.port=8080 debug
@SuppressWarnings("all")
public class Test_3 {
public static void main(String[] args) throws Exception {
SpringApplication app = new SpringApplication();
app.addInitializers(applicationContext -> System.out.println("执行初始化器增强..."));

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 2. 封装启动 args");
DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 8. 创建容器");
// 在执行构造的时候,已经推断出来了是什么类型,WebApplicationType.deduceFromClasspath();
GenericApplicationContext context = createApplicationContext(WebApplicationType.SERVLET);

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 9. 准备容器");
for (ApplicationContextInitializer initializer : app.getInitializers()) {
initializer.initialize(context);
}

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 10. 加载 bean 定义");
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
AnnotatedBeanDefinitionReader reader1 = new AnnotatedBeanDefinitionReader(beanFactory);
XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);

reader1.register(Config.class); // 来源配置类
// xml里面定义了一个Bean4
reader2.loadBeanDefinitions(new ClassPathResource("b03.xml")); // 来源XML
scanner.scan("com.xiao.learning.Boot.chapter39.sub"); // 来源包的名称

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 11. refresh 容器");
context.refresh();

for (String name : context.getBeanDefinitionNames()) {
System.out.println("name:" + name + " 来源:" + beanFactory.getBeanDefinition(name).getResourceDescription());
}

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 12. 执行 runner");
for (CommandLineRunner runner : context.getBeansOfType(CommandLineRunner.class).values()) {
runner.run(args);
}

for (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class).values()) {
runner.run(arguments);
}
context.close();
/*
a. 创建容器、加载 bean 定义、refresh, 对应的步骤
*/
}

private static GenericApplicationContext createApplicationContext(WebApplicationType type) {
GenericApplicationContext context = null;
switch (type) {
case SERVLET:
context = new AnnotationConfigServletWebServerApplicationContext();
break;
case REACTIVE:
context = new AnnotationConfigReactiveWebServerApplicationContext();
break;
case NONE:
context = new AnnotationConfigApplicationContext();
break;
}
return context;
}

static class Bean4 {

}

static class Bean5 {

}

static class Bean6 {

}

@Configuration
static class Config {
@Bean
public Bean5 bean5() {
return new Bean5();
}

@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}

@Bean
public CommandLineRunner commandLineRunner() {
return args -> System.out.println("commandLineRunner()..." + Arrays.toString(args));
}

@Bean
public ApplicationRunner applicationRunner() {
return args -> {
System.out.println("applicationRunner()..." + Arrays.toString(args.getSourceArgs()));
System.out.println(args.getOptionNames());
System.out.println(args.getOptionValues("server.port"));
System.out.println(args.getNonOptionArgs());
};
}
}
}

阶段二:Step3

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Step3 {
public static void main(String[] args) throws IOException {
ApplicationEnvironment env = new ApplicationEnvironment(); // 系统环境变量, properties, yaml
env.getPropertySources().addLast(new ResourcePropertySource(new ClassPathResource("step3.properties"))); // 里面就一个server.port=8080
env.getPropertySources().addFirst(new SimpleCommandLinePropertySource(args));
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
System.out.println(env.getProperty("JAVA_HOME"));

System.out.println(env.getProperty("server.port"));
}
}

阶段二:Step4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Step4 {

public static void main(String[] args) throws IOException, NoSuchFieldException {
ApplicationEnvironment env = new ApplicationEnvironment();
env.getPropertySources().addLast(
new ResourcePropertySource("step4", new ClassPathResource("step4.properties"))
);
// 对命名不规则的键做统一处理
ConfigurationPropertySources.attach(env);
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}

System.out.println(env.getProperty("user.first-name"));
System.out.println(env.getProperty("user.middle-name"));
System.out.println(env.getProperty("user.last-name"));
}
}

step4.properties

1
2
3
user.first-name=George
user.middle_name=Walker
user.lastName=Bush

阶段二:Step5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/*
可以添加参数 --spring.application.json={\"server\":{\"port\":9090}} 测试 SpringApplicationJsonEnvironmentPostProcessor
*/
public class Step5 {
public static void main(String[] args) {
SpringApplication app = new SpringApplication();
app.addListeners(new EnvironmentPostProcessorApplicationListener());

/*List<String> names = SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, Step5.class.getClassLoader());
for (String name : names) {
System.out.println(name);
}*/

// 从配置文件中加载源
EventPublishingRunListener publisher = new EventPublishingRunListener(app, args);
ApplicationEnvironment env = new ApplicationEnvironment();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强前");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
publisher.environmentPrepared(new DefaultBootstrapContext(), env);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}

}

private static void test1() {
SpringApplication app = new SpringApplication();
ApplicationEnvironment env = new ApplicationEnvironment();

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强前");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
ConfigDataEnvironmentPostProcessor postProcessor1 = new ConfigDataEnvironmentPostProcessor(new DeferredLogs(), new DefaultBootstrapContext());
postProcessor1.postProcessEnvironment(env, app);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> ConfigDataEnvironmentPostProcessor增强后");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
RandomValuePropertySourceEnvironmentPostProcessor postProcessor2 = new RandomValuePropertySourceEnvironmentPostProcessor(new DeferredLog());
postProcessor2.postProcessEnvironment(env, app);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> RandomValuePropertySourceEnvironmentPostProcessor增强后");
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
System.out.println(env.getProperty("server.port"));
// 产生随机数的源
System.out.println(env.getProperty("random.int"));
System.out.println(env.getProperty("random.int"));
System.out.println(env.getProperty("random.int"));
System.out.println(env.getProperty("random.uuid"));
System.out.println(env.getProperty("random.uuid"));
System.out.println(env.getProperty("random.uuid"));
}
}

阶段二:Step6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class Step6 {
// 绑定以spring.main为前缀的 key value 绑定至 SpringApplication, 请通过 debug 查看
public static void main(String[] args) throws IOException {
SpringApplication application = new SpringApplication();
ApplicationEnvironment env = new ApplicationEnvironment();
env.getPropertySources().addLast(new ResourcePropertySource("step4", new ClassPathResource("step4.properties")));
env.getPropertySources().addLast(new ResourcePropertySource("step6", new ClassPathResource("step6.properties")));

// User user = Binder.get(env).bind("user", User.class).get();
// System.out.println(user);

// 现有对象进行绑定
// User user = new User();
// Binder.get(env).bind("user", Bindable.ofInstance(user));
// System.out.println(user);

System.out.println(application);
// org.springframework.boot.SpringApplication.bindToSpringApplication
Binder.get(env).bind("spring.main", Bindable.ofInstance(application));
System.out.println(application);
}

static class User {
private String firstName;
private String middleName;
private String lastName;

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getMiddleName() {
return middleName;
}

public void setMiddleName(String middleName) {
this.middleName = middleName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

@Override
public String toString() {
return "User{" +
"firstName='" + firstName + '\'' +
", middleName='" + middleName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
}

step6.properties

1
2
spring.main.banner-mode=off
spring.main.lazy-initialization=true

阶段二:Step7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Step7 {
public static void main(String[] args) {
ApplicationEnvironment env = new ApplicationEnvironment();
SpringApplicationBannerPrinter printer = new SpringApplicationBannerPrinter(
new DefaultResourceLoader(),
new SpringBootBanner()
);
// 测试文字 banner
// env.getPropertySources().addLast(new MapPropertySource("custom", Collections.singletonMap("spring.banner.location", "banner1.txt")));
// 测试图片 banner
// env.getPropertySources().addLast(new MapPropertySource("custom", Collections.singletonMap("spring.banner.image.location", "banner2.png")));
// 版本号的获取
// System.out.println(SpringBootVersion.getVersion());
printer.print(env, Step7.class, System.out);
}
}

小结💡

  1. SpringApplication 构造方法中所做的操作
    • 可以有多种源用来加载 bean 定义
    • 应用类型推断
    • 添加容器初始化器
    • 添加监听器
    • 演示主类推断
  2. 如何读取 spring.factories 中的配置
  3. 从配置中获取重要的事件发布器:SpringApplicationRunListeners
  4. 容器的创建、初始化器增强、加载 bean 定义等
  5. CommandLineRunner、ApplicationRunner 的作用
  6. 环境对象
    1. 命令行 PropertySource
    2. ConfigurationPropertySources 规范环境键名称
    3. EnvironmentPostProcessor 后处理增强
      • 由 EventPublishingRunListener 通过监听事件2️⃣来调用
    4. 绑定 spring.main 前缀的 key value 至 SpringApplication
  7. 生成Banner

Tomcat 内嵌容器

Tomcat 基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Server
└───Service
├───Connector (协议, 端口)
└───Engine
└───Host(虚拟主机 localhost)
├───Context1 (应用1, 可以设置虚拟路径, / 即 url 起始路径; 项目磁盘路径, 即 docBase )
│ │ index.html
│ └───WEB-INF
│ │ web.xml (servlet, filter, listener) 3.0
│ ├───classes (servlet, controller, service ...)
│ ├───jsp
│ └───lib (第三方 jar 包)
└───Context2 (应用2)
│ index.html
└───WEB-INF
web.xml

Tomcat 内嵌容器

HelloServlet

1
2
3
4
5
6
7
8
public class HelloServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().print("\"<h3>hello</h3>\"");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class TestTomcat {
public static void main(String[] args) throws LifecycleException, IOException {
// 1.创建 Tomcat 对象
Tomcat tomcat = new Tomcat();
tomcat.setBaseDir("tomcat");

// 2.创建项目文件夹, 即 docBase 文件夹
File docBase = Files.createTempDirectory("boot.").toFile();
docBase.deleteOnExit();

// 3.创建 Tomcat 项目, 在 Tomcat 中称为 Context
Context context = tomcat.addContext("", docBase.getAbsolutePath());

// 4.编程添加 Servlet
context.addServletContainerInitializer(new ServletContainerInitializer() {
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
HelloServlet helloServlet = new HelloServlet();
ctx.addServlet("aaa", helloServlet).addMapping("/hello");
}
}, Collections.emptySet());

// 5.启动 Tomcat
tomcat.start();

// 6.创建连接器, 设置监听端口
Connector connector = new Connector(new Http11Nio2Protocol());
connector.setPort(8080);
tomcat.setConnector(connector);
}
}

集成 Spring 容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public class TestTomcat {
// Spring在 org.springframework.context.support.AbstractApplicationContext#refresh进行启动Tomcat
@SuppressWarnings("all")
public static void main(String[] args) throws LifecycleException, IOException {
// 1.创建 Tomcat 对象
Tomcat tomcat = new Tomcat();
tomcat.setBaseDir("tomcat");

// 2.创建项目文件夹, 即 docBase 文件夹
File docBase = Files.createTempDirectory("boot.").toFile();
docBase.deleteOnExit(); // 程序退出时删除

// 3.创建 Tomcat 项目, 在 Tomcat 中称为 Context
Context context = tomcat.addContext("", docBase.getAbsolutePath());

WebApplicationContext springContext = getApplicationContext();

// 4.编程添加 Servlet
context.addServletContainerInitializer(new ServletContainerInitializer() {
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
HelloServlet helloServlet = new HelloServlet();
ctx.addServlet("aaa", helloServlet).addMapping("/hello");

// DispatcherServlet dispatcherServlet = springContext.getBean(DispatcherServlet.class);
// ctx.addServlet("dispatcherServlet", dispatcherServlet).addMapping("/");
for (ServletRegistrationBean registrationBean : springContext.getBeansOfType(ServletRegistrationBean.class).values()) {
registrationBean.onStartup(ctx);
}
}
}, Collections.emptySet());

// 5.启动 Tomcat
tomcat.start();

// 6.创建连接器, 设置监听端口
Connector connector = new Connector(new Http11Nio2Protocol());
connector.setPort(8080);
tomcat.setConnector(connector);
}

public static WebApplicationContext getApplicationContext() {
// AnnotationConfigServletWebServerApplicationContext 支持内嵌Tomcat
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(Config.class);
context.refresh();
return context;
}

@Configuration
static class Config {
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}

@Bean
// 这个例子中必须为 DispatcherServlet 提供 AnnotationConfigWebApplicationContext, 否则会选择 XmlWebApplicationContext 实现
public DispatcherServlet dispatcherServlet(WebApplicationContext applicationContext) {
return new DispatcherServlet(applicationContext);
}

@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
handlerAdapter.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
return handlerAdapter;
}

@RestController
static class CustomController {
@GetMapping("hello2")
public Map<String, Object> hello() {
return Collections.singletonMap("hello2", "hello2, spring!");
}
}
}
}

Boot 自动配置

AopAutoConfiguration

Spring Boot 是利用了自动配置类来简化了 aop 相关配置

  • AOP 自动配置类为 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
  • 可以通过 spring.aop.auto=false 禁用 aop 自动配置
  • AOP 自动配置的本质是通过 @EnableAspectJAutoProxy 来开启了自动代理,如果在引导类上自己添加了 @EnableAspectJAutoProxy 那么以自己添加的为准
  • @EnableAspectJAutoProxy 的本质是向容器中添加了 AnnotationAwareAspectJAutoProxyCreator 这个 bean 后处理器,它能够找到容器中所有切面,并为匹配切点的目标类创建代理,创建代理的工作一般是在 bean 的初始化阶段完成的

DataSourceAutoConfiguration

  • 对应的自动配置类为:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  • 它内部采用了条件装配,通过检查容器的 bean,以及类路径下的 class,来决定该 @Bean 是否生效

简单说明一下,Spring Boot 支持两大类数据源:

  • EmbeddedDatabase - 内嵌数据库连接池
  • PooledDataSource - 非内嵌数据库连接池

PooledDataSource 又支持如下数据源

  • hikari 提供的 HikariDataSource
  • tomcat-jdbc 提供的 DataSource
  • dbcp2 提供的 BasicDataSource
  • oracle 提供的 PoolDataSourceImpl

如果知道数据源的实现类类型,即指定了 spring.datasource.type,理论上可以支持所有数据源,但这样做的一个最大问题是无法订制每种数据源的详细配置(如最大、最小连接数等)

MybatisAutoConfiguration

  • MyBatis 自动配置类为 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
  • 它主要配置了两个 bean
    • SqlSessionFactory - MyBatis 核心对象,用来创建 SqlSession
    • SqlSessionTemplate - SqlSession 的实现,此实现会与当前线程绑定
    • 用 ImportBeanDefinitionRegistrar 的方式扫描所有标注了 @Mapper 注解的接口
    • 用 AutoConfigurationPackages 来确定扫描的包
  • 还有一个相关的 bean:MybatisProperties,它会读取配置文件中带 mybatis. 前缀的配置项进行定制配置

@MapperScan 注解的作用与 MybatisAutoConfiguration 类似,会注册 MapperScannerConfigurer 有如下区别

  • @MapperScan 扫描具体包(当然也可以配置关注哪个注解)
  • @MapperScan 如果不指定扫描具体包,则会把引导类范围内,所有接口当做 Mapper 接口
  • MybatisAutoConfiguration 关注的是所有标注 @Mapper 注解的接口,会忽略掉非 @Mapper 标注的接口

这里有同学有疑问,之前介绍的都是将具体类交给 Spring 管理,怎么到了 MyBatis 这儿,接口就可以被管理呢?

  • 其实并非将接口交给 Spring 管理,而是每个接口会对应一个 MapperFactoryBean,是后者被 Spring 所管理,接口只是作为 MapperFactoryBean 的一个属性来配置

TransactionAutoConfiguration

  • 事务自动配置类有两个:

    • org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
    • org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
  • 前者配置了 DataSourceTransactionManager 用来执行事务的提交、回滚操作

  • 后者功能上对标 @EnableTransactionManagement,包含以下三个 bean

    • BeanFactoryTransactionAttributeSourceAdvisor 事务切面类,包含通知和切点
    • TransactionInterceptor 事务通知类,由它在目标方法调用前后加入事务操作
    • AnnotationTransactionAttributeSource 会解析 @Transactional 及事务属性,也包含了切点功能
  • 如果自己配置了 DataSourceTransactionManager 或是在引导类加了 @EnableTransactionManagement,则以自己配置的为准

ServletWebServerFactoryAutoConfiguration

  • 提供 ServletWebServerFactory

DispatcherServletAutoConfiguration

  • 提供 DispatcherServlet
  • 提供 DispatcherServletRegistrationBean

WebMvcAutoConfiguration

  • 配置 DispatcherServlet 的各项组件,提供的 bean 见过的有
    • 多项 HandlerMapping
    • 多项 HandlerAdapter
    • HandlerExceptionResolver

ErrorMvcAutoConfiguration

  • 提供的 bean 有 BasicErrorController

MultipartAutoConfiguration

  • 它提供了 org.springframework.web.multipart.support.StandardServletMultipartResolver
  • 该 bean 用来解析 multipart/form-data 格式的数据

HttpEncodingAutoConfiguration

  • POST 请求参数如果有中文,无需特殊设置,这是因为 Spring Boot 已经配置了 org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter
  • 对应配置 server.servlet.encoding.charset=UTF-8,默认就是 UTF-8
  • 当然,它只影响非 json 格式的数据

自动配置类原理

关键代码

假设已有第三方的两个自动配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration // ⬅️第三方的配置类
static class AutoConfiguration1 {
@Bean
public Bean1 bean1() {
return new Bean1();
}
}

@Configuration // ⬅️第三方的配置类
static class AutoConfiguration2 {
@Bean
public Bean2 bean2() {
return new Bean2();
}
}

提供一个配置文件 META-INF/spring.factories,key 为导入器类名,值为多个自动配置类名,用逗号分隔

1
2
3
com.xiao.learning.Boot.chapter41.A41_1$MyImportSelector=\
com.xiao.learning.Boot.chapter41.A41_1.AutoConfiguration1,\
com.xiao.learning.Boot.chapter41.A41_1.AutoConfiguration2

注意

  • 上述配置文件中 MyImportSelector 与 AutoConfiguration1,AutoConfiguration2 为简洁均省略了包名,自己测试时请将包名根据情况补全

引入自动配置

1
2
3
4
5
6
7
8
9
10
11
@Configuration // ⬅️本项目的配置类
@Import(MyImportSelector.class)
static class Config { }

static class MyImportSelector implements DeferredImportSelector {
// ⬇️该方法从 META-INF/spring.factories 读取自动配置类名,返回的 String[] 即为要导入的配置类
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return SpringFactoriesLoader
.loadFactoryNames(MyImportSelector.class, null).toArray(new String[0]);
}
}

小结💡

  1. 自动配置类本质上就是一个配置类而已,只是用 META-INF/spring.factories 管理,与应用配置类解耦
  2. @Enable 打头的注解本质是利用了 @Import
  3. @Import 配合 DeferredImportSelector 即可实现导入,selectImports 方法的返回值即为要导入的配置类名
  4. DeferredImportSelector 的导入会在最后执行,为的是让其它配置优先解析

条件装配底层

条件装配的底层是本质上是 @Conditional 与 Condition,这两个注解。引入自动配置类时,期望满足一定条件才能被 Spring 管理,不满足则不管理,怎么做呢?

比如条件是【类路径下必须有 dataSource】这个 bean ,怎么做呢?

首先编写条件判断类,它实现 Condition 接口,编写条件判断逻辑

1
2
3
4
5
6
static class MyCondition1 implements Condition { // 可以继承SpringBootCondition重写getMatchOutcome方法
// ⬇️如果存在 Druid 依赖,条件成立
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource", null);
}
}

其次,在要导入的自动配置类上添加 @Conditional(MyCondition1.class),将来此类被导入时就会做条件检查

1
2
3
4
5
6
7
8
@Configuration // 第三方的配置类
@Conditional(MyCondition1.class) // ⬅️加入条件
static class AutoConfiguration1 {
@Bean
public Bean1 bean1() {
return new Bean1();
}
}

分别测试加入和去除 druid 依赖,观察 bean1 是否存在于容器

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>

其它

FactoryBean

Bean1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Bean1 implements BeanFactoryAware {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);

private Bean2 bean2;

@Autowired
public void setBean2(Bean2 bean2) {
log.info("setBean2({})", bean2);
this.bean2 = bean2;
}

public Bean2 getBean2() {
return bean2;
}

@PostConstruct
public void init() {
log.debug("init");
}

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("setBeanFactory({})", beanFactory);
}
@Component
static class Bean2 {
}
}

Bean1FactoryBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Component("bean1")
public class Bean1FactoryBean implements FactoryBean<Bean1> {

private static final Logger log = LoggerFactory.getLogger(Bean1FactoryBean.class);

// 决定了根据【类型】获取或依赖注入能否成功
@Override
public Class<?> getObjectType() {
// 这里是通过Spring的getBean(Class<T> requiredType)获取
return Bean1.class;
}

// 决定了 getObject() 方法被调用一次还是多次
@Override
public boolean isSingleton() {
return true;
}

// 产生产品,即创建Bean
@Override
public Bean1 getObject() {
Bean1 bean1 = new Bean1();
log.info("create bean: {}", bean1);
return bean1;
}
}

Bean1PostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class Bean1PostProcessor implements BeanPostProcessor {

private static final Logger log = LoggerFactory.getLogger(Bean1PostProcessor.class);

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("bean1") && bean instanceof Bean1) {
log.info("before [{}] init", beanName);
}
return bean;
}

// FactoryBean创建的Bean属于代理创建,代理创建只会在创建之后调用此方法
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("bean1") && bean instanceof Bean1) {
log.info("after [{}] init", beanName);
}
return bean;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@ComponentScan
public class Test {

public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test.class);

// 单例的产品不会存储于 BeanFactory 的 singletonObjects 成员中, 而是另一个 factoryBeanObjectCache 成员中
Bean1 bean1 = (Bean1) context.getBean("bean1");
Bean1 bean2 = (Bean1) context.getBean("bean1");
Bean1 bean3 = (Bean1) context.getBean("bean1");
System.out.println(bean1);
System.out.println(bean2);
System.out.println(bean3);

System.out.println(context.getBean(Bean1.class));

System.out.println(context.getBean(Bean1FactoryBean.class));
System.out.println(context.getBean("&bean1")); // 获取工厂本身

context.close();
}

}

小结💡

  1. 它的作用是用制造创建过程较为复杂的产品, 如 SqlSessionFactory, 但 @Bean 已具备等价功能
  2. 使用上较为古怪, 一不留神就会用错
    1. 被 FactoryBean 创建的产品
      • 会认为创建、依赖注入、Aware 接口回调、前初始化这些都是 FactoryBean 的职责, 这些流程都不会走
      • 唯有后初始化的流程会走, 也就是产品可以被代理增强
      • 单例的产品不会存储于 BeanFactory 的 singletonObjects 成员中, 而是另一个 factoryBeanObjectCache 成员中
    2. 按名字去获取时, 拿到的是产品对象, 名字前面加 & 获取的是工厂对象

@Indexed 原理

真实项目中,只需要加入以下依赖即可

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<optional>true</optional>
</dependency>

@Indexed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*
做这个试验前, 先在 target/classes 创建 META-INF/spring.components, 内容为

com.xiao.learning.Other.chapter44.Bean1=org.springframework.stereotype.Component
com.xiao.learning.Other.chapter44.Bean2=org.springframework.stereotype.Component

做完实现建议删除, 避免影响其它组件扫描的结果

真实项目中, 这个步骤可以自动完成, 加入以下依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<optional>true</optional>
</dependency>
*/
public class Test {
public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 组件扫描的核心类
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);

// @Indexed 的原理, 在编译时就根据 @Indexed 生成 META-INF/spring.components 文件

// 扫描时如果发现 META-INF/spring.components 存在, 以它为准加载 bean definition
// 否则, 会遍历包下所有 class 资源 (包括 jar 内的)

// @Component注解里面有@Indexed注解
scanner.scan(Test.class.getPackage().getName());

for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
}

@Component
static class Bean1 {
}

@Component
static class Bean2 {
}

@Component
static class Bean3 {
}
}

小结💡

  1. 在编译时就根据 @Indexed 生成 META-INF/spring.components 文件
  2. 扫描时
    • 如果发现 META-INF/spring.components 存在, 以它为准加载 bean definition
    • 否则, 会遍历包下所有 class 资源 (包括 jar 内的)
  3. 解决的问题,在编译期就找到 @Component 组件,节省运行期间扫描 @Component 的时间

代理进一步理解

演示 - 代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
@SpringBootApplication
public class Test {

public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(Test.class, args);

Bean1 proxy = context.getBean(Bean1.class);



/*
1.演示 spring 代理的设计特点
依赖注入和初始化影响的是原始对象
代理与目标是两个对象,二者成员变量并不共用数据
*/
showProxyAndTarget(proxy);

System.out.println(">>>>>>>>>>>>>>>>>>>");
System.out.println(proxy.getBean2());
System.out.println(proxy.isInitialized());

/*
2.演示 static 方法、final 方法、private 方法均无法增强
*/

proxy.m1();
proxy.m2();
proxy.m3();
Method m4 = Bean1.class.getDeclaredMethod("m4");
m4.setAccessible(true);
m4.invoke(proxy);

context.close();
}


public static void showProxyAndTarget(Bean1 proxy) throws Exception {
System.out.println(">>>>> 代理中的成员变量");
System.out.println("\tinitialized=" + proxy.initialized);
System.out.println("\tbean2=" + proxy.bean2);

if (proxy instanceof Advised) {
Advised advised = (Advised) proxy;
System.out.println(">>>>> 目标中的成员变量");
Bean1 target = (Bean1) advised.getTargetSource().getTarget();
System.out.println("\tinitialized=" + target.initialized);
System.out.println("\tbean2=" + target.bean2);
}
}

@Component
static class Bean1 {

private static final Logger log = LoggerFactory.getLogger(Bean1.class);

protected Bean2 bean2;

protected boolean initialized;

@Autowired
public void setBean2(Bean2 bean2) {
log.info("setBean2(Bean2 bean2)");
this.bean2 = bean2;
}

@PostConstruct
public void init() {
log.info("init");
initialized = true;
}

public Bean2 getBean2() {
log.info("getBean2()");
return bean2;
}

public boolean isInitialized() {
log.info("isInitialized()");
return initialized;
}

public void m1() {
System.out.println("m1() 成员方法");
}

final public void m2() {
System.out.println("m2() final 方法");
}

static public void m3() {
System.out.println("m3() static 方法");
}

private void m4() {
System.out.println("m4() private 方法");
}

}

@Component
static class Bean2 {
}

@Aspect
@Component
static class MyAspect {

// 故意对所有方法增强
@Before("execution(* com.xiao.learning.Other.chapter45.Test.Bean1.*(..))")
public void before() {
System.out.println("before");
}
}
}

小结💡

  1. spring 代理的设计特点

    • 依赖注入和初始化影响的是原始对象

      • 因此 cglib 不能用 MethodProxy.invokeSuper()
    • 代理与目标是两个对象,二者成员变量并不共用数据

  2. static 方法、final 方法、private 方法均无法增强

    • 进一步理解代理增强基于方法重写

@Value 装配底层

@Value 装配过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
@Configuration
@SuppressWarnings("all")
public class Test {

public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test.class);
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

// 作用之一:解析@Value
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
resolver.setBeanFactory(beanFactory);

// test1(context, resolver, Bean1.class.getDeclaredField("home"));
// test2(context, resolver, Bean1.class.getDeclaredField("age"));
// test3(context, resolver, Bean2.class.getDeclaredField("bean3"));
test3(context, resolver, Bean4.class.getDeclaredField("value"));
}

private static void test3(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
DependencyDescriptor descriptor = new DependencyDescriptor(field, false);
// 获取 @Value 的内容
String value = resolver.getSuggestedValue(descriptor).toString();
System.out.println(value);

// 解析 ${}
value = context.getEnvironment().resolvePlaceholders(value);
System.out.println(value);
System.out.println(value.getClass());

// 解析 #{} @bean3
Object bean3 = context.getBeanFactory().getBeanExpressionResolver().evaluate(value, new BeanExpressionContext(context.getBeanFactory(), null));

// 类型转换
Object result = context.getBeanFactory().getTypeConverter().convertIfNecessary(bean3, descriptor.getDependencyType());
System.out.println(result);
}

private static void test2(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
DependencyDescriptor descriptor = new DependencyDescriptor(field, false);
// 获取 @Value 的内容
String value = resolver.getSuggestedValue(descriptor).toString();
System.out.println(value);

// 解析 ${}
value = context.getEnvironment().resolvePlaceholders(value);
System.out.println(value);
System.out.println(value.getClass());
Object age = context.getBeanFactory().getTypeConverter().convertIfNecessary(value, descriptor.getDependencyType());
System.out.println(age.getClass());
}

private static void test1(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
DependencyDescriptor descriptor = new DependencyDescriptor(field, false);
// 获取 @Value 的内容
String value = resolver.getSuggestedValue(descriptor).toString();
System.out.println(value);

// 解析 ${}
value = context.getEnvironment().resolvePlaceholders(value);
System.out.println(value);
}

public class Bean1 {
@Value("${JAVA_HOME}")
private String home;
@Value("18")
private int age;
}

public class Bean2 {
@Value("#{@bean3}") // SpringEL #{SpEL}
private Bean3 bean3;
}

@Component("bean3")
public class Bean3 {
}

static class Bean4 {
@Value("#{'hello, ' + '${JAVA_HOME}'}")
private String value;
}
}

小结💡

  1. ContextAnnotationAutowireCandidateResolver 作用之一,获取 @Value 的值
  2. 了解 ${ } 对应的解析器
  3. 了解 #{ } 对应的解析器
  4. TypeConvert 的一项体现

@Autowired 装配底层

按类型装配的步骤

  1. 查看需要的类型是否为 Optional,是,则进行封装(非延迟),否则向下走
  2. 查看需要的类型是否为 ObjectFactory 或 ObjectProvider,是,则进行封装(延迟),否则向下走
  3. 查看需要的类型(成员或参数)上是否用 @Lazy 修饰,是,则返回代理,否则向下走
  4. 解析 @Value 的值
    1. 如果需要的值是字符串,先解析 ${ },再解析 #{ }
    2. 不是字符串,需要用 TypeConverter 转换
  5. 看需要的类型是否为 Stream、Array、Collection、Map,是,则按集合处理,否则向下走
  6. 在 BeanFactory 的 resolvableDependencies 中找有没有类型合适的对象注入,没有向下走
  7. 在 BeanFactory 及父工厂中找类型匹配的 bean 进行筛选,筛选时会考虑 @Qualifier 及泛型
  8. 结果个数为 0 抛出 NoSuchBeanDefinitionException 异常
  9. 如果结果 > 1,再根据 @Primary 进行筛选
  10. 如果结果仍 > 1,再根据成员名或变量名进行筛选
  11. 结果仍 > 1,抛出 NoUniqueBeanDefinitionException 异常

@Autowired 装配过程

doResolveDependency的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@Configuration
public class Test_1 {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test_1.class);
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 1. 根据成员变量的类型注入
DependencyDescriptor dd1 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);
System.out.println(beanFactory.doResolveDependency(dd1, "bean1", null, null));
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 2. 根据参数的类型注入
Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), false);
System.out.println(beanFactory.doResolveDependency(dd2, "bean1", null, null));
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 3. 结果包装为 Optional<Bean2>
DependencyDescriptor dd3 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean3"), false);
if (dd3.getDependencyType() == Optional.class) {
dd3.increaseNestingLevel();
Object result = beanFactory.doResolveDependency(dd3, "bean1", null, null);
System.out.println(Optional.ofNullable(result));
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 4. 结果包装为 ObjectProvider,ObjectFactory
DependencyDescriptor dd4 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean4"), false);
if (dd4.getDependencyType() == ObjectFactory.class) {
dd4.increaseNestingLevel();
ObjectFactory<Object> objectFactory = new ObjectFactory() {
@Override
public Object getObject() throws BeansException {
return beanFactory.doResolveDependency(dd4, "bean1", null, null);
}
};
System.out.println(objectFactory.getObject());
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 5. 对 @Lazy 的处理
DependencyDescriptor dd5 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
resolver.setBeanFactory(beanFactory);
Object proxy = resolver.getLazyResolutionProxyIfNecessary(dd5, "bean1");
System.out.println(proxy);
System.out.println(proxy.getClass());
}

static class Bean1 {
@Autowired
@Lazy
private Bean2 bean2;

@Autowired
public void setBean2(Bean2 bean2) {
this.bean2 = bean2;
}

@Autowired
private Optional<Bean2> bean3;
@Autowired
private ObjectFactory<Bean2> bean4;
}

@Component("bean2")
static class Bean2 {
}
}

特殊类型装配过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
@Configuration
public class Test_2 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test_2.class);
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 1. 数组类型");
testArray(beanFactory);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 2. List 类型");
testList(beanFactory);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 3. applicationContext");
testApplicationContext(beanFactory);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 4. 泛型");
testGeneric(beanFactory);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 5. @Qualifier");
testQualifier(beanFactory);
}

private static void testQualifier(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
DependencyDescriptor dd5 = new DependencyDescriptor(Target.class.getDeclaredField("service"), true);
Class<?> type = dd5.getDependencyType();
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
resolver.setBeanFactory(beanFactory);
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);
// @Qualifier("service2")
if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd, name), dd5)) {
System.out.println(name);
System.out.println(dd5.resolveCandidate(name, type, beanFactory));
}
}
}

private static void testGeneric(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
DependencyDescriptor dd4 = new DependencyDescriptor(Target.class.getDeclaredField("dao"), true);
Class<?> type = dd4.getDependencyType(); // 获取外层信息,不包括泛型信息
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
resolver.setBeanFactory(beanFactory);
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);
// 对比 BeanDefinition 与 DependencyDescriptor 的泛型是否匹配
if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd, name), dd4)) {
System.out.println(name);
System.out.println(dd4.resolveCandidate(name, type, beanFactory));
}
}
}

private static void testApplicationContext(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException, IllegalAccessException {
DependencyDescriptor dd3 = new DependencyDescriptor(Target.class.getDeclaredField("applicationContext"), true);

Field resolvableDependencies = DefaultListableBeanFactory.class.getDeclaredField("resolvableDependencies");
resolvableDependencies.setAccessible(true);
Map<Class<?>, Object> dependencies = (Map<Class<?>, Object>) resolvableDependencies.get(beanFactory);
// dependencies.forEach((k, v) -> System.out.println("key:" + k + " value: " + v));
for (Map.Entry<Class<?>, Object> entry : dependencies.entrySet()) {
// 左边类型 右边类型
if (entry.getKey().isAssignableFrom(dd3.getDependencyType())) {
System.out.println(entry.getValue());
break;
}
}
}

private static void testList(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
DependencyDescriptor dd2 = new DependencyDescriptor(Target.class.getDeclaredField("serviceList"), true);
if (dd2.getDependencyType() == List.class) {
Class<?> resolve = dd2.getResolvableType().getGeneric().resolve();
System.out.println(resolve);
List<Object> list = new ArrayList<>();
String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, resolve);
for (String name : names) {
Object bean = dd2.resolveCandidate(name, resolve, beanFactory);
list.add(bean);
}
System.out.println(list);
}
}

private static void testArray(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
DependencyDescriptor dd1 = new DependencyDescriptor(Target.class.getDeclaredField("serviceArray"), true);
if (dd1.getDependencyType().isArray()) {
Class<?> componentType = dd1.getDependencyType().getComponentType(); // 获取数组中的元素类型
System.out.println(componentType);
// 根据Bean的名字在当前容器以及祖先容器中寻找
String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, componentType);
List<Object> beans = new ArrayList<>();
for (String name : names) {
System.out.println(name);
Object bean = dd1.resolveCandidate(name, componentType, beanFactory);
beans.add(bean);
}
Object array = beanFactory.getTypeConverter().convertIfNecessary(beans, dd1.getDependencyType());
System.out.println(array);
}
}

static class Target {
@Autowired
private Service[] serviceArray;
@Autowired
private List<Service> serviceList;
@Autowired
private ConfigurableApplicationContext applicationContext;
@Autowired
private Dao<Teacher> dao;
@Autowired
@Qualifier("service2")
private Service service;
}

interface Dao<T> {

}

@Component("dao1")
static class Dao1 implements Dao<Student> {
}

@Component("dao2")
static class Dao2 implements Dao<Teacher> {
}

static class Student {

}

static class Teacher {

}

interface Service {

}

@Component("service1")
static class Service1 implements Service {

}

@Component("service2")
static class Service2 implements Service {

}

@Component("service3")
static class Service3 implements Service {

}
}

多个Bean注入优先级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@Configuration
public class Test_3 {
public static void main(String[] args) throws NoSuchFieldException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test_3.class);
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
testPrimary(beanFactory);
testDefault(beanFactory);
}

private static void testDefault(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
DependencyDescriptor dd = new DependencyDescriptor(Target2.class.getDeclaredField("service3"), false);
Class<?> type = dd.getDependencyType();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
// 多个Bean为指定的时候,根据成员变量的名字与BeanName进行匹配注入
// bean的名字 成员变量的变量名
if (name.equals(dd.getDependencyName())) {
System.out.println(name);
}
}
}

private static void testPrimary(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
DependencyDescriptor dd = new DependencyDescriptor(Target1.class.getDeclaredField("service"), false);
Class<?> type = dd.getDependencyType();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
if (beanFactory.getMergedBeanDefinition(name).isPrimary()) {
System.out.println(name);
}
}
}

static class Target1 {
@Autowired
private Service service;
}

static class Target2 {
@Autowired
private Service service3;
}

interface Service {

}

@Component("service1")
static class Service1 implements Service {

}

@Component("service2")
static class Service2 implements Service {

}

@Component("service3")
static class Service3 implements Service {

}
}

小结💡

  1. @Autowired 本质上是根据成员变量或方法参数的类型进行装配
  2. 如果待装配类型是 Optional,需要根据 Optional 泛型找到 bean,再封装为 Optional 对象装配
  3. 如果待装配的类型是 ObjectFactory,需要根据 ObjectFactory 泛型创建 ObjectFactory 对象装配
    • 此方法可以延迟 bean 的获取
  4. 如果待装配的成员变量或方法参数上用 @Lazy 标注,会创建代理对象装配
    • 此方法可以延迟真实 bean 的获取
    • 被装配的代理不作为 bean
  5. 如果待装配类型是数组,需要获取数组元素类型,根据此类型找到多个 bean 进行装配
  6. 如果待装配类型是 Collection 或其子接口,需要获取 Collection 泛型,根据此类型找到多个 bean
  7. 如果待装配类型是 ApplicationContext 等特殊类型
    • 调用org.springframework.context.support.AbstractApplicationContext#refresh方法的org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory方法进行添加特殊的Bean存储至org.springframework.beans.factory.support.DefaultListableBeanFactory.resolvableDependencies
    • 会在 BeanFactory 的 resolvableDependencies 成员按类型查找装配
    • resolvableDependencies 是 map 集合,key 是特殊类型,value 是其对应对象
    • 不能直接根据 key 进行查找,而是用 isAssignableFrom 逐一尝试右边类型是否可以被赋值给左边的 key 类型
  8. 如果待装配类型有泛型参数
    • 需要利用 ContextAnnotationAutowireCandidateResolver 按泛型参数类型筛选
  9. 如果待装配类型有 @Qualifier
    • 需要利用 ContextAnnotationAutowireCandidateResolver 按注解提供的 bean 名称筛选
  10. 有 @Primary 标注的 @Component 或 @Bean 的处理
  11. 与成员变量名或方法参数名同名 bean 的处理

事件监听器

实现ApplicationListener接口监听事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@Configuration
public class Test_1 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test_1.class);
context.getBean(MyService.class).doBusiness();
context.close();
}

static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}

@Component
static class MyService {
private static final Logger log = LoggerFactory.getLogger(MyService.class);
@Autowired
private ApplicationEventPublisher publisher; // applicationContext

public void doBusiness() {
log.info("主线业务");
// 主线业务完成后需要做一些支线业务,下面是问题代码
publisher.publishEvent(new MyEvent("MyService.doBusiness()"));
}
}

@Component
static class SmsApplicationListener implements ApplicationListener<MyEvent> {
private static final Logger log = LoggerFactory.getLogger(SmsApplicationListener.class);

@Override
public void onApplicationEvent(MyEvent event) {
log.info("发送短信");
}
}

@Component
static class EmailApplicationListener implements ApplicationListener<MyEvent> {
private static final Logger log = LoggerFactory.getLogger(EmailApplicationListener.class);

@Override
public void onApplicationEvent(MyEvent event) {
log.info("发送邮件");
}
}

}

使用EventListener注解监听事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@Configuration
public class Test_2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test_2.class);
context.getBean(MyService.class).doBusiness();
context.close();
}

static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}

@Component
static class MyService {
private static final Logger log = LoggerFactory.getLogger(MyService.class);
@Autowired
private ApplicationEventPublisher publisher; // applicationContext

public void doBusiness() {
log.debug("主线业务");
// 主线业务完成后需要做一些支线业务,下面是问题代码
publisher.publishEvent(new MyEvent("MyService.doBusiness()"));
}
}

@Component
static class SmsService {
private static final Logger log = LoggerFactory.getLogger(SmsService.class);

@EventListener
public void listener(MyEvent myEvent) {
log.debug("发送短信");
}
}

@Component
static class EmailService {
private static final Logger log = LoggerFactory.getLogger(EmailService.class);

@EventListener
public void listener(MyEvent myEvent) {
log.debug("发送邮件");
}
}

@Bean
public ThreadPoolTaskExecutor executor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
return executor;
}

@Bean
public SimpleApplicationEventMulticaster applicationEventMulticaster(ThreadPoolTaskExecutor executor) {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(executor);
return multicaster;
}

}

EventListener注解解析原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
* CustomListener注解,模拟EventListener注解监听事件
*/
@Configuration
public class Test_3 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test_3.class);
context.getBean(CustomService.class).doBusiness();
context.close();
}

@Bean
public SmartInitializingSingleton smartInitializingSingleton(ConfigurableApplicationContext context) {
return () -> {
for (String name : context.getBeanDefinitionNames()) {
Object bean = context.getBean(name);
for (Method method : bean.getClass().getMethods()) {
if (method.isAnnotationPresent(CustomListener.class)) {
context.addApplicationListener((event) -> {
System.out.println(event);
Class<?> eventType = method.getParameterTypes()[0];// 监听器方法需要的事件类型,假设只有一个
if (eventType.isAssignableFrom(event.getClass())) {
try {
method.invoke(bean, event);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
}
};
}

@Component
static class CustomService {
private static final Logger log = LoggerFactory.getLogger(CustomService.class);
@Autowired
private ApplicationEventPublisher publisher; // applicationContext

public void doBusiness() {
log.info("主线业务");
// 主线业务完成后需要做一些支线业务,下面是问题代码
publisher.publishEvent(new CustomEvent("CustomService.doBusiness()"));
}
}

@Component
static class SmsService {
private static final Logger log = LoggerFactory.getLogger(SmsService.class);

@CustomListener
public void listener(CustomEvent customEvent) {
log.info("发送短信");
}
}

@Component
static class EmailService {
private static final Logger log = LoggerFactory.getLogger(EmailService.class);

@CustomListener
public void listener(CustomEvent customEvent) {
log.info("发送邮件");
}
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface CustomListener {
}

static class CustomEvent extends ApplicationEvent {
public CustomEvent(Object source) {
super(source);
}
}
}

小结💡

事件监听器的两种方式

  1. 实现 ApplicationListener 接口
    • 根据接口泛型确定事件类型
  2. @EventListener 标注监听方法
    • 根据监听器方法参数确定事件类型
    • 解析时机:在 SmartInitializingSingleton(所有单例初始化完成后),解析每个单例 bean

事件发布器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
@Configuration
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test.class);
context.getBean(CustomService.class).doBusiness();
context.close();
}

static class CustomEvent extends ApplicationEvent {
public CustomEvent(Object source) {
super(source);
}
}

@Component
static class CustomService {
private static final Logger log = LoggerFactory.getLogger(CustomService.class);
@Autowired
private ApplicationEventPublisher publisher; // applicationContext

public void doBusiness() {
log.info("主线业务");
// 主线业务完成后需要做一些支线业务,下面是问题代码
publisher.publishEvent(new CustomEvent("CustomService.doBusiness()"));
}
}

@Component
static class SmsApplicationListener implements ApplicationListener<CustomEvent> {
private static final Logger log = LoggerFactory.getLogger(SmsApplicationListener.class);

@Override
public void onApplicationEvent(CustomEvent event) {
log.info("发送短信");
}
}

@Component
static class EmailApplicationListener implements ApplicationListener<CustomEvent> {
private static final Logger log = LoggerFactory.getLogger(EmailApplicationListener.class);

@Override
public void onApplicationEvent(CustomEvent event) {
log.info("发送邮件");
}
}

@Bean
public ThreadPoolTaskExecutor executor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
return executor;
}

@Bean
public ApplicationEventMulticaster applicationEventMulticaster(ConfigurableApplicationContext context, ThreadPoolTaskExecutor executor) {
return new AbstractApplicationEventMulticaster() {
private final List<GenericApplicationListener> listeners = new ArrayList<>();

// 收集监听器
public void addApplicationListenerBean(String name) {
ApplicationListener listener = context.getBean(name, ApplicationListener.class);
System.out.println(listener);
// 获取该监听器支持的事件类型
ResolvableType type = ResolvableType.forClass(listener.getClass()).getInterfaces()[0].getGeneric();
System.out.println(type);

// 将原始的 listener 封装为支持事件类型检查的 listener
GenericApplicationListener genericApplicationListener = new GenericApplicationListener() {
// 是否支持某事件类型 真实的事件类型
public boolean supportsEventType(ResolvableType eventType) {
return type.isAssignableFrom(eventType);
}

public void onApplicationEvent(ApplicationEvent event) {
executor.submit(() -> listener.onApplicationEvent(event));
}
};

listeners.add(genericApplicationListener);
}

// 发布事件
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
for (GenericApplicationListener listener : listeners) {
if (listener.supportsEventType(ResolvableType.forClass(event.getClass()))) {
listener.onApplicationEvent(event);
}
}
}
};
}

abstract static class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster {

@Override
public void addApplicationListener(ApplicationListener<?> listener) {

}

@Override
public void addApplicationListenerBean(String listenerBeanName) {

}

@Override
public void removeApplicationListener(ApplicationListener<?> listener) {

}

@Override
public void removeApplicationListenerBean(String listenerBeanName) {

}

@Override
public void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate) {

}

@Override
public void removeApplicationListenerBeans(Predicate<String> predicate) {

}

@Override
public void removeAllListeners() {

}

@Override
public void multicastEvent(ApplicationEvent event) {

}

@Override
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {

}
}
}

小结💡

事件发布器模拟实现

  1. addApplicationListenerBean 负责收集容器中的监听器
    • 监听器会统一转换为 GenericApplicationListener 对象,以支持判断事件类型
  2. multicastEvent 遍历监听器集合,发布事件
    • 发布前先通过 GenericApplicationListener.supportsEventType 判断支持该事件类型才发事件
    • 可以利用线程池进行异步发事件优化
  3. 如果发送的事件对象不是 ApplicationEvent 类型,Spring 会把它包装为 PayloadApplicationEvent 并用泛型技术解析事件对象的原始类型

Spring
https://xiaoyu72.com/articles/6c92115f/
Author
XiaoYu
Posted on
May 27, 2022
Updated on
August 28, 2023
Licensed under