Spring原理学习笔记

学习来源:黑马程序员

学习时间:2023年11月4日

1 容器接口

1.1 BeanFactory和ApplicationContext

最重要的两个接口分别是BeanFactoryApplicationContext。关系如下:

image-20231104163116441

从这个类图我们可以大致看出BeanFactoryApplicationContext的关系,BeanFactoryApplicationContext的基类,BeanFactory所拥有的功能ApplicationContext都拥有,不仅如此,ApplicationContext还拓展了一些功能,它通过实现MessageSource、 ResourceLoader等接口,在BeanFactory简单IOC容器的基础上添加了许多高级容器的特征。

Spring的核心容器是BeanFactory

在SpringBoot的引导类中,run方法的返回值就是一个可配置的Spring容器

1
2
3
4
5
6
7
8
9
10
@SpringBootApplication
public class A01Application {

public static void main(String[] args) {
// 可配置的容器
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
// getBean方法在接口BeanFactory中定义
context.getBean("aaa");
}
}

BeanFactory接口中的方法:

image-20231104163930951

1.2 BeanFactory接口的功能

1.1节可以看到BeanFactory提供的方法只有getBean,而控制反转,依赖注入直到Bean的生命周期的各种功能,是由它的实现类来提供的。

BeanFactory的一个重要的实现类是DefaultListableBeanFactory,类图如下:

image-20231104165210071

其中DefaultListableBeanFactory的父类DefaultSingletonBeanRegistry管理着单例的对象:

1
2
3
4
5
6
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
// ...
// 一个map集合
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
// ...
}

准备两个bean,分别为Component1Component2

1
2
3
4
5
6
7
8
9
@Component
public class Component1 {

}

@Component
public class Component2 {

}

打印这两个bean的信息:

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

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

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
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(e -> e.getKey().startsWith("component"))
.forEach(e -> {
System.out.println(e.getKey() + "=" + e.getValue());
});
}
}

打印结果:

1
2
component1=com.hongyi.apiintegration.spring.Component1@3aa41da1
component2=com.hongyi.apiintegration.spring.Component2@74fab04a

1.3 ApplicationContext接口的功能

暂略

2 容器实现

2.1 BeanFactory接口实现

2.1.1 BeanFactoryPostProcessor

DefaultListableBeanFactory类是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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// bean的定义(definition: class,scope,初始化和销毁等)
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();

// 将bean注册到beanFactory中,注意,只是将bean的信息放进容器里,并未实际在内存中创建出bean对象(实例化)
// param1: bean的名称; param2: bean的定义
beanFactory.registerBeanDefinition("config", beanDefinition);
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println("--------------");
}

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

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

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

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

@Autowired
private Bean2 bean2;

public Bean2 getBean2() {
return bean2;
}
}

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

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

打印信息:

1
config

发现只有一个config的bean,而没有bean1和bean2的bean。原因在于BeanFactory并没有提供解析注解的功能,而是需要一些后处理器来完成。

1
2
3
4
5
// 给BeanFactory添加一些常用的后处理器(原始的BeanFactory没有解析注解的功能)
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}

打印结果:

1
2
3
4
5
6
7
8
9
config
// 解析Component,Bean注解,属于BeanFactory的后处理器
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
// 解析Autowired注解,属于Bean的后处理器
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
// 解析Resource注解,属于Bean的后处理器
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory

可以看到容器中多出了几个后处理器的bean,主要分为两类:

  • BeanFactory后处理器,BeanFactoryPostProcessor,用于解析Component,Bean注解
  • Bean后处理器,BeanPostProcessor,用于解析Autowired,Resource注解

其中internalConfigurationAnnotationProcessor后处理器用于解析注解,例如@Configuration@Bean等。

现在执行这些后处理器:

1
2
3
4
5
6
7
8
9
// getBeansOfType: 根据bean的类型获取bean,返回一个map
// 这里根据bean工厂的后处理器BeanFactoryPostProcessor的类型来获取bean
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
// 对每一个后处理器进行执行
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}

打印结果:

1
2
3
4
5
6
7
8
config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2

可以看到bean1和bean2的定义被加载进了容器当中。

2.1.2 BeanPostProcessor

现在执行:

1
System.out.println(beanFactory.getBean(Bean1.class).getBean2());

打印结果:

1
2
3
4
14:56:08.360 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
14:56:08.363 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
14:56:08.383 [main] DEBUG com.hongyi.apiintegration.spring.TestBeanFactory$Bean1 - 构造 Bean1()
null

bean1中注入了bean2,但是打印时却为null,说明@Autowired注解并未生效。同样需要后处理器来执行,这里后处理器的类型为BeanPostProcessor

1
2
3
4
// bean的后处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowired @Resource
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

System.out.println(beanFactory.getBean(Bean1.class).getBean2());

打印结果:

image-20231115150751734

2.1.3 即时加载和延时加载

此外注意到,目前bean对象的产生是延时的(懒加载):当调用到getBean()时,才调用构造方法,创建出对象。

如果要实现即时加载(饿加载):

1
2
3
4
// 准备好所有的单例对象
beanFactory.preInstantiateSingletons();
System.out.println("--------------");
System.out.println(beanFactory.getBean(Bean1.class).getBean2());

打印结果:

image-20231115151635209

可以看到,在执行beanFactory.getBean(Bean1.class).getBean2()之前,容器就为我们创建好了bean1和bean2的实例对象。

2.1.4 小结

BeanFactory接口不会做的事:

  • 不会主动添加和调用BeanFactory后处理器
  • 不会主动添加和调用Bean后处理器
  • 不会主动初始化单例对象,需要显式调用preInstantiateSingletons()
  • 不会解析${}#{}

2.2 ApplicationContext接口实现

ApplicationContext接口实现类主要有:

  • ClassPathXmlApplicationContext类:基于classpath下xml格式的配置文件来创建容器
  • FileSystemXmlApplicationContext类:基于磁盘路径下xml格式的配置文件来创建容器
  • AnnotationConfigApplicationContext类:基于java配置类来创建
  • AnnotationConfigServletWebServerApplicationContext:基于java配置类来创建,用于web环境

2.2.1 ClassPathXmlApplicationContext

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
@Slf4j
public class A02Application {
public static void main(String[] args) {
testClassPathXmlApplicationContext();
}

private static void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}

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

static class Bean1 {

}

static class Bean2 {
// 依赖注入
private Bean1 bean1;

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

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

resources创建配置文件b01.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="bean1" class="com.hongyi.apiintegration.spring.a02.A02Application.Bean1"/>

<bean id="bean2" class="com.hongyi.apiintegration.spring.a02.A02Application.Bean2">
<property name="bean1" ref="bean1"/>
</bean>

</beans>

打印结果:

image-20231115153625619

2.2.2 FileSystemXmlApplicationContext

2.2.3 AnnotationConfigApplicationContext

以Java配置类(以@Configuration标注)的形式替代了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
36
37
38
39
40
41
42
43
44
45
46
47
@Slf4j
public class A02Application {
public static void main(String[] args) {
testAnnotationConfigApplicationContext();
}

private static void testAnnotationConfigApplicationContext() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}

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

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

static class Bean1 {

}

static class Bean2 {
// 依赖注入
@Autowired
private Bean1 bean1;

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

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

打印结果:

1
2
3
4
5
6
7
8
9
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
a02Application.Config
bean1
bean2
com.hongyi.apiintegration.spring.a02.A02Application$Bean1@1fb700ee

可以看到,容器中有后处理器的定义,还有配置类Config的定义,以及bean1和bean2的定义,依赖注入也成功了。

2.2.4 AnnotationConfigServletWebServerApplicationContext

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
@Slf4j
public class A02Application {
public static void main(String[] args) {
testAnnotationConfigServletWebServerApplicationContext();
}

private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);

}

@Configuration
static class WebConfig {
// 三个必要的组件:(1)内嵌web容器 (2)前端控制器 (3)联系web容器和前端控制器的注册器
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory(); // 内嵌tomcat容器
}

@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}

@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
// 将dispatcherServlet注册到web容器中(这里是tomcat)
// 拦截路径设置为 /
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}

// 分发请求,相当于controller
@Bean("/hello")
public Controller controller1() {
return (request, response) -> {
response.getWriter().print("Hello World!");
return null;
};
}
}
}

访问结果:

image-20231115160137385

3 Bean的生命周期

3.1 四个阶段

按顺序为:

  1. 实例化(构造)
  2. 依赖注入
  3. 初始化
  4. 销毁

在每个阶段的前后,都可以添加一些Bean后处理器(BeanPostProcessor)来进行功能增强。

  • 启动类
1
2
3
4
5
6
7
8
@SpringBootApplication
public class A03Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A03Application.class, args);
// 关闭容器
context.close();
}
}
  • 一个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
@Slf4j
@Component
public class LifeCycleBean {
// 方法执行顺序:从上到下

// 1.构造(或者 实例化)
public LifeCycleBean() {
log.info("构造");
}

// 2.依赖注入
@Autowired
public void autowire(@Value("${JAVA_HOME}") String home) {
log.info("依赖注入:{}", home);
}

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

// 4.销毁
@PreDestroy
public void destroy() {
log.info("销毁");
}
}

执行结果:

image-20231115164607739

  • 添加后处理器
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
@Slf4j
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

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

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

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

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

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

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

执行结果:

image-20231115164703701

3.2 模板方法

模板方法设计模式详见[[Java设计模式学习笔记新#6.1 模板方法模式]]

Bean后处理器采用了模板方法的设计模式。

  • BeanPostProcessor接口
1
2
3
public interface BeanPostProcessor {
void inject(Object bean);
}
  • MyBeanFactory
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
public class MyBeanFactory {
// 后处理器的集合
private List<BeanPostProcessor> processors = new ArrayList<>();

public Object getBean() {
Object bean = new Object();
System.out.println("构造 " + bean);
System.out.println("依赖注入 " + bean);
// 调用后处理器,不变
for (BeanPostProcessor processor : processors) {
processor.inject(bean);
}
System.out.println("初始化 " + bean);
return bean;
}

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

public static void main(String[] args) {
MyBeanFactory beanFactory = new MyBeanFactory();
// 添加后处理器,可变
beanFactory.addBeanPostProcessors(bean -> {
System.out.println("解析@Autowired");
});
beanFactory.addBeanPostProcessors(bean -> {
System.out.println("解析@Resource");
});
Object bean = beanFactory.getBean();
System.out.println(bean);
}
}

打印结果:

1
2
3
4
5
构造 java.lang.Object@6433a2
依赖注入 java.lang.Object@6433a2
解析@Autowired
初始化 java.lang.Object@6433a2
java.lang.Object@6433a2

4 Bean后处理器

Bean后处理器(BeanPostProcessor)的作用:为Bean的生命周期的各个阶段(实例化,依赖注入,初始化和销毁)提供扩展。

4.1 常见的Bean后处理器

  • AutowiredAnnotationBeanPostProcessor:解析@Autowired@Value注解(涉及依赖注入)
  • CommonAnnotationBeanPostProcessor:解析@Resource@PostConstruct@PreDestroy注解(涉及依赖注入,初始化和销毁)
  • ConfigurationPropertiesBindingPostProcessor:解析@ConfigurationProperties注解(涉及依赖注入)

代码演示

  • Bean1.java
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
@Slf4j
public class Bean1 {

private Bean2 bean2;

@Autowired
public void setBean2(Bean2 bean2) {
log.info("@Autowired生效: {}", bean2);
this.bean2 = bean2;
}

private Bean3 bean3;

@Resource
public void setBean3(Bean3 bean3) {
log.info("@Resource生效: {}", bean3);
this.bean3 = bean3;
}

private String home;

@Autowired
public void setHome(@Value("${JAVA_HOME}") String home) {
log.info("@Value生效: {}", home);
this.home = home;
}
}
  • Bean2Bean3
1
2
3
public class Bean2 {

}
  • Bean4.java
1
2
3
4
5
6
7
@ConfigurationProperties(prefix = "java")
@Data
public class Bean4 {
private String home;

private String version;
}
  • 测试
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
public class A04Application {

public static void main(String[] args) {
// 一个干净的容器
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

// 初始化容器
// 执行BeanFactory后处理器,添加Bean后处理器,实例化并初始化所有单例
context.refresh();

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

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

}

GenericApplicationContext基本就是对DefaultListableBeanFactory 做了个简易的封装,几乎所有方法都是使用了DefaultListableBeanFactory的方法去实现。

执行结果:

image-20231115210846879

4.2 AutowiredAnnotationBeanPostProcessor流程解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class DigInAutowired {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 注册两个bean
beanFactory.registerSingleton("bean2", new Bean2());
beanFactory.registerSingleton("bean3", new Bean3());

beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());

AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(beanFactory); // 关联BeanFactory和后处理器

Bean1 bean1 = new Bean1();
System.out.println(bean1);
// 执行依赖注入(解析@Autowired)
// 查找哪些属性,方法加上了@Autowired,并将这些信息封装为InjectionMetaData
processor.postProcessProperties(null, bean1, "bean1");
System.out.println(bean1);
}
}

打印结果:

image-20231115213006356

可以看到,在未添加bean后处理器时打印bean1,bean1依赖的bean2和home为null,当添加AutowiredAnnotationBeanPostProcessor类型的后处理器后再次打印,可以看到bean2和home被成功注入,bean3为@Resource注解,因此未能被注入。

postProcessProperties方法源码:

1
2
3
4
5
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 调用findAutowiringMetadata,该方法查找bean定义中哪些属性和方法上加上了@Autowired注解,并将其封装为InjectionMetadata类型的数据
InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);
// ...
}

现在使用反射来执行私有方法findAutowiringMetadata

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DigInAutowired {
public static void main(String[] args) throws Throwable {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2());
beanFactory.registerSingleton("bean3", new Bean3());

beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());

AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(beanFactory);

Bean1 bean1 = new Bean1();
Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
findAutowiringMetadata.setAccessible(true);
// 调用invoke来执行findAutowiringMetadata方法
InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);
System.out.println(metadata); // 断点
// 调用InjectionMetadata的inject方法进行依赖注入,注入时按照类型进行查找
metadata.inject(bean1, "bean1", null);
System.out.println(bean1);
}
}

image-20231115215300571

invoke执行后,后处理器将@Autowired注解的属性和方法中的参数封装为InjectionMetadata,可以看到InjectionMetadata metaData是一个ArrayList类型的数组,包含两个元素,即是Bean1中的@Autowired依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private Bean2 bean2;

private String home;

@Autowired
public void setBean2(Bean2 bean2) {
log.info("@Autowired生效: {}", bean2);
this.bean2 = bean2;
}

@Autowired
public void setHome(@Value("${JAVA_HOME}") String home) {
log.info("@Value生效: {}", home);
this.home = home;
}

执行inject时,容器会从中寻找Bean2类型和String类型的bean,完成这些bean的实例化,打印结果为:

image-20231115215727524

那么Spring容器是如何按照类型从容器中来查找Bean1所依赖的bean呢?这涉及到inject方法的源码,由于源代码实现太过复杂,这里通过反射进行模拟实现:

对于Bean1:

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

private Bean2 bean2;

// 放在方法上
@Autowired
public void setBean2(Bean2 bean2) {
log.info("@Autowired生效: {}", bean2);
this.bean2 = bean2;
}

// 放在成员变量上
@Autowired
private Bean3 bean3;
}

测试:

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 DigInAutowired {
public static void main(String[] args) throws Throwable {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2());
beanFactory.registerSingleton("bean3", new Bean3());

// inject: 按照类型查找bean
// 下面就是inject做的事情:

// 情况1:当@Autowired注解放在属性(成员变量上时)
Field bean3 = Bean1.class.getDeclaredField("bean3");
// required=false表示,当没找到Bean3类型的bean时,返回null
// required=true表示,当没找到Bean3类型的bean时,抛出异常
DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
Object o1 = beanFactory.doResolveDependency(dd1, null, null, null);
System.out.println(o1);

// 情况2:当@Autowired注解放在方法上时
Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
// 0表示第0个参数
DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), false);
Object o2 = beanFactory.doResolveDependency(dd2, null, null, null);
System.out.println(o2);
}
}

打印结果:可见被成功注入了。

1
2
com.hongyi.apiintegration.spring.a04.Bean3@55f3ddb1
com.hongyi.apiintegration.spring.a04.Bean2@2b98378d

如果去掉beanFactory.registerSingleton("bean3", new Bean3());,即不注册Bean3类型的bean,则:

1
2
null
com.hongyi.apiintegration.spring.a04.Bean2@d8355a8

再者,如果设置DependencyDescriptortrue

1
DependencyDescriptor dd1 = new DependencyDescriptor(bean3, true);

则结果为:

image-20231115221918477

抛出NoSuchBeanDefinitionException的异常。

5 BeanFactory后处理器

BeanFactory后处理器(BeanFactoryPostProcessor)的作用:为BeanFactory做扩展,例如解析注解@ComponentScan@Bean等,将这些被注解标注了的类和方法转换为BeanDefinition注册到容器中。

5.0 BeanDefinition

BeanDefinition是定义 Bean 的配置元信息接口,存储Bean的相关信息,主要包括:Bean的属性、是否单例、延迟加载、Bean的名称、构造方法等。

  • Spring中每一个被扫描到的bean都会生成一个BeanDefinition
  • BeanDefinition的主要作用是为了在只解析一次类的情况下,最大程度的拿到这类的信息。防止重复解析导致效率变低。

该接口派生出 AnnotatedBeanDefinition 接口,以及常用子类 RootBeanDefinitionGenericBeanDefinition

可以使用 BeanDefinitionBuildernew BeanDefinition 实现类构建 BeanDefinition 对象。

5.1 常见的BeanFactory后处理器

  • ConfigurationClassPostProcessor:解析@ComponentScan@Bean@Import@ImportResource
  • MapperScannerConfigurer:解析@Mapper,将接口转换为对象

代码示例

image-20231115225608473

  • Bean1
1
2
3
4
5
public class Bean1 {
public Bean1() {
log.info("我被 Spring 管理了");
}
}
  • Bean2
1
2
3
4
5
6
@Component
public class Bean2 {
public Bean2() {
log.info("我被 Spring 管理了");
}
}
  • Mapper1接口
1
2
3
@Mapper
public interface Mapper1 {
}
  • Config
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
@Configuration
@ComponentScan("com.hongyi.apiintegration.spring.a05.component")
public class Config {

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

// 存在依赖注入
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}

@Bean(initMethod = "init")
public DruidDataSource druidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("12345678");
return dataSource;
}
}
  • 测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Slf4j
public class A05Application {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);

// 初始化容器(执行BeanFactory后处理器,Bean后处理器,实例化单例bean)
context.refresh();

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

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

没有配置BeanFactory后处理器时:

image-20231115225849368

配置后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Slf4j
public class A05Application {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);

// 解析@ComponentScan @Bean @Import @ImportResource
context.registerBean(ConfigurationClassPostProcessor.class);
// 解析@Mapper
context.registerBean(MapperScannerConfigurer.class, bd -> {
bd.getPropertyValues().add("basePackage", "com.hongyi.apiintegration.spring.a05.mapper");
});

// 初始化容器(执行BeanFactory后处理器,Bean后处理器,实例化单例bean)
context.refresh();

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

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

image-20231115230034325

image-20231115230228471

打印的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
org.mybatis.spring.mapper.MapperScannerConfigurer
bean2
bean1
sqlSessionFactoryBean
druidDataSource
mapper1
mapper2
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory

5.2 @ComponentScan流程解析

这里对@ComponentScan注解的流程进行模拟实现,并抽取成一个自定义的BeanFactory后处理器。

  • bean的准备:位于包com.hongyi.apiintegration.spring.a05.component
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 Bean2 {
public Bean2() {
log.info("我被 Spring 管理了");
}
}

// @Controller是@Component的派生注解,此外还有@Repository和@Service
@Controller
@Slf4j
public class Bean3 {
public Bean3() {
log.info("我被 Spring 管理了");
}
}

// 注意Bean4没有注解
public class Bean4 {
public Bean4() {
log.info("我被 Spring 管理了");
}
}
  • 模拟@Component注解的实现

步骤:

  1. 查看配置类是否有@ComponentScan注解
  2. 获取@ComponentScanvalue属性(可能有多个),即扫描的包com.hongyi.apiintegration.spring.a05.component遍历每个包
  3. 遍历该包下的所有类,查看每一个类是否有@Component注解或者派生注解修饰
  4. 如果有修饰,则根据该类的信息构造BeanDefinition,然后注册到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
@Slf4j
public class A05Application {
public static void main(String[] args) throws IOException {
GenericApplicationContext context = new GenericApplicationContext();
// 注册配置类
context.registerBean("config", Config.class);

// 1.查看配置是否有@ComponentScan注解
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null) {
// 2.遍历每个包
for (String p : componentScan.basePackages()) {
System.out.println(p);
// 将包名转换为标准的Resource路径
// com.hongyi.apiintegration.spring.a05.component -> classpath*:com/hongyi/apiintegration/spring/a05/component/**/*.class
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
System.out.println(path);
// 转换为Resource资源
Resource[] resources = context.getResources(path);
// factory用于解析Resource资源,获取对应的类信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
// BeanName的构造器
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
// 3.遍历每一个Resource,即各个类,查看是否有@Component注解
for (Resource resource : resources) {
MetadataReader reader = factory.getMetadataReader(resource);
// 有@Component注解或派生注解
if (reader.getAnnotationMetadata().hasAnnotation(Component.class.getName())
|| reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())) {
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
// 构造bean的定义
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
// 构造bean的名字
String beanName = generator.generateBeanName(beanDefinition, beanFactory);
// 将bean注册到工厂中
beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}

// 初始化容器(执行BeanFactory后处理器,Bean后处理器,实例化单例bean)
context.refresh();

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

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

打印结果:

image-20231116132515177

现在封装成ComponentScanPostProcessor

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
public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {
// context.refresh()时调用
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
try {
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null) {
for (String p : componentScan.basePackages()) {
System.out.println(p);
// com.hongyi.apiintegration.spring.a05.component -> classpath*:com/hongyi/apiintegration/spring/a05/component/**/*.class
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
System.out.println(path);
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (Resource resource : resources) {
MetadataReader reader = factory.getMetadataReader(resource);
if (reader.getAnnotationMetadata().hasAnnotation(Component.class.getName())
|| reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())) {
// bean的定义
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) {
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
// bean的名字
String beanName = generator.generateBeanName(beanDefinition, beanFactory);
// 将bean注册到工厂中
beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

将该后处理器注册到容器中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Slf4j
public class A05Application {
public static void main(String[] args) throws IOException {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// 注册自定义的BeanFactory后处理器
context.registerBean(ComponentScanPostProcessor.class);

// 初始化容器(执行BeanFactory后处理器,Bean后处理器,实例化单例bean)
context.refresh();

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

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

打印结果:

image-20231116133606672

5.3 @Bean流程解析

对配置类Config中每一个被标注了@Bean的方法进行遍历,然后构造BeanDefinition注册到容器中。

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
public class AtBeanPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
try {
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/hongyi/apiintegration/spring/a05/Config.class"));
// 被@Bean注解标注的方法
Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
// 遍历每一个方法,生成一个BeanDefinition
for (MethodMetadata method : methods) {
System.out.println(method);
Map<String, Object> annotationAttributes = method.getAnnotationAttributes(Bean.class.getName());
String initMethod = (String) annotationAttributes.get("initMethod");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(method.getMethodName(), "config");
// 设置 按照构造方法进行依赖注入
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
if (initMethod.length() > 0) {
builder.setInitMethodName(initMethod);
}
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
// 以方法名作为bean的名字
if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) {
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
beanFactory.registerBeanDefinition(method.getMethodName(), beanDefinition);
}

}
} catch (IOException e) {
e.printStackTrace();
}
}
}

将这个后处理器注册到容器中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Slf4j
public class A05Application {
public static void main(String[] args) throws IOException {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// 注册自定义的BeanFactory后处理器
context.registerBean(ComponentScanPostProcessor.class);
context.registerBean(AtBeanPostProcessor.class);

// 初始化容器(执行BeanFactory后处理器,Bean后处理器,实例化单例bean)
context.refresh();

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

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

执行结果:

image-20231116151055572

5.4 @Mapper流程解析

暂略

6 Aware接口

  • Aware接口提供了一种内置的注入手段,可以注入BeanFactoryApplicationContext
  • InitializingBean接口提供了一种内置的初始化手段
  • 内置的注入和初始化不受扩展功能的影响,总是会被执行

6.1 常见接口

  • BeanNameAware接口:注入bean的名字
  • BeanFactoryAware接口:注入BeanFactory容器
  • ApplicationContextAware接口:注入ApplicationContext容器
  • EmbeddedValueResolverAware接口:解析${}#{}

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Slf4j
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
// 执行时机:依赖注入阶段
@Override
public void setBeanName(String name) {
log.info("当前的bean" + this + " 名字是: " + name);
}
// 执行时机:依赖注入阶段
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("当前的bean" + this + " 容器是: " + applicationContext);
}
// 执行时机:初始化阶段
@Override
public void afterPropertiesSet() {
log.info("当前的bean" + this + " 初始化");
}
}

测试:

1
2
3
4
5
6
7
8
public class A06Application {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myBean", MyBean.class);
context.refresh();
context.close();
}
}

执行结果:

image-20231116154000646


这些接口与@Autowired@PostConstruct的不同:

  • @Autowired@PostConstruct需要使用BeanPostProcessor后处理器,属于扩展功能
  • Aware接口属于内置功能,在某些情况下,扩展功能会失效,而内置功能不会失效

代码示例

MyBean增加代码:

1
2
3
4
5
6
7
8
9
@Autowired
public void aaa(ApplicationContext applicationContext) {
log.info("当前的bean" + this + " 使用@Autowired注入的容器是: " + applicationContext);
}

@PostConstruct
public void init() {
log.info("当前的bean" + this + " 使用@PostConstruct初始化");
}

运行后和上面的结果一样,说明@Autowired@PostConstruct注解失效。

解决办法:为容器注册相应的Bean后处理器

1
2
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
context.registerBean(CommonAnnotationBeanPostProcessor.class);

执行结果:

image-20231116154227898

6.2 @Autowired失效

代码示例

在配置类中配置一个BeanFactoryPostProcessor的Bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
public class MyConfig1 {

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

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

// 配置一个BeanFactoryPostProcessor的Bean
@Bean
public BeanFactoryPostProcessor processor1() {
return beanFactory -> {
log.info("执行processor1");
};
}

}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class A06Application {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myConfig1", MyConfig1.class);
// 注册@Configuration和@Autowired注解必要的后处理器
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
context.registerBean(CommonAnnotationBeanPostProcessor.class);
context.registerBean(ConfigurationClassPostProcessor.class);

context.refresh();
context.close();
}
}

执行结果:

image-20231116170711082

发现ApplicationContext未能注入,并且@PostConstruct注解的初始化方法也未能执行。

原因分析

配置类中不包含BeanFactoryPostProcessor时的情况:

image-20231116155053660

当配置类包含BeanFactoryPostProcessor时,因此要创建BeanFactoryPostProcessor必须提前创建Java配置类,而此时的BeanPostProcessor尚未准备好,导致@Autowired@PostConstruct失效:

image-20231116171121211

修改代码

实现Aware接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Slf4j
@Configuration
public class MyConfig2 implements InitializingBean, ApplicationContextAware {

@Override
public void afterPropertiesSet() throws Exception {
log.info("初始化");
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("注入ApplicationContext: " + applicationContext);
}

// 配置一个BeanFactoryPostProcessor的Bean
@Bean
public BeanFactoryPostProcessor processor1() {
return beanFactory -> {
log.info("执行processor1");
};
}
}

image-20231116171703460

现在能够成功注入。

7 初始化和销毁

7.1 初始化

初始化的手段有三种:

  • 使用@PostConstruct注解
  • 实现InitializingBean接口,并重写
  • 指定@BeaninitMethod属性

执行顺序:由上到下

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Slf4j
public class Bean1 implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
log.info("InitializingBean初始化");
}

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

public void init3() {
log.info("@Bean的initMethod初始化");
}
}

测试:

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

@Bean(initMethod = "init3")
public Bean1 bean1() {
return new Bean1();
}

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A07Application.class, args);
context.close();
}

}

执行结果:

image-20231116172657372

7.2 销毁

销毁的手段有三种:

  • 使用@PreDestroy注解
  • 实现Disposable接口,并重写
  • 指定@BeandestroyMethod属性

代码略

8 Scope作用域

bean的Scope有五种:

  • Singleton
  • Prototype
  • Request
  • Session
  • Application

8.1 域演示

现在演示后面三个域。

代码示例

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
@Slf4j
@Scope("application") // 设置作用域
@Component
public class BeanForApplication {
@PreDestroy
public void destroy() {
log.info("destroy");
}
}

@Slf4j
@Scope("request") // 设置作用域
@Component
public class BeanForRequest {
@PreDestroy
public void destroy() {
log.info("destroy");
}
}

@Slf4j
@Scope("session") // 设置作用域
@Component
public class BeanForSession {
@PreDestroy
public void destroy() {
log.info("destroy");
}
}

控制器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RestController
public class MyController {
@Lazy
@Autowired
private BeanForSession beanForSession;

@Lazy
@Autowired
private BeanForApplication beanForApplication;

@Lazy
@Autowired
private BeanForRequest beanForRequest;

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

发送请求后,返回:

1
2
3
request scope:com.hongyi.apiintegration.spring.a08.BeanForRequest@5e949f21
session scope:com.hongyi.apiintegration.spring.a08.BeanForSession@9eb8728
application scope:com.hongyi.apiintegration.spring.a08.BeanForApplication@52f10fb9

再次发送请求:

1
2
3
request scope:com.hongyi.apiintegration.spring.a08.BeanForRequest@3f3a62a3
session scope:com.hongyi.apiintegration.spring.a08.BeanForSession@9eb8728
application scope:com.hongyi.apiintegration.spring.a08.BeanForApplication@52f10fb9

发现只是request域的对象发生了变化,此外:

1
2
3
2023-11-17 14:25:07.754  c.h.a.spring.a08.BeanForRequest          : destroy
2023-11-17 14:25:12.714 c.h.a.spring.a08.BeanForRequest : destroy
2023-11-17 14:25:20.673 c.h.a.spring.a08.BeanForRequest : destroy

可以看到当请求完成后,request域的对象会被销毁,而session域和application域的对象不会被销毁。

session域对象的存活时间默认为30min,可以设置:

1
2
3
4
5
server:
port: 80
servlet:
session:
timeout: 10s

8.2 @Scope失效

当一个单例对象注入其他scope(除了单例之外)的对象时,会发生后者scope失效的问题。

演示

  • 单例
1
2
3
4
5
6
7
8
9
@Component
public class E {
@Autowired
private F f;

public F getF() {
return f;
}
}
  • 多例
1
2
3
4
5
@Scope("prototype")
@Component
public class F {

}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
@Slf4j
@ComponentScan("com.hongyi.apiintegration.spring.a09")
public class A09Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A09Application.class);
E e = context.getBean(E.class);
System.out.println(e.getF());
System.out.println(e.getF());
System.out.println(e.getF()); // 理论上三条打印结果不同
context.close();
}
}

执行结果:

1
2
3
com.hongyi.apiintegration.spring.a09.F@5e0e82ae
com.hongyi.apiintegration.spring.a09.F@5e0e82ae
com.hongyi.apiintegration.spring.a09.F@5e0e82ae 发现是同一个对象

失效原因

对于单例对象,依赖注入只执行一次E用的始终是第一次依赖注入的F

解决方法

  • 方法1:依赖注入时使用@Lazy注解生成一个代理对象,再由代理对象来调用F的方法

image-20231117150234949

1
2
3
@Lazy
@Autowired
private F f;
1
2
3
4
System.out.println(e.getF().getClass());
System.out.println(e.getF());
System.out.println(e.getF());
System.out.println(e.getF());

执行结果:

1
2
3
4
class com.hongyi.apiintegration.spring.a09.F$$EnhancerBySpringCGLIB$$3be3693d // 被CGLIB代理增强的F的子类代理
com.hongyi.apiintegration.spring.a09.F@2b91004a
com.hongyi.apiintegration.spring.a09.F@2fb3536e
com.hongyi.apiintegration.spring.a09.F@35aea049
  • 方法2:也是生成代理对象
1
2
3
4
5
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F {

}
  • 方法3:注入一个对象工厂,然后通过对象工厂获得
1
2
3
4
5
6
@Autowired
private ObjectFactory<F> f;

public F getF() {
return f.getObject();
}
  • 方法4:注入容器,然后通过容器获得
1
2
3
4
5
6
@Autowired
private ApplicationContext context;

public F getF() {
return context.getBean(F.class);
}

9 AOP实现

9.1 AspectJ

AspectJ编译器能够实现AOP的功能,原理在于它能在编译阶段将class文件进行修改(增强)。

需要在pom中引入AspectJ的依赖和插件:

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
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>

<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>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
  • 服务层
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
@Aspect // 切面
public class MyAspect {

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

// 通知
@Before("execution(* com.hongyi.aspectj_01.service.MyService.foo())") // 切点
public void before() {
log.info("before()");
}
}

测试:

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

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

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

}

打印结果:

image-20231117161713213

查看反编译后的MyServiceclass文件,发现源码中多了一行由AspectJ添加的方法:

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(); // AspectJ增加的代码
log.info("foo()");
}
}

切面类并没有被容器管理,将容器相关的测试代码删除后再进行测试:

1
2
3
4
5
6
public class Aspectj01Application {
private static final Logger log = LoggerFactory.getLogger(Aspectj01Application.class);
public static void main(String[] args) {
new MyService().foo();
}
}

打印结果:

image-20231117162212435

依然能够进行方法增强。

9.2 Agent

类加载阶段修改代码来增强方法。

image-20231117164643017

9.3 Proxy

9.3.1 JDK动态代理

可参考[[Java设计模式学习笔记新#5.1.4 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
36
37
38
39
public class JdkProxyDemo {
interface Foo {
void foo();
}

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

public static void main(String[] args) {
Target target = new Target();
// 获取代理对象
Foo proxyInstance = (Foo) Proxy.newProxyInstance(
// 目标对象的类加载器
target.getClass().getClassLoader(),
// 目标对象的接口
target.getClass().getInterfaces(),
// 代理对象增强的方法
// proxy: 代理对象
// method: 目标对象的方法
// args1: method的参数
(proxy, method, args1) -> {
// 前置通知
System.out.println("before...");
// 正常情况:目标.方法(参数)
// 反射:方法.invoke(目标, 参数)
Object result = method.invoke(target, args1);
// 后置通知
System.out.println("after...");
// 返回目标对象的方法返回值
return result;
}
);
proxyInstance.foo();
}
}

执行结果:

1
2
3
before...
target foo
after...

目标类和代理类是平级的兄弟关系,相同点在于他们都实现了目标类的接口。

9.3.2 CGLIB动态代理

参见[[Java设计模式学习笔记新#5.1.5 CGLIB动态代理]]

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CglibProxyDemo {
static class Target {
public void foo() {
System.out.println("target foo");
}
}

public static void main(String[] args) {
Target target = new Target();
Target proxyInstance = (Target) Enhancer.create(Target.class, (MethodInterceptor) (proxy, method, args1, methodProxy) -> {
System.out.println("before...");
Object result = method.invoke(target, args1);
System.out.println("after...");
return result;
});
proxyInstance.foo();
}
}

目标类和代理类是父子关系。

  • 如果目标类是final,则不能使用cglib来代理。
  • 如果目标类的方法是final,也会代理失败(因为代理类是通过重写父类的方法来进行增强的)。

此外:methodProxy可以避免反射来调用目标的方法

1
2
3
4
5
6
7
8
// Object result = method.invoke(target, args1);
// methodProxy可以避免反射来调用目标的方法
Object result = methodProxy.invoke(target, args1); // Spring采用的方式

// 或者
Object result = methodProxy.invokeSuper(proxy, args1);

// 三者效果一样

9.4 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
35
36
37
public class A13 {
// 共同实现的接口
interface Foo {
void foo();
int bar();
}

// 目标类
static class Target implements Foo {
@Override
public void foo() {
System.out.println("target foo");
}

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

interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

// 测试方法
public static void main(String[] args) {
Foo proxy0 = new $Proxy0((proxy, method, args1) -> {
// 1.功能增强
System.out.println("before...");
// 2.调用目标类的方法(反射)
return method.invoke(new Target(), args1);
});
proxy0.foo();
proxy0.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
public class $Proxy0 implements Foo {

private final InvocationHandler h;

public $Proxy0(InvocationHandler h) {
this.h = h;
}

@Override
public void foo() {
try {
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 = Foo.class.getMethod("foo");
bar = Foo.class.getMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
}

执行结果:

1
2
3
4
before...
target foo
before...
target bar

9.5 CGLIB动态代理原理

9.5.1 模拟实现

下面模拟实现CGLIB的动态代理。

  • MethodInterceptor接口:和JDK动态代理的InvocationHandler类似
1
2
3
public interface MethodInterceptor {
Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable;
}
  • 目标类
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)");
}
}
  • 代理类:继承自目标类,并重写方法
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
public class Proxy extends Target{
// 和JDK动态代理的InvocationHandler类似
private final MethodInterceptor methodInterceptor;

public Proxy(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}

static Method save0;
static Method save1;
static Method save2;
static {
try {
// 反射
save0 = Target.class.getMethod("save");
save1 = Target.class.getMethod("save", int.class);
save2 = Target.class.getMethod("save", long.class);
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}

@Override
public void save() {
try {
methodInterceptor.intercept(this, save0, new Object[0], null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

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

@Override
public void save(long j) {
try {
methodInterceptor.intercept(this, save2, new Object[]{j}, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
  • 测试
1
2
3
4
5
6
7
8
9
10
11
public class A14 {
public static void main(String[] args) {
Proxy proxy = new Proxy((o, method, args1, methodProxy) -> {
System.out.println("before...");
return method.invoke(new Target(), args1);
});
proxy.save();
proxy.save(1);
proxy.save(1L);
}
}

运行结果:

1
2
3
4
5
6
before...
save()
before...
save(int)
before...
save(long)

9.5.2 MethodProxy

CGLIB的MethodInterceptor的方法intercept相比于JDK的InvocationHandler接口的invoke方法参数多了一个MethodProxy,它能避免使用反射来执行目标对象的方法。

9.6 Spring选择代理

9.6.1 Spring的代理选择规则

两个切面的概念:

  • aspect:该切面是通知(advice)和切点(pointcut)的集合,例如通知1和切点1,通知2和切点2等等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Aspect // 一个切面
public class MyAspect {
@Before("execution(* foo())") // 通知1和切点1
public void before() {
System.out.println("前置增强");
}

@After("execution(* foo())") // 通知2和切点2
public void after() {
System.out.println("后置增强");
}

// ...
}
  • advisor:该切面是更细粒度的切面,只包含一个通知和切点。

测试代码

  • 情况一:
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
public class A15 {
public static void main(String[] args) {
// 1.切点
// 这里采用Pointcut接口的一个实现类:AspectJExpressionPointcut,即采用AspectJ的切点表达式
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.创建代理:通过代理工厂创建
Target2 target = new Target2();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
// 由于Target2并没有实现接口,因此Spring采用CGLIB代理
factory.setInterfaces(target.getClass().getInterfaces());
Target2 proxy = (Target2) factory.getProxy();
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();
}

interface I1 {
void foo();

void bar();
}

// 实现了接口
static class Target1 implements I1 {
@Override
public void foo() {
System.out.println("target1 foo");
}

@Override
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
5
com.hongyi.apiintegration.spring.a15.A15$Target2$$EnhancerBySpringCGLIB$$e9831691 // CGLIB代理
before...
target2 foo
after...
target2 bar
  • 情况二:
1
2
3
4
5
6
7
8
9
10
Target1 target = new Target1();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
// Target1实现了接口
factory.setInterfaces(target.getClass().getInterfaces());
I1 proxy = (I1) factory.getProxy();
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();

打印结果:

1
2
3
4
5
class com.hongyi.apiintegration.spring.a15.$Proxy0 // JDK代理
before...
target1 foo
after...
target1 bar
  • 情况三
1
2
3
4
5
6
7
8
9
10
11
Target1 target = new Target1();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
factory.setInterfaces(target.getClass().getInterfaces());
// 虽然Target1实现了接口,但设置ProxyTargetClass为true,则Spring总是采用CGLIB
factory.setProxyTargetClass(true);
I1 proxy = (I1) factory.getProxy();
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();

打印结果:

1
2
3
4
5
classcom.hongyi.apiintegration.spring.a15.A15$Target1$$EnhancerBySpringCGLIB$$8ccfe76e
before...
target1 foo
after...
target1 bar

总结

Spring创建代理的选择规则:

  • proxyTargetClass=false:目标实现了接口,则Spring采用JDK来创建代理
  • proxyTargetClass=false:目标没有实现接口,则Spring采用CGLIB来创建代理
  • proxyTargetClass=true:Spring采用CGLIB来创建代理

9.6.2 切点匹配

SpringAOP通过切点表达式来进行切点的匹配。

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 A16 {
public static void main(String[] args) throws NoSuchMethodException {
AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();
// 匹配任意返回值的bar空参方法
pt1.setExpression("execution(* bar())");
System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class)); // false
System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class)); // true

// 匹配带有@Transactional注解的方法
AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();
pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class)); // true
System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class)); // false
}

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

}

public void 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
47
48
49
50
51
52
53
54
55
56
public class A16 {
public static void main(String[] args) throws NoSuchMethodException {
// 自定义的切点匹配
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);
}
};
// true
System.out.println(pt3.matches(T1.class.getMethod("foo"), T1.class));
// false
System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class));
// true
System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));
// true
System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class));
}

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 {
@Override
public void foo() {

}
}
}

总结:底层切点的实现调用了aspectj的匹配方法

9.6.3 两种切面

  • 高级切面@Aspect
  • 低级切面Advisor

底层实现中需要将高级切面转换为低级切面。

切面的识别和转换依赖于AnnotationAwareAspectJAutoProxyCreator。它是一个Bean后处理器,在依赖注入之前和初始化之后执行。

作用:

  • 解析@Aspect注解
  • 创建代理对象

该类有两个重要方法:

  • findEligibleAdvisors:找到有资格的Advisors(切面)
    • 一部分切面是低级的,可以自己编写
    • 一部分切面是高级的,需要对@Aspect进行解析后获得
  • wrapIfNecessary:内部调用了findEligibleAdvisors,只要返回集合不为空,就表示需要创建代理

9.6.4 代理对象的创建时机