Spring原理学习
Spring原理学习
容器与Bean
BeanFactory和ApplicationContext
BeanFactory
001-第一讲-BeanFactory与ApplicationContext_哔哩哔哩_bilibili
-
它是 ApplicationContext 的父接口**(见下图)**
-
它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory
-
getBean()
是BeanFactory最主要的功能 -
表面上只有 getBean
-
实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类
(DefaultListableBeanFactory)
提供
ApplicationContext
003-第一讲-ApplicationContext功能1_哔哩哔哩_bilibili
-
ApplicationContext 接口,是 BeanFactory 的子接口。它扩展了 BeanFactory 接口的功能,如:
-
国际化
public class TestMessageSource { public static void main(String[] args) { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("messageSource", MessageSource.class, () -> { ResourceBundleMessageSource ms = new ResourceBundleMessageSource(); ms.setDefaultEncoding("utf-8"); ms.setBasename("messages"); return ms; }); context.refresh(); System.out.println(context.getMessage("hi", null, Locale.ENGLISH)); System.out.println(context.getMessage("hi", null, Locale.CHINESE)); System.out.println(context.getMessage("hi", null, Locale.JAPANESE)); } }
国际化文件均在 src/resources 目录下
messages.properties(空)
messages_en.properties
hi=Hello
messages_ja.properties
hi=こんにちは
messages_zh.properties
hi=你好
注意
- ApplicationContext 中 MessageSource bean 的名字固定为 messageSource
- 使用 SpringBoot 时,国际化文件名固定为 messages
- 空的 messages.properties 也必须存在
-
通配符方式获取一组 Resource
-
资源整合 Environment 环境(能通过它获取各种来源的配置信息)
-
事件发布与监听,实现组件之间的解耦
-
事件驱动
概述
事件通常由事件源(Event Source)触发,并且可以被其他组件或对象监听和处理。事件源可以是用户的交互操作、传感器的检测、系统的状态变化等等。当事件源触发一个事件时,它会通知所有监听该事件的对象,以便它们可以执行相应的操作或逻辑。
假设有一个用户注册功能,当用户注册成功后需要发送一封欢迎邮件和一条短信。如果不使用事件驱动的方式,代码可能会像这样:
public class UserService {
public void register(User user) {
// 注册用户
//...
// 发送欢迎邮件
EmailService emailService = new EmailService();
emailService.sendWelcomeEmail(user.getEmail());
// 发送短信
SmsService smsService = new SmsService();
smsService.sendSms(user.getMobile(), "欢迎注册");
}
}
在上面的代码中,UserService 类负责注册用户,并在注册成功后直接调用 EmailService 和 SmsService 发送邮件和短信。这样就形成了直接的依赖关系,UserService 类需要知道有哪些服务类需要被调用,并且需要知道这些服务类的具体实现。这样会导致代码的可维护性和灵活性变差。
如果使用事件驱动的方式,代码可能会像这样:
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void register(User user) {
// 注册用户
//...
// 发布用户注册事件
eventPublisher.publishEvent(new UserRegisteredEvent(this, user));
}
}
public class EmailService {
@EventListener
public void sendWelcomeEmail(UserRegisteredEvent event) {
User user = event.getUser();
// 发送欢迎邮件
//...
}
}
public class SmsService {
@EventListener
public void sendSms(UserRegisteredEvent event) {
User user = event.getUser();
// 发送短信
//...
}
}
在上面的代码中,UserService 类负责注册用户,并使用 ApplicationEventPublisher 接口发布了 UserRegisteredEvent 事件。EmailService 和 SmsService 类使用 @EventListener 注解监听了 UserRegisteredEvent 事件,并在事件发生时发送邮件和短信。这样,UserService 类和 EmailService、SmsService 类之间就形成了松耦合的关系,UserService 类只需要负责发布事件,而其他组件则只需要监听事件并做出相应的处理,不需要知道彼此之间的具体实现。
应用
在实际开发中,您可以使用 Spring 框架提供的事件驱动机制来实现事件驱动编程。下面是一个简单的步骤,可以帮助您在 Spring 中使用事件驱动编程:
- 创建事件类。事件类应该继承自 ApplicationEvent 类,并且包含一些与事件相关的属性和方法。
public class UserRegisteredEvent extends ApplicationEvent {
private User user;
public UserRegisteredEvent(Object source, User user) {
super(source);
this.user = user;
}
public User getUser() {
return user;
}
}
- 在发布事件的组件中注入 ApplicationEventPublisher 对象,并使用 publishEvent() 方法发布事件。
@Component
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void register(User user) {
// 注册用户
//...
// 发布用户注册事件
eventPublisher.publishEvent(new UserRegisteredEvent(this, user));
}
}
- 在监听事件的组件中使用 @EventListener 注解来监听事件,并在事件发生时执行相应的处理逻辑。
@Component
public class EmailService {
@EventListener
public void sendWelcomeEmail(UserRegisteredEvent event) {
User user = event.getUser();
// 发送欢迎邮件
//...
}
}
@Component
public class SmsService {
@EventListener
public void sendSms(UserRegisteredEvent event) {
User user = event.getUser();
// 发送短信
//...
}
}
在上面的代码中,UserService 组件使用 ApplicationEventPublisher 对象发布了 UserRegisteredEvent 事件,而 EmailService 和 SmsService 组件使用 @EventListener 注解监听了这个事件,并在事件发生时执行相应的处理逻辑。
***(不生效再考虑)***需要注意的是,为了让 Spring 能够自动识别和处理事件,需要在 Spring 配置文件中添加以下配置:
<context:annotation-config />
这样,Spring 就会自动扫描组件中的 @EventListener 注解,并将它们注册为事件监听器。
总的来说,使用 Spring 的事件驱动机制可以实现组件之间的解耦,提高系统的灵活性和可维护性。同时,事件驱动编程还可以让代码变得更加清晰、简洁和易于维护。
容器实现
Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,这里出于怀旧的原因,把它们都列出来,供大家参考
- DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转和依赖注入功能,都是它来实现
- ClassPathXmlApplicationContext,从类路径查找 XML 配置文件,创建容器(旧)
- FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)
- XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)
- AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)
- AnnotationConfigApplicationContext,Spring boot 中非 web 环境容器(新)
- AnnotationConfigServletWebServerApplicationContext,Spring boot 中 servlet web 环境容器(新)
- AnnotationConfigReactiveWebServerApplicationContext,Spring boot 中 reactive web 环境容器(新)
另外要注意的是,后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现,但它们是组合了 DefaultListableBeanFactory 的功能,并非继承而来
DefaultListableBeanFactory实现
007-第二讲-BeanFactory实现_哔哩哔哩_bilibili
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义(class, scope, 初始化, 销毁)
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config", beanDefinition);
//输出的只是bean定义
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println("=====================================");
// 给 BeanFactory 添加一些常用的后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println("=====================================");
// BeanFactory 后处理器主要功能,补充了一些 bean 定义
Collection<BeanFactoryPostProcessor> beanFactoryPostProcessors
= beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values();
//internalConfigurationAnnotationProcessor
//internalEventListenerProcessor
beanFactoryPostProcessors.forEach(beanFactoryPostProcessor -> beanFactoryPostProcessor.postProcessBeanFactory(beanFactory));
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println("=====================================");
// Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...
//将bean后处理器加入到bean后处理器链中后,bean后处理器才会发挥作用
Collection<BeanPostProcessor> beanPostProcessors
= beanFactory.getBeansOfType(BeanPostProcessor.class).values();
//internalAutowiredAnnotationProcessor
//internalCommonAnnotationProcessor
beanPostProcessors.stream()
//给bean后处理器排序
.sorted(beanFactory.getDependencyComparator())
//将bean后处理器加入到bean后处理器链中后,bean后处理器才会发挥作用
.forEach(beanFactory::addBeanPostProcessor);
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println("=====================================");
beanFactory.preInstantiateSingletons(); // 准备好所有单例,在调用此方法之前bean没有被创建
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ");
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
System.out.println(beanFactory.getBean(Bean1.class).getInter());
/*
学到了什么:
a. beanFactory 不会做的事
1. 不会主动调用 BeanFactory 后处理器
2. 不会主动添加 Bean 后处理器
3. 不会主动初始化单例
4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }
b. bean 后处理器会有排序的逻辑
*/
System.out.println("Common:" + (Ordered.LOWEST_PRECEDENCE - 3));
System.out.println("Autowired:" + (Ordered.LOWEST_PRECEDENCE - 2));
}
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
@Bean
public Bean3 bean3() {
return new Bean3();
}
@Bean
public Bean4 bean4() {
return new Bean4();
}
}
interface Inter {
}
static class Bean3 implements Inter {
}
static class Bean4 implements Inter {
}
static class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
public Bean1() {
log.debug("构造 Bean1()");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2() {
return bean2;
}
@Autowired
@Resource(name = "bean4")
private Inter bean3;
public Inter getInter() {
return bean3;
}
}
static class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("构造 Bean2()");
}
}
}
- beanFactory 可以通过 registerBeanDefinition 注册一个 bean definition 对象
- 我们平时使用的配置类、xml、组件扫描等方式都是生成 bean definition 对象注册到 beanFactory 当中
- bean definition 描述了这个 bean 的创建蓝图:scope 是什么、用构造还是工厂创建、初始化销毁方法是什么,等等
- beanFactory 需要手动调用 beanFactory 后处理器对它做增强
- 例如通过解析 @Bean、@ComponentScan 等注解,来补充一些 bean definition
- beanFactory 需要手动添加 bean 后处理器,以便对后续 bean 的创建过程提供增强
- 例如 @Autowired,@Resource 等注解的解析都是 bean 后处理器完成的
- bean 后处理的添加顺序会对解析结果有影响,见视频中同时加 @Autowired,@Resource 的例子
- beanFactory 需要手动调用方法来初始化单例
- beanFactory 需要额外设置才能解析 ${} 与 #{}
ApplicationContext实现
010-第二讲-ApplicationContext实现1,2_哔哩哔哩_bilibili
/*
常见 ApplicationContext 实现
*/
public class A02 {
private static final Logger log = LoggerFactory.getLogger(A02.class);
public static void main(String[] args) {
// testClassPathXmlApplicationContext();
// testFileSystemXmlApplicationContext();
testAnnotationConfigApplicationContext();
// testAnnotationConfigServletWebServerApplicationContext();
/* 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(new FileSystemResource("src\\main\\resources\\a02.xml"));
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}*/
/*
学到了什么
a. 常见的 ApplicationContext 容器实现
b. 内嵌容器、DispatcherServlet 的创建方法、作用
*/
}
// ⬇️较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
private static void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("a02.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
// ⬇️基于磁盘路径下 xml 格式的配置文件来创建
private static void testFileSystemXmlApplicationContext() {
FileSystemXmlApplicationContext context =
new FileSystemXmlApplicationContext(
"src\\main\\resources\\a02.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
// ⬇️较为经典的容器, 基于 java 配置类来创建
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());
}
// ⬇️较为经典的容器, 基于 java 配置类来创建, 用于 web 环境
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@Configuration
static class WebConfig {
@Bean
public ServletWebServerFactory servletWebServerFactory(){
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean("/hello")
public Controller controller1() {
return (request, response) -> {
response.getWriter().print("hello");
return null;
};
}
}
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2(Bean1 bean1) {
Bean2 bean2 = new Bean2();
bean2.setBean1(bean1);
return bean2;
}
}
static class Bean1 {
}
static class Bean2 {
private Bean1 bean1;
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
public Bean1 getBean1() {
return bean1;
}
}
}
- 前面两个为xml方式注册
- 第三个为配置类注册,会自动为我们添加常用的bean处理器
- 第四个配置方式就体现了springboot和springMvc原理
- 第一个 Bean
tomcatServletWebServerFactory()
是用来创建 Servlet Web 服务器的。在这个例子中,我们使用 Tomcat 作为 Servlet 容器,因此创建了一个TomcatServletWebServerFactory
对象,并设置了端口号为8080
。这个 Bean 的主要作用是创建一个嵌入式的 Tomcat 服务器,用于运行 Spring Boot 应用程序。 - 第二个 Bean
dispatcherServlet()
是用来创建DispatcherServlet
对象的。DispatcherServlet
是 Spring MVC 框架的核心组件,用于处理 HTTP 请求并将请求分发到对应的 Controller 类中进行处理。在这个例子中,我们创建了一个DispatcherServlet
对象,并将其设置为根 Servlet。这个 Bean 的主要作用是创建一个DispatcherServlet
对象,用于处理 HTTP 请求。 - 第三个 Bean
dispatcherServletRegistration()
是用来将DispatcherServlet
注册到 Servlet 容器中的。在这个例子中,我们创建了一个DispatcherServletRegistrationBean
对象,并将DispatcherServlet
对象作为参数传入。然后,我们设置了DispatcherServlet
对象的 URL 映射规则为/
,这意味着所有的 HTTP 请求都会被DispatcherServlet
捕获和处理。最后,我们将DispatcherServletRegistrationBean
对象返回,用于将DispatcherServlet
注册到 Servlet 容器中。
- 第一个 Bean
Bean的生命周期
简单阶段
一个受 Spring 管理的 bean,生命周期主要阶段有
- 创建:根据 bean 的构造方法或者工厂方法来创建 bean 实例对象
- 依赖注入:根据 @Autowired,@Value 或其它一些手段,为 bean 的成员变量填充值、建立关系
- 初始化:回调各种 Aware 接口,调用对象的各种初始化方法
- 销毁:在容器关闭时,会销毁所有单例对象(即调用它们的销毁方法)
- prototype 对象也能够销毁,不过需要容器这边主动调用
@Component
public class LifeCycleBean {
private static final Logger log = LoggerFactory.getLogger(LifeCycleBean.class);
public LifeCycleBean() {
log.debug("构造");
}
@Autowired
public void autowire(@Value("${JAVA_HOME}") String home) {
log.debug("依赖注入: {}", home);
}
@PostConstruct
public void init() {
log.debug("初始化");
}
@PreDestroy
public void destroy() {
log.debug("销毁");
}
}
-
构造阶段
在构造阶段中,Spring 容器会调用 Bean 的构造函数来创建实例,并将 Bean 的依赖项注入到构造函数中。如果 Bean 的构造函数需要依赖其他 Bean,Spring 容器会在构造函数调用之前先实例化这些依赖项。此外,如果 Bean 的构造函数需要进行一些初始化操作,例如设置默认值或分配内存空间,也可以在这个阶段完成。
-
初始化阶段
在初始化阶段中,Spring 容器会为 Bean 设置属性,注入依赖项,并调用 Bean 的各种生命周期回调方法。在这个阶段中,Bean 可以执行任何初始化逻辑,例如建立数据库连接、打开文件或初始化其他资源。常见的生命周期回调方法包括
@PostConstruct
和@PreDestroy
注解、实现InitializingBean
和DisposableBean
接口以及自定义的 Bean 后置处理器。这些回调方法可以让开发者在 Bean 初始化和销毁的不同阶段执行自定义的逻辑 -
总结
总的来说,构造阶段和初始化阶段是 Bean 生命周期中非常重要的两个阶段。构造阶段主要负责创建对象并分配内存空间,而初始化阶段主要负责将 Bean 实例变得完整,以便投入使用。在初始化阶段中,Bean 实例会得到属性值的赋值、依赖项的注入和自定义的初始化逻辑的执行。
示例
当 Spring 容器创建一个 Bean 实例时,它会经历两个阶段:构造阶段和初始化阶段。
在构造阶段中,Spring 容器会调用 Bean 的构造函数来创建实例,并将 Bean 的依赖项注入到构造函数中。在这个阶段,Bean 的属性尚未设置,因此不能执行任何操作依赖于属性的逻辑。下面是一个示例:
public class MyBean {
private String name;
public MyBean(String name) {
this.name = name;
System.out.println("MyBean constructor called");
}
public void sayHello() {
System.out.println("Hello, " + name);
}
}
在初始化阶段中,Spring 容器会为 Bean 设置属性,注入依赖项,并调用 Bean 的各种生命周期回调方法。常见的生命周期回调方法包括 @PostConstruct
和 @PreDestroy
注解、实现 InitializingBean
和 DisposableBean
接口以及自定义的 Bean 后置处理器。下面是一个示例:
public class MyBean {
private String name;
public MyBean(String name) {
this.name = name;
System.out.println("MyBean constructor called");
}
@PostConstruct
public void init() {
System.out.println("MyBean init called");
}
public void sayHello() {
System.out.println("Hello, " + name);
}
@PreDestroy
public void destroy() {
System.out.println("MyBean destroy called");
}
}
在上面的示例中,@PostConstruct
注解标识的 init()
方法会在 Bean 初始化之后立即执行,@PreDestroy
注解标识的 destroy()
方法会在容器关闭时执行。这些方法可以执行任何初始化或清理逻辑。
在实际开发中,我们还可以通过自定义 Bean 后置处理器来扩展 Bean 的生命周期。下面是一个示例:
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("MyBeanPostProcessor postProcessBeforeInitialization called for " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("MyBeanPostProcessor postProcessAfterInitialization called for " + beanName);
return bean;
}
}
在上面的示例中,MyBeanPostProcessor
实现了 BeanPostProcessor
接口,并覆盖了 postProcessBeforeInitialization()
和 postProcessAfterInitialization()
方法。这些方法会在 Bean 初始化之前和之后执行,可以用来定制 Bean 实例的创建过程。
当运行上述示例代码时,控制台的输出结果如下:
MyBean constructor called
MyBeanPostProcessor postProcessBeforeInitialization called for myBean
MyBean init called
MyBeanPostProcessor postProcessAfterInitialization called for myBean
Hello, world!
MyBean destroy called
可以看到,首先输出了 "MyBean constructor called",表示 Bean 的构造函数被调用,然后输出了 "MyBeanPostProcessor postProcessBeforeInitialization called for myBean",表示自定义的 Bean 后置处理器的 postProcessBeforeInitialization()
方法被调用。接着输出了 "MyBean init called",表示 @PostConstruct
注解标识的初始化方法被调用。然后输出了 "MyBeanPostProcessor postProcessAfterInitialization called for myBean",表示自定义的 Bean 后置处理器的 postProcessAfterInitialization()
方法被调用。最后输出了 "Hello, world!",表示 Bean 实例被完整地创建,并且可以执行其方法。最后输出了 "MyBean destroy called",表示 @PreDestroy
注解标识的销毁方法在容器关闭时被调用。
综上所述,构造阶段和初始化阶段是 Bean 生命周期中非常重要的两个阶段。在构造阶段中,Spring 容器会调用 Bean 的构造函数来创建实例,并将 Bean 的依赖项注入到构造函数中;在初始化阶段中,Spring 容器会为 Bean 设置属性,注入依赖项,并调用 Bean 的各种生命周期回调方法。
增强阶段
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {
private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
// return false;
}
return true;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
return pvs;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
return bean;
}
}
创建前后的增强
- postProcessBeforeInstantiation
- 这里返回的对象若不为 null 会替换掉原本的 bean,并且仅会走 postProcessAfterInitialization 流程
- postProcessAfterInstantiation
- 这里如果返回 false 会跳过依赖注入阶段
依赖注入前的增强
- postProcessProperties
- 如 @Autowired、@Value、@Resource
初始化前后的增强
- postProcessBeforeInitialization
- 这里返回的对象会替换掉原本的 bean
- 如 @PostConstruct、@ConfigurationProperties
- postProcessAfterInitialization
- 这里返回的对象会替换掉原本的 bean
- 如代理增强
销毁之前的增强
- postProcessBeforeDestruction
- 如 @PreDestroy
模板设计模式
大流程已经固定好了, 通过接口回调(bean 后处理器)在一些关键点前后提供扩展
public class TestMethodTemplate {
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();
}
// 模板方法 Template Method Pattern
static class MyBeanFactory {
public Object getBean() {
Object bean = new Object();
System.out.println("构造 " + bean);
System.out.println("依赖注入 " + bean); // @Autowired, @Resource
for (BeanPostProcessor processor : processors) {
processor.inject(bean);
}
System.out.println("初始化 " + bean);
return bean;
}
private List<BeanPostProcessor> processors = new ArrayList<>();
public void addBeanPostProcessor(BeanPostProcessor processor) {
processors.add(processor);
}
}
static interface BeanPostProcessor {
public void inject(Object bean); // 对依赖注入阶段的扩展
}
}
运行该代码,控制台输出的结果如下:
构造 java.lang.Object@5cad8086
依赖注入 java.lang.Object@5cad8086
解析 @Autowired
解析 @Resource
初始化 java.lang.Object@5cad8086
可以看到,按照 getBean()
方法中定义的算法骨架,首先构造了一个 Object
对象,然后进行依赖注入,接着通过添加的两个 BeanPostProcessor
对象,分别解析了 @Autowired
和 @Resource
注解。最后进行了初始化,并输出了相关信息。
- getBean() 方法定义了一个算法的骨架,包含了构造、依赖注入、初始化等步骤,其中 依赖注入 步骤的实现是采用了模板方法设计模式中的典型做法,即将其实现委托给实现 BeanPostProcessor 接口的类。这样,子类可以通过实现 BeanPostProcessor 接口,对依赖注入 步骤进行自定义扩展,而不需要改变算法的结构。
- 可以看到,在该算法中,依赖注入步骤的具体实现是由添加的
BeanPostProcessor
对象实现的,这展示了模板方法设计模式的灵活性和可扩展性。
Bean后处理器
Bean后处理器是Spring框架中的一个扩展点,用于在Spring容器实例化Bean对象后,对Bean对象进行一些自定义的处理。通过实现BeanPostProcessor接口,并将实现类注册到Spring容器中,就可以在Spring容器创建Bean对象时拦截Bean对象并进行自定义处理。Bean后处理器可以用于实现AOP、事务管理、属性注入、异常处理等功能。
作用
主要分为6个阶段的Bean强化
- 初始化前
- 初始化后
- 依赖注入阶段
- 实例化前
- 实例化后
- 销毁前
常见的Bean后处理器
015-第四讲-常见bean后处理器1,2_哔哩哔哩_bilibili
public class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
private Bean2 bean2;
@Autowired
public void setBean2(Bean2 bean2) {
log.debug("@Autowired 生效: {}", bean2);
this.bean2 = bean2;
}
@Autowired
private Bean3 bean3;
@Resource
public void setBean3(Bean3 bean3) {
log.debug("@Resource 生效: {}", bean3);
this.bean3 = bean3;
}
private String home;
@Autowired
public void setHome(@Value("${JAVA_HOME}") String home) {
log.debug("@Value 生效: {}", home);
this.home = 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 +
", home='" + home + '\'' +
'}';
}
}
public class Bean2 {
}
public class Bean3 {
}
@ConfigurationProperties(prefix = "java")
public class Bean4 {
private String home;
private String version;
public String getHome() {
return home;
}
public void setHome(String home) {
this.home = home;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
@Override
public String toString() {
return "Bean4{" +
"home='" + home + '\'' +
", version='" + version + '\'' +
'}';
}
}
/*
bean 后处理器的作用
*/
public class A04 {
public static void main(String[] args) {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
// ⬇️用原始方法注册三个 bean
context.registerBean("bean1", Bean1.class);
context.registerBean("bean2", Bean2.class);
context.registerBean("bean3", Bean3.class);
context.registerBean("bean4", Bean4.class);
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());//负责解析值注入,后续会深入讲解
context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value
context.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroy
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());//@ConfigurationProperties
// ⬇️初始化容器
context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例
System.out.println(context.getBean(Bean4.class));
// ⬇️销毁容器
context.close();
}
}
运行结果
[DEBUG] 12:59:46.330 [main] com.itheima.a04.Bean1 - @Resource 生效: com.itheima.a04.Bean3@37858383
[DEBUG] 12:59:46.349 [main] com.itheima.a04.Bean1 - @Value 生效: C:\Users\ZGQ02\.jdks\temurin-1.8.0_362
[DEBUG] 12:59:46.352 [main] com.itheima.a04.Bean1 - @Autowired 生效: com.itheima.a04.Bean2@2fd1433e
[DEBUG] 12:59:46.352 [main] com.itheima.a04.Bean1 - @PostConstruct 生效
Bean4{home='C:\Users\ZGQ02\.jdks\corretto-17.0.7', version='17.0.7'}
[DEBUG] 12:59:46.442 [main] com.itheima.a04.Bean1 - @PreDestroy 生效
GenericApplicationContext
是Spring框架中的一种通用的应用上下文实现,它是一个干净的容器,也就是说它不会自动扫描和注册任何Bean对象。相比之下,像AnnotationConfigApplicationContext
等其他应用上下文实现,会自动扫描和注册标注了特定注解的Bean对象,比如@Component、@Service、@Repository、@Controller等。- 通过
context.getDefaultListableBeanFactory().setAutowireCandidateResolver()
方法设置了一个ContextAnnotationAutowireCandidateResolver
对象,用于解析依赖注入的值。 AutowiredAnnotationBeanPostProcessor
:用于解析@Autowired
和@Value
注解,完成自动装配的功能。CommonAnnotationBeanPostProcessor
:用于解析@Resource
、@PostConstruct
和@PreDestroy
等常用注解。ConfigurationPropertiesBindingPostProcessor
:用于解析@ConfigurationProperties
注解。
@Autowired bean后处理器执行分析
017-第四讲-@Autowired bean后处理器执行分析_哔哩哔哩_bilibili
public class DigInAutowired {
public static void main(String[] args) throws Throwable {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2()); // 忽略bean的创建过程,依赖注入,初始化
beanFactory.registerSingleton("bean3", new Bean3());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器
// ⬇️初始化容器
context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例
// ⬇️销毁容器
context.close();
}
}
-
registerSingleton
方法用于向Spring容器中注册一个单例Bean。它的作用是将一个已经创建好的Bean实例注册到Spring容器中,这个Bean实例只会被创建一次,并且在整个应用程序中都可以共享使用。当需要获取这个Bean时,Spring会直接返回这个已经创建好的实例,而不会再次创建新的实例。(单例模式)registerBeanDefinition
方法用于向Spring容器中注册一个Bean定义。它的作用是告诉Spring如何创建这个Bean,包括Bean的类型、构造函数参数、属性值等信息。当需要获取这个Bean时,Spring会根据这个Bean定义创建出一个新的Bean实例,每次获取Bean时都会创建一个新的实例。(可单例模式,多例模式)
接着上述代码,我们加入接下来的代码
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(beanFactory);
Bean1 bean1 = new Bean1(); //代码参照之前的Bean1,这里我们是手动注册bean1,并未交给spring管理
System.out.println(bean1);
processor.postProcessProperties(null, bean1, "bean1"); // 执行依赖注入 @Autowired @Value
System.out.println(bean1);
-
**processor.setBeanFactory(beanFactory);**将
AutowiredAnnotationBeanPostProcessor
的beanFactory
属性设置为指定的beanFactory
对象。具体来说,它将Spring容器的beanFactory
对象注入到AutowiredAnnotationBeanPostProcessor
中,使得AutowiredAnnotationBeanPostProcessor
可以通过beanFactory
对象来获取和管理Bean实例。 -
具体来说,
AutowiredAnnotationBeanPostProcessor
类继承自InstantiationAwareBeanPostProcessorAdapter
,并且实现了BeanFactoryAware
接口。这个接口中有一个setBeanFactory
方法,用于将Spring容器中的BeanFactory
对象注入到AutowiredAnnotationBeanPostProcessor
类中。当Spring容器启动时,会自动调用setBeanFactory
方法,将BeanFactory
对象注入到AutowiredAnnotationBeanPostProcessor
类中,从而使得AutowiredAnnotationBeanPostProcessor
可以通过BeanFactory
对象来获取和管理Bean实例。 -
processor.postProcessProperties(null, bean1, "bean1");
-
@Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { //1. 获取@Autowired信息 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { //2. 执行@Autowired注入 metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; }
-
通过反射调用
findAutowiringMetadata
方法-
//1. 获取 Bean 上加了 @Value @Autowired 的成员变量,方法参数信息 Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class); findAutowiringMetadata.setAccessible(true); InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);// 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息 System.out.println(metadata);
执行结果如下图所示,获取到Bean1类中标注有@Autowired的信息
-
-
调用InjectionMetadata metadata 的
inject()
方法-
//2. 调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值 metadata.inject(bean1, "bean1", null); System.out.println(bean1);
执行结果如下
-
-
再看到
inject()
方法的大概执行流程-
// 3. 如何按类型查找值 Field bean3 = Bean1.class.getDeclaredField("bean3"); //spring内部封装DependencyDescriptor类型对象,DependencyDescriptor对象的构造函数需要传入两个参数, 分别为依赖关系所对应的成员变量或方法参数,以及一个布尔值,表示是否允许依赖关系为null。我们通过构造 DependencyDescriptor对象来描述需要查找的依赖关系的信息。 DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false); /*doResolveDependency方法是BeanFactory接口中定义的一个方法, 它是Spring容器查找Bean实例或依赖关系的核心方法之一。*/ Object o = beanFactory.doResolveDependency(dd1, null, null, null); System.out.println(o); Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class); DependencyDescriptor dd2 = //true为ioc容器中必须在找到依赖关系,否则将报错 new DependencyDescriptor(new MethodParameter(setBean2, 0), true); //beanFactory.doResolveDependency()方法的主要作用是解析Bean实例的依赖关系,确保依赖关系的正确注入 Object o1 = beanFactory.doResolveDependency(dd2, null, null, null); System.out.println(o1); Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class); DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), true); Object o2 = beanFactory.doResolveDependency(dd3, null, null, null); System.out.println(o2);
运行结果如下图
-
-
- AutowiredAnnotationBeanPostProcessor.findAutowiringMetadata 用来获取某个 bean 上加了 @Value @Autowired 的成员变量,方法参数的信息,表示为 InjectionMetadata
- InjectionMetadata 可以完成依赖注入
- InjectionMetadata 内部根据成员变量,方法参数封装为 DependencyDescriptor 类型
- 有了 DependencyDescriptor,就可以利用 beanFactory.doResolveDependency 方法进行基于类型的查找
常见Bean工厂后处理器
component包下
@Component
public class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("我被 Spring 管理啦");
}
}
@Controller
public class Bean3 {
private static final Logger log = LoggerFactory.getLogger(Bean3.class);
public Bean3() {
log.debug("我被 Spring 管理啦");
}
}
public class Bean4 {
private static final Logger log = LoggerFactory.getLogger(Bean4.class);
public Bean4() {
log.debug("我被 Spring 管理啦");
}
}
测试代码
@Configuration
@ComponentScan("com.itheima.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 dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean
public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}
@Bean
public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}
}
/*
BeanFactory 后处理器的作用
*/
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
运行结果
加入ConfigurationClassPostProcessor.class
context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource
运行结果如下
ConfigurationClassPostProcessor.class
主要作用是补充bean的定义,解析@ComponentScan @Bean @Import @ImportResource注解
mapper包下
@Mapper
public interface Mapper1 {
}
@Mapper
public interface Mapper2 {
}
加入MapperScannerConfigurer.class
context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
bd.getPropertyValues().add("basePackage", "com.itheima.a05.mapper");
});
运行结果如下
-
带有mapper注解的类也被注册到了ioc中
-
如果使用了 @MapperScan 注解来指定扫描的包,那么还会生成一些额外的 Bean。这些额外的 Bean 包括:
- internalConfigurationAnnotationProcessor:用于解析 @Configuration 注解的 Bean 后处理器。
- internalAutowiredAnnotationProcessor:用于解析 @Autowired 注解的 Bean 后处理器。
- internalCommonAnnotationProcessor:用于解析通用注解(如 @PostConstruct、@PreDestroy 等)的 Bean 后处理器。
- internalEventListenerProcessor:用于处理事件监听器(如 @EventListener 注解)的 Bean 后处理器。
- internalEventListenerFactory:用于创建事件监听器的 Bean 工厂。
这些 Bean 是 Spring 框架自动添加的,用于支持注解的解析和事件监听等功能。
-
如果我们不想生成这些额外的 Bean,可以在注册 MapperScannerConfigurer 类型的 Bean 时,显式地禁用自动注入功能,例如:
context.registerBean(MapperScannerConfigurer.class, bd -> { bd.setAutowireCandidate(false); bd.getPropertyValues().add("basePackage", "com.itheima.a05.mapper"); });
在这个例子中,我们通过调用 bd.setAutowireCandidate(false) 方法来禁用自动注入功能,从而避免生成额外的 Bean。
Bean工厂后处理器模拟实现
ComponentScanPostProcessor
020-第五讲-工厂后处理器模拟实现-组件扫描_哔哩哔哩_bilibili
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override // context.refresh
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null) {
for (String p : componentScan.basePackages()) {
System.out.println(p);
// com.itheima.a05.component -> classpath*:com/itheima/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) {
// System.out.println(resource);
MetadataReader reader = factory.getMetadataReader(resource);
// System.out.println("类名:" + reader.getClassMetadata().getClassName());
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
// System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName())); /** System.out.println("是否加了 @Component 派生:"+annotationMetadata.hasMetaAnnotation(Component.class.getName()));
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
//获取Bean的定义信息
AbstractBeanDefinition bd = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
//生成Bean的名字
String name = generator.generateBeanName(bd, beanFactory);
//将Bean注册进入ioc
beanFactory.registerBeanDefinition(name, bd);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
AnnotationUtils.findAnnotation(Config.class, ComponentScan.class)
方法查找Config
类上的@ComponentScan
注解,并获取其basePackages
属性- 将每个包路径转换为对应的类路径,并使用
ClassPathResource
对象加载所有类文件资源,是因为Spring 框架中的组件扫描机制需要扫描 Java 类文件,以识别其中标记了特定注解的类,并将其实例化为 Bean 注册到 Spring 容器中。具体来说,当应用程序启动时,Spring 容器会通过扫描类路径下的所有类文件,检查其中标记了@Component
、@Service
、@Controller
、@Repository
等注解的类,并对其进行实例化和依赖注入等操作。 classpath*:
前缀是 Spring Framework 中用于指定类路径的特殊前缀,表示在所有的类路径中查找符合条件的资源。具体来说,它会在当前应用程序的类路径和所有 jar 包的类路径中查找符合条件的资源,以便更全面地扫描类和资源classpath
是 Java 中用于表示类路径的关键字,通常用于指定 Java 程序的类路径,以便程序能够正确地加载和运行类文件。具体来说,类路径是一个包含多个目录和 jar 包的路径列表,它告诉 Java 虚拟机在哪里查找类文件和资源文件(通常用来指代src/main/resources
目录)classpath*
和classpath
的区别在于,它会在classpath
的基础上,再搜索所有的 jar 包文件(包括子目录中的 jar 包文件)中的类文件和资源文件。这意味着,如果一个类或资源文件在 jar 包中,而不在classpath
中的目录中,那么使用classpath
是无法找到它的,而必须使用classpath*
。
CachingMetadataReaderFactory
是 Spring Framework 中的一个工具类,用于读取 Java 类文件的元数据信息。它可以读取指定类文件的类名、包名、注解、继承关系等信息,以及类文件所在的资源路径和 URL。factory
是一个CachingMetadataReaderFactory
对象,resource
表示要读取的类文件资源metadataReader
是由factory
创建的MetadataReader
对象,用于读取类文件的元数据信息。通过metadataReader
可以获取类名、注解等信息。metadataReader.getClassMetadata().getClassName()
用于获取类名,metadataReader.getAnnotationMetadata()
用于获取类文件的注解信息。metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName())
判断类文件是否被标注了@Component
注解,metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())
判断类文件是否被标注了@Component
注解的派生注解。
再将我们定义的ComponentScanPostProcessor,加入到容器中
context.registerBean(ComponentScanPostProcessor.class); // 解析 @ComponentScan
运行结果如下
config
com.itheima.a05.ComponentScanPostProcessor
bean2
bean3
AtBeanPostProcessor
022-第五讲-工厂后处理器模拟实现-@Bean_哔哩哔哩_bilibili
public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itheima/a05/Config.class"));
Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata method : methods) {
System.out.println(method);
String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(method.getMethodName(), "config");
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
if (initMethod.length() > 0) {
builder.setInitMethodName(initMethod);
}
AbstractBeanDefinition bd = builder.getBeanDefinition();
beanFactory.registerBeanDefinition(method.getMethodName(), bd);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
首先,创建了一个
CachingMetadataReaderFactory
对象,用于读取类文件的元数据信息。然后,通过getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName())
方法获取了被标注了@Bean
注解的方法列表。接着,遍历这些方法,并根据方法的元数据信息动态创建 BeanDefinition,并将其注册到 Spring 容器中。 -
然后,对于每个被标注了
@Bean
注解的方法,创建了一个BeanDefinitionBuilder
对象,并设置了其工厂方法和构造函数自动装配模式。如果方法上指定了initMethod
属性,则将其设置为 Bean 的初始化方法。最后,将该 BeanDefinition 注册到 Spring 容器中,以方法名作为 Bean 的名称。
运行结果如下
config
com.itheima.a05.AtBeanPostProcessor
bean1
sqlSessionFactoryBean
dataSource
MapperPostProcessor
023-第五讲-工厂后处理器模拟实现-Mapper_哔哩哔哩_bilibili
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:com/itheima/a05/mapper/**/*.class");
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
for (Resource resource : resources) {
MetadataReader reader = factory.getMetadataReader(resource);
ClassMetadata classMetadata = reader.getClassMetadata();
if (classMetadata.isInterface()) {
AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
.addConstructorArgValue(classMetadata.getClassName())
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
AbstractBeanDefinition bd2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
String name = generator.generateBeanName(bd2, beanFactory);
beanFactory.registerBeanDefinition(name, bd);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
-
首先,创建了一个
PathMatchingResourcePatternResolver
对象,用于查找指定包下的所有类文件。然后,使用getResource
方法获取指定包下所有类文件的资源数组。接着,创建了一个AnnotationBeanNameGenerator
对象,用于生成 Bean 的名称。 -
接下来,遍历每个类文件资源,使用
CachingMetadataReaderFactory
对象读取类文件的元数据信息,并判断该类是否为接口。如果是接口,则创建一个MapperFactoryBean
对象,并使用该对象的构造函数参数传入接口的类名。然后,创建一个AbstractBeanDefinition
对象,并将其设置为MapperFactoryBean
的 BeanDefinition,并注册到 Spring 容器中。同时,创建另一个AbstractBeanDefinition
对象,并将其设置为接口的 BeanDefinition,并使用AnnotationBeanNameGenerator
生成 Bean 的名称,并将其注册到 Spring 容器中。 -
实际上注册到 IOC 容器中的是
MapperFactoryBean
对象,而非 Mapper 接口本身。当需要使用该 Mapper 接口时,Spring 容器会根据MapperFactoryBean
的 BeanDefinition 创建对应的 Bean 实例,并使用该实例的getObject()
方法返回该 Mapper 接口的代理对象。在使用该 Mapper 接口的代理对象时,实际上调用的是
MapperFactoryBean
中创建的代理对象,而非 Mapper 接口本身
转载自:https://juejin.cn/post/7261799437730054199