转载自:https://blog.csdn.net/qq_38505969/article/details/123739542
介绍
代码仓库地址:https://gitee.com/CandyWall/spring-source-study
跟着黑马满一航老师的spring高级49讲做的学习笔记,本笔记跟视频内容的项目名称和代码略有不同,我将49讲的代码每一讲的代码都拆成了独立的springboot项目,并且项目名称尽量做到了见名知意,都是基于我自己的考量,代码都已经过运行验证过的,仅供参考。
视频教程地址:https://www.bilibili.com/video/BV1P44y1N7QG
注:
1. 每一讲对应一个二级标题,每一个三级标题是使用子项目名称命名的,和我代码仓库的项目是一一对应的;
2. 代码里面用到了lombok插件来简化了Bean中的get()、set()方法,以及日志的记录的时候用了lombok的@Slf4j注解。
笔记中如有不正确的地方,欢迎在评论区指正,非常感谢!!!
每个子项目对应的视频链接以及一些重要内容的笔记
第一讲 BeanFactory
与ApplicationContext
的区别与联系
spring_01_beanfactory_applicationcontext_differences_connections
p2 001-第一讲-BeanFactory与ApplicationContext_1
测试代码:
@SpringBootApplication
@Slf4j
public class A01Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
// class org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
System.out.println(context.getClass());
}
}
到底什么是BeanFactory
-
它是
ApplicationContext
的父接口鼠标选中
ConfigurableApplicationContext
,按Ctrl + Shift + U
或者Ctrl + Alt + U
打开类图,可以看到ApplicationContext
的有个父接口是BeanFactory
-
它才是
Spring
的核心容器,主要的ApplicationContext
实现都 [组合]了他的功能打印
context.getClass()
,可以看到SpringBoot的启动程序返回的ConfigurableApplicationContext
的具体的实现类是AnnotationConfigServletWebServerApplicationContext
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args); // org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext System.out.println(context.getClass());
按图索骥,
AnnotationConfigServletWebServerApplicationContext
又间接继承了GenericApplicationContext
,在这个类里面可以找到beanFactory
作为成员变量出现。
BeanFactory
接口中的方法
查看springboot
默认的ConfigurableApplicationContext
类中的BeanFactory
的实际类型
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
// org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 查看实际类型
// class org.springframework.beans.factory.support.DefaultListableBeanFactory
System.out.println(beanFactory.getClass());
从打印结果可以了解到实际类型为DefaultListableBeanFactory
,所以这里以BeanFactory
的一个实现类DefaultListableBeanFactory
作为出发点,进行分析。
它的类图如下:
这里我们暂且不细看DefaultListableBeanFactory
,先看DefaultListableBeanFactory
的父类DefaultSingletonBeanFactory
,先选中它,然后按F12
,可以跳转到对应的源码,可以看到有个私有的成员变量singletonObjects
这里通过反射的方法来获取该成员变量,进行分析
先补充一下反射获取某个类的成员变量的步骤:
获取成员变量,步骤如下:
获取Class对象
获取构造方法
通过构造方法,创建对象
获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)
通过方法,给指定对象的指定成员变量赋值或者获取值
public void set(Object obj, Object value)
在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值
public Object get(Object obj)
返回指定对象obj中,此 Field 对象表示的成员变量的值
代码如下:
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
// 设置私有变量可以被访问
singletonObjects.setAccessible(true);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
// 查看实际类型
// class org.springframework.beans.factory.support.DefaultListableBeanFactory
System.out.println(beanFactory.getClass());
map.entrySet().stream().filter(entry -> entry.getKey().startsWith("component")).forEach(System.out::println);
这里singletonObjects.get(beanFactory)
为什么要传一个ConfigurableListableBeanFactory
的变量进去呢?打印了这个beanFactory
的实际类型为DefaultListableBeanFactory
,查看其类图,可以了解到该类也实现了DefaultSingletonBeanRegistry
接口,所以这里反射获取某个类的成员变量的get()
方法中可以作为参数传进来。
p4 003-第一讲-ApplicationContext功能1
ApplicationContext
比 BeanFactory
多点啥?
多实现了四个接口:
MessageSource
: 国际化功能,支持多种语言ResourcePatternResolver
: 通配符匹配资源路径EnvironmentCapable
: 环境信息,系统环境变量,*.properties
、*.application.yml
等配置文件中的值ApplicationEventPublisher
: 发布事件对象
-
MessageSource
在
resources
目录下创建四个文件messages.propertes
、messages_en.properties
、messages_ja.properties
、messages_zh.properties
,然后分别在四个文件里面定义同名的key
,比如在message_en.properties
中定义hi=hello
,在messages_ja.propertes
中定义hi=こんにちは
,在messages_zh
中定义hi=你好
,这样在代码中就可以根据这个**key hi
和不同的语言类型**获取不同的value
了。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.JAPANESE));
运行结果如下:
p5 004-第一讲-ApplicationContext功能2,3
-
ResourcePatternResolver
例1:获取类路径下的
messages
开头的配置文件Resource[] resources = context.getResources("classpath:messages*.properties"); for (Resource resource : resources) { System.out.println(resource); }
例2:获取
spring
相关jar
包中的spring.factories
配置文件resources = context.getResources("classpath*:META-INF/spring.factories"); for (Resource resource : resources) { System.out.println(resource); }
-
EnvironmentCapable
获取系统环境变量中的
java_home
和项目的application.yml
中的server.port
属性System.out.println(context.getEnvironment().getProperty("java_home")); System.out.println(context.getEnvironment().getProperty("server.port"));
p6 005-第一讲-ApplicationContext功能4
-
ApplicationEventPublisher
定义一个用户注册事件类,继承自
ApplicationEvent
类public class UserRegisteredEvent extends ApplicationEvent { public UserRegisteredEvent(Object source) { super(source); } }
再定义一个监听器类,用于监听用户注册事件,类头上需要加
@Component
注解,将该类交给spring
管理,定义一个处理事件的方法,参数类型为用户注册事件类的对象,方法头上需要加上@EventListener
注解@Component @Slf4j public class UserRegisteredListener { @EventListener public void userRegist(UserRegisteredEvent event) { System.out.println("UserRegisteredEvent..."); log.debug("{}", event); } }
接着再定义一个用户服务类,里面有个
register(String username, String password)
方法可以完成用户的注册,注册完毕后发布一下用户注册完毕事件。@Component @Slf4j public class UserService { @Autowired private ApplicationEventPublisher context; public void register(String username, String password) { log.debug("新用户注册,账号:" + username + ",密码:" + password); context.publishEvent(new UserRegisteredEvent(this)); } }
最后在
Springboot
启动类中调用一下UserService
里面的register()
方法注册一个新用户,UserRegisteredListener
中就能处理这个用户注册完毕的事件,实现了UserService
类和UserRegisteredListener
类的解耦。UserService userService = context.getBean(UserService.class); userService.register("张三", "123456");
第二讲 BeanFactory
和 ApplicationContext
类的重要实现类
spring_02_01_beanfactory_impl
p10 009-第二讲-BeanFactory实现-后处理器排序
DefaultListableBeanFactory
接着第一讲中的内容,执行以下代码,可以了解到
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
// org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 查看实际类型
// class org.springframework.beans.factory.support.DefaultListableBeanFactory
System.out.println(beanFactory.getClass());
ConfigurableApplicationContext
类内部组合的BeanFactory
实际类型为DefaultListableBeanFactory
,spring
底层创建实体类就是依赖于这个类,所以它是BeanFactory
接口最重要的一个实现类,下面使用这个类,模拟一下spring
使用DefaultListableBeanFactory
类创建其他实体类对象的过程。
测试代码如下:
package top.jacktgq.spring_02_beanfactory_impl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.annotation.Resource; import java.util.ArrayList; import java.util.stream.Collectors; /** * @Author CandyWall * @Date 2022/3/24--21:20 * @Description */ public class TestBeanFactory { public static void main(String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // bean 的定义(即bean的一些描述信息,包含class:bean是哪个类,scope:单例还是多例,初始化、销毁方法等) AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition(); beanFactory.registerBeanDefinition("config", beanDefinition); // 给 BeanFactory添加一些常用的后处理器,让它具备解析@Configuration、@Bean等注解的能力 AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); // 从bean工厂中取出BeanFactory的后处理器,并且执行这些后处理器 // BeanFactory 后处理器主要功能,补充了一些 bean 的定义 beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> { System.out.println(beanFactoryPostProcessor); beanFactoryPostProcessor.postProcessBeanFactory(beanFactory); }); // 打印BeanFactory中Bean for (String name : beanFactory.getBeanDefinitionNames()) { System.out.println(name); } // 从BeanFactory中取出Bean1,然后再从Bean1中取出它依赖的Bean2 // 可以看到结果为null,所以@Autowired注解并没有被解析 // Bean1 bean1 = beanFactory.getBean(Bean1.class); // System.out.println(bean1.getBean2()); // 要想@Autowired、@Resource等注解被解析,还要添加Bean的后处理器,可以针对Bean的生命周期的各个阶段提供扩展 // 从bean工厂中取出Bean的后处理器,并且执行这些后处理器 // BeanFactory 后处理器主要功能,补充了一些 bean 的定义 // beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor); // beanFactory.addBeanPostProcessors(beanFactory.getBeansOfType(BeanPostProcessor.class).values()); // 改变Bean后处理器加入BeanFactory的顺序 // 写法1: // ArrayList<BeanPostProcessor> list = new ArrayList<>(beanFactory.getBeansOfType(BeanPostProcessor.class).values()); // Collections.reverse(list); // beanFactory.addBeanPostProcessors(list); // 写法2: beanFactory.addBeanPostProcessors(beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().sorted(beanFactory.getDependencyComparator()).collect(Collectors.toCollection(ArrayList::new))); // 准备好所有单例,get()前就把对象初始化好 beanFactory.preInstantiateSingletons(); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>"); Bean1 bean1 = beanFactory.getBean(Bean1.class); System.out.println(bean1.getBean2()); /** * 学到了什么: * a. beanFactory 不会做的事 * 1. 不会主动调用BeanFactory的后处理器 * 2. 不会主动添加Bean的后处理器 * 3. 不会主动初始化单例 * 4. 不会解析BeanFactory,还不会解析 ${}, #{} * * b. Bean后处理器会有排序的逻辑 */ System.out.println(bean1.getInter()); } @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(); } } @Slf4j static class Bean1 { @Autowired private Bean2 bean2; public Bean2 getBean2() { return bean2; } @Autowired @Resource(name = "bean4") private Inter bean3; public Inter getInter() { return bean3; } public Bean1() { log.debug("构造 Bean1()"); } } @Slf4j static class Bean2 { public Bean2() { log.debug("构造 Bean2()"); } } interface Inter { } @Slf4j static class Bean3 implements Inter { public Bean3() { log.debug("构造 Bean3()"); } }
<span class="token annotation punctuation">@Slf4j</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Bean4</span> <span class="token keyword">implements</span> <span class="token class-name">Inter</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">public</span> <span class="token class-name">Bean4</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"构造 Bean4()"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
}
总结:
-
beanFactory 不会做的事
-
不会主动调用BeanFactory的后处理器
-
不会主动添加Bean的后处理器
-
不会主动初始化单例
-
不会解析BeanFactory,还不会解析 ${}, #{}
-
-
Bean后处理器会有排序的逻辑
先定义一个接口Inter,再定义两个Bean,名称分别为Bean3和Bean4,都继承Inter,接着在Config中通过@Bean注解将Bean3和Bean4都加进Bean工厂中,然后在Bean1中定义一个Inter对象,通过@Autowired注解将实现类注入进来。
@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(); } } @Slf4j static class Bean1 { @Autowired private Bean2 bean2; public Bean2 getBean2() { return bean2; } @Autowired @Resource(name = "bean4") private Inter bean3; public Inter getInter() { return bean3; } public Bean1() { log.debug("构造 Bean1()"); } } @Slf4j static class Bean2 { public Bean2() { log.debug("构造 Bean2()"); } } interface Inter { } @Slf4j static class Bean3 implements Inter { public Bean3() { log.debug("构造 Bean3()"); } } @Slf4j static class Bean4 implements Inter { public Bean4() { log.debug("构造 Bean4()"); } }
如果把以
Inter
接口声明的变量名定义为inter
,@Autowired
注解首先会**根据名称(byName)进行匹配,没有匹配上,于是又会根据类型(byType)**进行匹配,发现Bean3和Bean4都实现了Inter接口,会报无法自动装配的错误。所以为了避免这种错误,以
Inter
接口声明的变量名只能为bean3
或者bean4
,这里把以Inter
接口声明的变量名定义为bean3
,然后就不报错了,@Autowired
会通过byName
的方式进行匹配。在main方法中去获取Inter,然后打印,可以看到注入的是Bean3
如果此时在
private Inter bean3;
上面再加上@Resource(name = "bean4")
注解,然后再打印结果,结果还是bean3
,为什么呢?我们先看一下加入BeanFactory
的Bean后处理器的顺序,解析@Autowired
注解的后处理器internalAutowiredAnnotationProcessor
的顺序排在解析@Resource
注解的后处理器internalCommonAnnotationProcessor
的前面,所以internalAutowiredAnnotationProcessor
会被BeanFactory
先启用,故@Autowired
注解先被解析了。如果想要让
@Resource
注解先被解析呢,这就需要让后处理器internalCommonAnnotationProcessor
比internalAutowiredAnnotationProcessor
先加入BeanFactory
,代码如下:// 改变Bean后处理器加入BeanFactory的顺序 beanFactory.addBeanPostProcessors(beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().sorted(beanFactory.getDependencyComparator()).collect(Collectors.toCollection(ArrayList::new)));
这样一来注入的结果就是
Bean4
,@Resource(name = "bean4")
注解被先解析了通过
AnnotationConfigUtils
给beanFactory
添加一些后处理的时候会默认设置比较器,可以对BeanPostProcessor
进行排序,排序的依据是BeanPostProcessor
内部的order
属性,其中internalAutowiredAnnotationProcessor
的order属性的值为Ordered.LOWEST_PRECEDENCE - 2
,internalCommonAnnotationProcessor
的order
属性的值为Ordered.LOWEST_PRECEDENCE - 3
。从打印结果来看,
internalAutowiredAnnotationProcessor:2147483645
,internalCommonAnnotationProcessor:2147483644
,internalCommonAnnotationProcessor
的order
值更小,所以排序的时候会排在前面 -
BeanFactory
本身功能只是将定义好的BeanDefinition
加进来,而BeanFactory
的后处理器BeanFactoryPostProcessor
补充了一些Bean
的定义,可以解析@Configuration
、@Bean
等注解,将这些被注解修饰的Bean
也加进BeanFactory
。@Configuration
和@Bean
注解的解析过程的源码可以看AnnotationConfigUtils
和ConfigurationClassPostProcessor
-
要想
@Autowired
、@Resource
等注解被解析,还要添加Bean
的后处理器BeanPostProcessor
,可以针对Bean
的生命周期的各个阶段提供扩展。 -
BeanFactory中的对象都是懒加载的,如果不去调用get()方法获取的话,就不会初始化,如果想要让对象在get()之前就创建好,需要调用
beanFactory.preInstantiateSingletons()
方法。 -
教程弹幕中有人问:为啥
@Bean
和@Configration
注解不需要建立联系就能使用?- 建立联系了啊,上面也获取了
BeanFactory
的后置处理器,然后foreach
循环就是建立BeanFactory
的后置处理器和BeanFactory的联系。 - 另外
@Configuration
加不加,Config
类中的@Bea
n注解都会被解析,@Configuration
是用于spring
类扫描的时候用的,加了这个注解的类被扫描到了就会被放进Bean
工厂
- 建立联系了啊,上面也获取了
spring_02_02_applicationcontext_impl
p11 010-第二讲-ApplicationContext实现1,2
p12 011-第二讲-ApplicationContext实现3
p13 012-第二讲-ApplicationContext实现4
四个重要的ApplicationContext
接口的实现类
ClassPathXmlApplicationContext
:FileSystemXmlApplicationContext
:AnnotationConfigApplicationContext
:AnnotationConfigServletWebServerApplication
:
相关测试代码如下:
@Slf4j
public class TestApplicationContext {
@Test
// ⬇️1.最为经典的容器,基于classpath 下 xml 格式的配置文件来创建
public void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring_bean.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
@Test
// ⬇️2.基于磁盘路径下 xml 格式的配置文件来创建
public void testFileSystemXmlApplicationContext() {
// 可以用绝对路径或者相对路径
// FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("D:\\ideacode\\spring-source-study\\spring_02_02_applicationcontext_impl\\src\\main\\resources\\spring_bean.xml");
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("src\\main\\resources\\spring_bean.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
@Test
// ⬇️模拟一下ClassPathXmlApplicationContext和FileSystemXmlApplicationContext底层的一些操作
public void testMockClassPathAndFileSystemXmlApplicationContext() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取之前");
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println("读取之后");
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// reader.loadBeanDefinitions("spring_bean.xml");
// reader.loadBeanDefinitions(new ClassPathResource("spring_bean.xml"));
reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\spring_bean.xml"));
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@Test
// ⬇️3.较为经典的容器,基于java配置类来创建
public void testAnnotationConfigApplicationContext() {
// 会自动加上5个后处理器
// 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
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
@Test
// ⬇️4.较为经典的容器,基于java配置类来创建,并且还可以用于web环境
// 模拟了 springboot web项目内嵌Tomcat的工作原理
public void testAnnotationConfigServletWebServerApplicationContext() throws IOException {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 防止程序终止
System.in.read();
}
}
@Configuration
class WebConfig {
@Bean
// 1. WebServer工厂
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
// 2. web项目必备的DispatcherServlet
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
// 3. 将DispatcherServlet注册到WebServer上
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean("/hello")
public Controller controller1() {
return (request, response) -> {
response.getWriter().println("hello");
return null;
};
}
}
// 单元测试的过程中如果要解析一些Spring注解,比如@Configuration的时候不要把相关类定义到写单元测试类的内部类,会读取不到
@Configuration
class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2(Bean1 bean1) {
Bean2 bean2 = new Bean2();
bean2.setBean1(bean1);
return bean2;
}
}
class Bean1 {
}
class Bean2 {
private Bean1 bean1;
public Bean1 getBean1() {
return bean1;
}
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
}
spring_bean.xml
如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--
把5个后处理器加进来
等价于:AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
-->
<context:annotation-config />
<bean id="bean1" class="top.jacktgq.Bean1" />
<bean id="bean2" class="top.jacktgq.Bean2">
<property name="bean1" ref="bean1"/>
</bean>
</beans>
第三讲 Bean
的生命周期和模板方法设计模式
spring_03_bean_lifecycle
springboot
项目启动类
@SpringBootApplication
public class BeanLifeCycleApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BeanLifeCycleApplication.class, args);
context.close();
}
}
定义一个LifeCycleBean
,加上@Component
注解,再编写一些方法,给这些方法加上Bean
的生命周期过程中的注解
@Component
@Slf4j
public class LifeCycleBean {
public LifeCycleBean() {
log.debug("构造");
}
@Autowired
public void autowire(@Value("${JAVA_HOME}") String name) {
log.debug("依赖注入:{}", name);
}
@PostConstruct
public void init() {
log.debug("初始化");
}
@PreDestroy
public void destroy() {
log.debug("销毁");
}
}
编写自定义Bean
的后处理器,需要实现InstantiationAwareBeanPostProcessor
和DestructionAwareBeanPostProcessor
接口,并加上@Component
注解,对lifeCycleBean
的生命周期过程进行扩展。
@Slf4j
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {
@Override
// 实例化前(即调用构造方法前)执行的方法
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<<<<<<< 实例化前执行,如@PreDestroy");
// 返回null保持原有对象不变,返回不为null,会替换掉原有对象
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 void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if(beanName.equals("lifeCycleBean"))
log.debug("<<<<<<<<<<<销毁之前执行");
}
@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;
}
}
运行结果如下:
public class TestMethodTemplatePattern {
public static void main(String[] args) {
MyBeanFactory beanFactory = new MyBeanFactory();
beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Autowired"));
beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));
beanFactory.getBean();
}
static class MyBeanFactory {
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;
}
private List<BeanPostProcessor> processors = new ArrayList<>();
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
processors.add(beanPostProcessor);
}
}
interface BeanPostProcessor {
void inject(Object bean);
}
}
第四讲 常见Bean
后处理器以及@Autowired
注解被解析的详细过程
spring_04_beanpostprocessor
定义三个Bean
,名称分别为Bean1
,Bean2
,Bean3
,其中Bean1
中依赖了Bean2
和Bean3
,Bean2
通过@Autowired
的注解注入,Bean3
通过@Resource
注解注入,再通过@Value
注解注入一个Java
的环境变量JAVA_HOME
的值。最后定义两个方法init()
和destroy()
,分别加上@PostConstruct
和@PreDestroy
注解。
@Slf4j
public class Bean1 {
private Bean2 bean2;
@Autowired
public void setBean2(Bean2 bean2) {
log.debug("@Autowired 生效:{}", bean2);
this.bean2 = bean2;
}
@Autowired
public void setJava_home(@Value("${JAVA_HOME}") String java_home) {
log.debug("@Value 生效:{}", java_home);
this.java_home = java_home;
}
private Bean3 bean3;
@Resource
public void setBean3(Bean3 bean3) {
log.debug("@Resource 生效:{}", bean3);
this.bean3 = bean3;
}
private String java_home;
@PostConstruct
public void init() {
log.debug("@PostConstruct 生效:{}");
}
@PreDestroy
public void destroy() {
log.debug("@PreDestroy 生效:{}");
}
@Override
public String toString() {
return "Bean1{" +
"bean2=" + bean2 +
", bean3=" + bean3 +
", java_home='" + java_home + '\'' +
'}';
}
}
public class Bean2 {
}
public class Bean3 {
}
@ConfigurationProperties(prefix = “java”)
@Slf4j
public class Bean4 {
private String home;
private String version;
}
这里用GenericApplicationContext
来探究一下@Autowired
、@Value
、@Resource
、@PostConstruct
、@PreDestroy
以及springboot
项目中的@ConfigurationProperties
这些注解分别是由哪个后处理器来解析的。
注:GenericApplicationContext
是一个【干净】的容器,默认不会添加任何后处理器,方便做测试,这里用DefaultListableBeanFactory
也可以完成测试,只是会比使用GenericApplicationContext
麻烦一些。
测试代码如下:
@Slf4j
public class TestBeanPostProcessor {
@Test
public void testBeanPostProcessor() throws Exception {
// ⬇️GenericApplicationContext 是一个【干净】的容器,默认不会添加任何后处理器,方便做测试
// 这里用DefaultListableBeanFactory也可以完成测试,只是会比使用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);
// 设置解析 @Value 注解的解析器
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
// 添加解析 @Autowired 和 @Value 注解的后处理器
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
// 添加解析 @Resource、@PostConstruct、@PreDestroy 注解的后处理器
context.registerBean(CommonAnnotationBeanPostProcessor.class);
// 添加解析 @ConfigurationProperties注解的后处理器
// ConfigurationPropertiesBindingPostProcessor后处理器不能像上面几种后处理器那样用context直接注册上去
// context.registerBean(ConfigurationPropertiesBindingPostProcessor.class);
// 需要反着来注册一下
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());
// ⬇️初始化容器
context.refresh();
System.out.println(context.getBean(Bean4.class));
// ⬇️销毁容器
context.close();
}
}
运行结果如下:
经过测试和运行结果的比对:
@Autowired
注解对应的后处理器是AutowiredAnnotationBeanPostProcessor
;@Value
注解需要配合@Autowired
注解一起使用,所以也用到了AutowiredAnnotationBeanPostProcessor
后处理器,然后@Value
注解还需要再用到ContextAnnotationAutowireCandidateResolver
解析器,否则会报错;@Resource
、@PostConstruct
、@PreDestroy
注解对应的后处理器是CommonAnnotationBeanPostProcessor
;@ConfigurationProperties
注解对应的后处理器是ConfigurationPropertiesBindingPostProcessor
。
p18 017-第四讲-@Autowired bean后处理器执行分析
p19 018-第四讲-@Autowired bean后处理器执行分析
本案例测试代码紧接着上面,这里对Bean1
中加了@Autowired
注解的属性注入Bean2
、方法注入Bean3
以及方法注入环境变量JAVA_HOME
的过程进行分析。
@Autowired
注解解析用到的后处理器是AutowiredAnnotationBeanPostProcessor
- 这个后处理器就是通过调用
postProcessProperties(PropertyValues pvs, Object bean, String beanName)
完成注解的解析和注入的功能 - 这个方法中又调用了一个私有的方法
findAutowiringMetadata(beanName, bean.getClass(), pvs)
,其返回值InjectionMetadata
中封装了被@Autowired
注解修饰的属性和方法 - 然后会调用
InjectionMetadata.inject(bean1, "bean1", null)
进行依赖注入 - 由于
InjectionMetadata.inject(bean1, "bean1", null)
的源码调用链过长,摘出主要调用过程进行说明: - 成员变量注入,
InjectionMetadata
注入Bean3
的过程:InjectionMetadata
会把Bean1
中加了@Autowired
注解的属性的BeanName
先拿到,这里拿到的BeanName
就是bean3
,然后再通过反射拿到这个属性,Field bean3Field = Bean1.class.getDeclaredField("bean3");
- 将这个属性封装成一个
DependencyDescriptor
对象,再去调用Bean3 bean3Value = (Bean3) beanFactory.doResolveDependency(dd1, null, null, null);
拿到bean3Value
- 最后把值赋给这个属性
bean3Field.set(bean1, bean3Value);
- 方法参数注入,
InjectionMetadata
注入Bean2
的过程:InjectionMetadata
会把Bean1
中加了@Autowired
注解的方法的MethodName
先拿到,这里拿到的MethodName
就是setBean2
,然后再通过反射拿到这个方法,Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
- 将这个属性封装成一个
DependencyDescriptor
对象,再去调用Bean2 bean2Value = (Bean2) beanFactory.doResolveDependency(dd2, "bean2", null, null);
拿到bean2Value
- 最后调用方法
setBean2.invoke(bean1, bean2Value)
,给方法参数赋值。
- 方法参数注入,参数类型为String类型,且加上了@Value注解,
InjectionMetadata
注入环境变量JAVA_HOME
的过程:InjectionMetadata
会把Bean1
中加了@Autowired
注解的方法的MethodName
先拿到,这里拿到的MethodName
就是setJava_home
,然后再通过反射拿到这个方法,Method setJava_home = Bean1.class.getDeclaredMethod("setJava_home", String.class);
- 将这个属性封装成一个
DependencyDescriptor
对象,再去调用String java_home = (String) beanFactory.doResolveDependency(dd3, null, null, null);
拿到java_home
- 最后调用方法
setJava_home.invoke(bean1, java_home);
,给方法参数赋值。
全部测试代码如下:
@Slf4j
public class TestBeanPostProcessors {
@Test
public void testAutowiredAnnotationBeanPostProcessor() throws Throwable {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 这里为了省事就不使用 beanFactory.registerBeanDefinition()方法去添加类的描述信息了
// 直接使用 beanFactory.registerSingleton可以直接将Bean的单例对象注入进去,
// 后面调用beanFactory.getBean()方法的时候就不会去根据Bean的定义去创建Bean的实例了,
// 也不会有懒加载和依赖注入的初始化过程了。
beanFactory.registerSingleton("bean2", new Bean2());
beanFactory.registerSingleton("bean3", new Bean3());
// 设置@Autowired注解的解析器
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
// 设置解析 @Value 注解中的 ${} 表达式的解析器
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);
// 1. 查找哪些属性、方法加了 @Autowired,这称之为InjectionMetadata
// 创建后处理器
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
// 后处理器在解析@Autowired和@Value的时候需要用到其他Bean,
// 而BeanFactory提供了需要的Bean,所以需要把BeanFactory传给这个后处理器
processor.setBeanFactory(beanFactory);
// 创建Bean1
Bean1 bean1 = new Bean1();
System.out.println(bean1);
// 解析@Autowired和@Value注解,执行依赖注入
// PropertyValues pvs: 给注解的属性注入给定的值,这里不需要手动给定,传null即可
// processor.postProcessProperties(null, bean1, "bean1");
// postProcessProperties()方法底层原理探究
// 通过查看源码得知 postProcessProperties()方法中调用了一个私有的方法findAutowiringMetadata(beanName, bean.getClass(), pvs); 会返回一个InjectionMetadata的对象,然后会调用InjectionMetadata.inject(bean1, "bean1", null)进行依赖注入
// 通过反射调用一下
/*Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata",String.class, Class.class, PropertyValues.class);
findAutowiringMetadata.setAccessible(true);
// 获取Bean1上加了@Value @Autowired注解的成员变量和方法参数信息
InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);
System.out.println(metadata);
// 2. 调用 InjectionMetaData 来进行依赖注入,注入时按类型查找值
metadata.inject(bean1, "bean1", null);
System.out.println(bean1);*/
// 3. 如何去Bean工厂里面按类型查找值
// 由于InjectionMetadata.inject(bean1, "bean1", null)的源码调用链过长,摘出主要调用过程进行演示
// 3.1 @Autowired加在成员变量上,InjectionMetatadata给Bean1注入Bean3的过程
// 通过InjectionMetadata把Bean1加了@Autowired注解的属性的BeanName先拿到,这里假设拿到的BeanName就是 bean3
// 通过BeanName反射获取到这个属性,
Field bean3Field = Bean1.class.getDeclaredField("bean3");
// 设置私有属性可以被访问
bean3Field.setAccessible(true);
// 将这个属性封装成一个DependencyDescriptor对象
DependencyDescriptor dd1 = new DependencyDescriptor(bean3Field, false);
// 再执行beanFactory的doResolveDependency
Bean3 bean3Value = (Bean3) beanFactory.doResolveDependency(dd1, null, null, null);
System.out.println(bean3Value);
// 给Bean1的成员bean3赋值
bean3Field.set(bean1, bean3Value);
System.out.println(bean1);
// 3.2 @Autowired加在方法上,InjectionMetatadata给Bean1注入Bean2的过程
Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
Bean2 bean2Value = (Bean2) beanFactory.doResolveDependency(dd2, "bean2", null, null);
System.out.println(bean2Value);
// 给Bean1的setBean2()方法的参数赋值
setBean2.invoke(bean1, bean2Value);
System.out.println(bean1);
// 3.3 @Autowired加在方法上,方法参数为String类型,加了@Value,
// InjectionMetadata给Bean1注入环境变量JAVA_HOME属性的值
Method setJava_home = Bean1.class.getDeclaredMethod("setJava_home", String.class);
DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setJava_home, 0), true);
String java_home = (String) beanFactory.doResolveDependency(dd3, null, null, null);
System.out.println(java_home);
setJava_home.invoke(bean1, java_home);
System.out.println(bean1);
}
}
运行结果如下:
第五讲 常见Bean工厂后处理器以及模拟实现组件扫描
spring_05_beanfactorypostprocessor
定义Bean1
、Bean2
、Mapper1
、Mapper2
、Config
5个类,其中Bean2
上面加上@Component
和@ComponentScan
注解,Config
上加@Component
注解,Config
中通过@Bean
注解定义Bean1
,Mapper1
、Mapper2
上加@Mapper
注解,类的定义参考下图,由于涉及的类比较多,具体代码可以去我的代码仓库获取。
这里用GenericApplicationContext
来探究一下@Component
、@ComponentScan
、@Bean
、@MapperScan
这些注解分别是由哪个后处理器来解析的。
测试代码如下:
@Slf4j
public class TestBeanFactoryPostProcessors {
@Test
public void testBeanPostProcessors() throws IOException {
// ⬇️GenericApplicationContext 是一个【干净】的容器,默认不会添加任何后处理器,方便做测试
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// 添加Bean工厂后处理器ConfigurationClassPostProcessor
// 解析@ComponentScan、@Bean、@Import、@ImportResource注解
context.registerBean(ConfigurationClassPostProcessor.class);
// 添加Bean工厂后处理器MapperScannerConfigurer,解析@MapperScan注解
context.registerBean(MapperScannerConfigurer.class, beanDefinition -> {
// 指定扫描的包名
beanDefinition.getPropertyValues().add("basePackage", "top.jacktgq.mapper");
});
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
经过测试和运行结果的比对:
@Component
、@Bean
对应的Bean
工厂后处理器是ConfigurationClassPostProcessor
;@MapperScan
对应的Bean
工厂后处理器是MapperScannerConfigurer
。
自定义组件扫描Bean
工厂后处理器CandyComponentScanPostProcessor
来解析@Component
注解,代码如下:
public class CandyAtComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null) {
for (String basePage : componentScan.basePackages()) {
System.out.println(basePage);
// top.jacktgq.component -> classpath*:com/jacktgq/component/**/*.class
String path = "classpath*:" + basePage.replace('.', '/') + "/**/*.class";
// System.out.println(path);
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (Resource resource : new PathMatchingResourcePatternResolver().getResources(path)) {
// System.out.println(resource);
// 查看对应的类上是否有@Component注解
// System.out.println("分隔符>>>>>>>>>>>>>>>>>");
MetadataReader reader = factory.getMetadataReader(resource);
String className = reader.getClassMetadata().getClassName();
// System.out.println("类名:" + className);
String name = Component.class.getName();
// System.out.println("是否加了 @Component注解:" + reader.getAnnotationMetadata().hasAnnotation(name));
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
// System.out.println("是否加了@Component的派生注解:" + annotationMetadata.hasMetaAnnotation(name));
// 如果直接或者间接加了@Component注解
if (annotationMetadata.hasAnnotation(Component.class.getName()) || annotationMetadata.hasMetaAnnotation(name)) {
// 创建Bean的定义
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(className).getBeanDefinition();
String beanName = generator.generateBeanName(beanDefinition, beanFactory);
// 将Bean定义加入工厂
beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
// context.refresh()中会回调该方法
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
测试代码:
@Slf4j
public class TestBeanFactoryPostProcessors {
@Test
// 模拟实现组件扫描
public void testMockComponentScan() throws Exception {
// ⬇️GenericApplicationContext 是一个【干净】的容器,默认不会添加任何后处理器,方便做测试
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// 把自定义组件扫描Bean工厂后处理器加进来
context.registerBean(CandyComponentScanPostProcessor.class);
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
运行结果:
自定义Bean
工厂后处理器CandyAtBeanPostProcessor
来解析@Bean
注解,代码如下:
public class CandyAtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("top/jacktgq/Config.class"));
Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata annotatedMethod : annotatedMethods) {
System.out.println(annotatedMethod);
String initMethod = annotatedMethod.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
// 这里不需要指定类名了,因为最终的BeanDefinition是Config类中加了@Bean属性的方法的返回值的类型的定义。
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(annotatedMethod.getMethodName(), "config");
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
if (initMethod.length() > 0) {
builder.setInitMethodName(initMethod);
}
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
beanFactory.registerBeanDefinition(annotatedMethod.getMethodName(), beanDefinition);
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
注:自定义解析 @ComponentScan
和 后面的 @Bean
注解的Bean
工厂后处理器,实现BeanDefinitionRegistryPostProcessor
接口而不是BeanFactoryPostProcessor
接口,这么做的原因是:
- 实现
BeanFactoryPostProcessor
,postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory)
方法,参数configurableListableBeanFactory
工厂调用不了registerBeanDefinition()
方法,需要做强制转换,转成DefaultableBeanFactory
类型,才能调用registerBeanDefinition()
方法。 - 实现
BeanDefinitionRegistryPostProcessor
,它里面除了postProcessBeanFactory()
方法,还有一个postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory)
,参数beanFactory
可以直接调用registerBeanDefinition()
方法,避免了ConfigurableListableBeanFactory
向DefaultableBeanFactory
的强制转换。
测试代码:
public class TestBeanFactoryPostProcessors {
@Test
// 模拟实现@Bean注解的解析
public void testMockAtBeanAnnotation() throws Exception {
// ⬇️GenericApplicationContext 是一个【干净】的容器,默认不会添加任何后处理器,方便做测试
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean(CandyAtBeanPostProcessor.class);
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
运行结果:
自定义Bean
工厂后处理器CandyAtMapperPostProcessor
来解析@Mapper
注解,代码如下:
public class CandyAtMapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:top/jacktgq/mapper/**/*.class");
// Bean的名字生成器
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 beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class).addConstructorArgValue(classMetadata.getClassName()).setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE).getBeanDefinition();
// 这里不能使用名字生成器和MapperFactoryBean的BeanDefinition作为参数直接生成名字,
// 这样会导致多个相同的类型的对象因为名字一样产生覆盖的问题
// 解决办法 这里参考Spring源码的做法
// 用@Mapper注解修饰的接口的BeanDefinition作为参数生成名字
AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
String beanName = generator.generateBeanName(bd, beanFactory);
beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
测试代码:
@Slf4j
public class TestBeanFactoryPostProcessors {
@Test
// 模拟实现@Mapper注解的解析
public void testMockAtMapperAnnotation() throws Exception {
// ⬇️GenericApplicationContext 是一个【干净】的容器,默认不会添加任何后处理器,方便做测试
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// 先解析@Bean注解,把SqlSessionFactory加到Bean工厂里面
context.registerBean(CandyAtBeanPostProcessor.class);
// 解析Mapper接口
context.registerBean(CandyAtMapperPostProcessor.class);
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
运行结果: