BeanFactory与ApplicationContext的区别与联系
启动类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MSYAk9og-1662393502579)(${graph}/image-20220905001956519.png)]
springBoot引导类:
package com.deng;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class HeimaSpringApplication {
public static void main(String[] args) {
// BeanFactory 核心容器
// ApplicationContext是BeanFactory的实现类,ApplicationContext还实现了其他功能,
// 也就是说ApplicationContext增强了BeanFactory
ConfigurableApplicationContext context = SpringApplication.run(HeimaSpringApplication.class, args);
// org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
System.out.println(context.getClass());
}
}
ConfigurableApplicationContext的实现类为AnnotationConfigServletWebServerApplicationContext
AnnotationConfigServletWebServerApplicationContext又间接继承了GenericApplicationContext,在这个类里面可以找到beanFactory作为成员变量出现。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FvcpmweW-1662393502580)(${graph}/image-20220905015033230.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9rHwkq2q-1662393502581)(${graph}/image-20220905014403662.png)]
结构图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rRE0fZ6I-1662393502582)(${graph}/image-20220905002757537.png)]
BeanFactory的默认实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZR96sj2e-1662393502582)(${graph}/image-20220905005121319.png)]
package com.deng;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class HeimaSpringApplication {
public static void main(String[] args) {
// org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
ConfigurableApplicationContext context = SpringApplication.run(HeimaSpringApplication.class, args);
// class org.springframework.beans.factory.support.DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
}
}
这里我们暂且不细看DefaultListableBeanFactory,先看DefaultListableBeanFactory的父类DefaultSingletonBeanFactory,可以看到有个私有的成员变量singletonObjects 存储单例对象
DefaultListableBeanFactory:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UWnNXwC9-1662393502583)(${graph}/image-20220905005255993.png)]
ApplicationContext的功能
ApplicationContext 比 BeanFactory 多点啥?
多实现了四个接口:
MessageSource
: 国际化功能,支持多种语言
ResourcePatternResolver
: 通配符匹配资源路径
EnvironmentCapable
: 环境信息,系统环境变量,.properties、.application.yml等配置文件中的值
ApplicationEventPublisher
: 发布事件对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LbSrwLIL-1662393502584)(${graph}/image-20220905143653039.png)]
MessageSource
国际化功能,支持多种语言
public interface MessageSource {
@Nullable
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
在resources目录下创建四个文件messages.propertes、messages_en.properties、messages_ja.properties、messages_zh.properties,然后分别在四个文件里面定义同名的key,比如在message_en.properties中定义hi=hello,在messages_zh中定义hi=你好,这样在代码中就可以根据这个key hi和不同的语言类型获取不同的value了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nf4E4sWU-1662393502585)(${graph}/image-20220905145922331.png)]
System.out.println(context.getMessage("hi", null, Locale.CHINA));
System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HxJzPCTR-1662393502586)(${graph}/image-20220905150005362.png)]
ResourcePatternResolver
通配符匹配资源路径,返回匹配的资源
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
获取jar包中资源:
//Resource[] resources = context.getResources("classpath:application.properties");
// classes* 可以取jar包下拿取资源
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
for (Resource resource : resources) {
System.out.println(resource);
}
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xouP62Mq-1662393502587)(${graph}/image-20220905151512155.png)]
EnvironmentCapable
获取环境信息,系统环境变量,.properties、.application.yml等配置文件中的值
public interface EnvironmentCapable {
Environment getEnvironment();
}
获得java_home和application.properties中的内容
System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("spring.messages.encoding"));
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mzRnw2W1-1662393502587)(${graph}/image-20220905151651508.png)]
ApplicationEventPublisher
发布事件对象
package org.springframework.context;
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}
定义一个用户注册事件类,继承自ApplicationEvent类
package com.deng;
import org.springframework.context.ApplicationEvent;
public class UserRegisteredEvent extends ApplicationEvent {
public UserRegisteredEvent(Object source) {
super(source);
}
发布事件:
context.publishEvent(new UserRegisteredEvent(context));
监听事件:
package com.deng;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class Component1 {
@EventListener
public void message(UserRegisteredEvent event) {
System.out.println("事件处理");
}
}
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jqkQd27J-1662393502588)(${graph}/image-20220905153203518.png)]
容器实现
BeanFactory实现的特点
DefaultListableBeanFactory的使用
BeanFactory后置处理器与Bean后置处理器
package com.deng;
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;
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();
// 注册config的bean定义信息
beanFactory.registerBeanDefinition("config", beanDefinition);
// 给 BeanFactory添加一些常用的后处理器(存储到内部容器),让它具备解析@Configuration、@Bean等注解的能力
// BeanFactory 后处理器主要功能,补充了一些 bean 的定义
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// 从bean工厂中取出BeanFactory的后处理器
// 比如internalConfigurationAnnotationProcessor
// 将解析注解,将bean定义信息添加到beanFactory
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
System.out.println(beanFactoryPostProcessor);
// 处理
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
// 从BeanFactory中取出Bean1,然后再从Bean1中取出它依赖的Bean2
// 可以看到结果为null,所以@Autowired注解并没有被解析
Bean1 bean1 = beanFactory.getBean(Bean1.class);
// 要想@Autowired、@Resource等注解被解析,还要添加Bean的后处理器,可以针对Bean的生命周期的各个阶段提供扩展
// 从bean工厂中取出Bean的后处理器,并且执行这些后处理器
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
// 提前创建对象
beanFactory.preInstantiateSingletons();
// 此时,@Autowired已经被解析
Bean1 bean = beanFactory.getBean(Bean1.class);
// 打印BeanFactory中Bean
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean1 {
}
static class Bean2 {
}
}
结果:
// AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);加入的bean
// 也就是后面的5个bean
//internalConfigurationAnnotationProcessor 去解析注解,将bean定义信息注册到BeanFactory中
// BeanFactory本身不具备解析注解的功能
// beanFactory 的后处理
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
// bean的后处理器 @Autowire 针对于bean的生命周期
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
// bean的后处理器 @Resource
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2
BeanFactory后置增强的几个实现类
@ComponentScan,@Bean,@Configuration
class org.springframework.context.annotation.ConfigurationClassPostProcessor
class org.springframework.context.event.EventListenerMethodProcessor
Bean后置增强的几个实现类
@Autowire
class org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
@Resource
class org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
BeanFacory的特点
a. beanFactory 不会做的事
1. 不会主动调用BeanFactory的后处理器
2. 不会主动添加Bean的后处理器
3. 不会主动初始化单例
4. 不会解析BeanFactory,还不会解析 ${}, #{}
b. Bean后处理器会有排序的逻辑
Bean后置处理器顺序
当一个属性同时被@Autowire 与 @Resource修饰时
起决定的是他们对应的Bean后置处理器的加入顺序,早加入的优先级高,就处理。
ApplicaionContext的常见实现与用法
ClassPathXmlApplicationContext:
FileSystemXmlApplicationContext:
AnnotationConfigApplicationContext:
AnnotationConfigServletWebServerApplication:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jBwToTrM-1662393502589)(${graph}/bec10309f6c068585fd7382f96ae8d80.png)]
@Slf4j
public class TestApplicationContext {
@Test
// 1.最为经典的容器,基于classpath 下 xml 格式的配置文件来创建
public void testClassPathXmlApplicationContext() {
// 如果xml中没有<context:annotation-config/>
// 则无法解析@Autowire这样的注解
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() {
// 这里返回了一个实现Controller接口的类
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-config.xml
// 加入后处理器
<context:annotation-config/>
Bean的生命周期
生命周期
package com.demo01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class BeanLifeCycleApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BeanLifeCycleApplication.class, args);
context.close();
}
}
LifeCycleBean:
package com.demo01;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
@Slf4j
public class LifeCycleBean {
public LifeCycleBean() {
log.info("构造");
}
@Autowired
public void autowire(@Value("${JAVA_HOME}") String name) {
log.info("依赖注入:{}", name);
}
@PostConstruct
public void init() {
log.info("初始化");
}
@PreDestroy
public void destroy() {
log.info("销毁");
}
}
MyBeanPostProcessor
:
package com.demo01;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {
@Override
// 实例化前(即调用构造方法前)执行的方法
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.info("<<<<<<<<<<< 实例化前执行");
// 返回null保持原有对象不变,返回不为null,会替换掉原有对象
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 void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if(beanName.equals("lifeCycleBean"))
log.info("<<<<<<<<<<<销毁之前执行");
}
@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;
}
}
结果:
com.demo01.MyBeanPostProcessor : <<<<<<<<<<< 实例化前执行,
com.demo01.LifeCycleBean : 构造
com.demo01.MyBeanPostProcessor : <<<<<<<<<<< 实例化后执行,这里如果返回 false 会跳过依赖注入阶段
com.demo01.MyBeanPostProcessor : <<<<<<<<<<< 依赖注入阶段执行,如@Autowired、@Value、@Resource
com.demo01.LifeCycleBean : 依赖注入:D:\java\jdk1.8.0_291
com.demo01.MyBeanPostProcessor : <<<<<<<<<<< 初始化之前执行,这里返回的对象会替换掉原本的bean,如 @PostConstruct、@ConfigurationProperties
com.demo01.LifeCycleBean : 初始化
com.demo01.MyBeanPostProcessor : <<<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的bean,如 代理增强
com.demo01.MyBeanPostProcessor : <<<<<<<<<<<销毁之前执行
com.demo01.LifeCycleBean : 销毁
模板方法
package com.demo01;
import java.util.ArrayList;
import java.util.List;
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后置处理器
后置处理器
定义三个Bean,名称分别为Bean1,Bean2,Bean3,其中Bean1中依赖了Bean2和Bean3,Bean2通过@Autowired的注解注入,Bean3通过@Resource注解注入,再通过@Value注解注入一个Java的环境变量JAVA_HOME的值。最后定义两个方法init()和destroy(),分别加上@PostConstruct和@PreDestroy注解。
package com.demo02;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
@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 {
}
// springboot 中的注解,自动读取配置文件
@ConfigurationProperties(prefix = "java")
@Slf4j
class Bean4 {
// 读取java.home
private String home;
// 读取java.version
private String version;
}
这里用GenericApplicationContext 来探究一下@Autowired、@Value、@Resource、@PostConstruct、@PreDestroy以及springboot项目中的@ConfigurationProperties这些注解分别是由哪个后处理器来解析的。
注:GenericApplicationContext 是一个【干净】的容器,默认不会添加任何后处理器,方便做测试,这里用DefaultListableBeanFactory也可以完成测试,只是会比使用GenericApplicationContext麻烦一些。
测试:
package com.demo02;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;
@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();
}
}
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yhmqr6a3-1662393502590)(${graph}/image-20220905205110097.png)]
@Autowired处理器
本案例测试代码紧接着上面,这里对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);
}
}
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yCe3eYbj-1662393502590)(${graph}/08274455f402d5eb3309c3ba1670c5d8.png)]
BeanFactory后置处理器
测试使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tDcROakJ-1662393502591)(${graph}/image-20220905234503378.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ixMvg0CT-1662393502591)(${graph}/image-20220905234539224.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-acesPthg-1662393502592)(${graph}/image-20220905234632839.png)]
测试代码:
package com.demo03;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import java.io.IOException;
@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();
}
}
经过测试和运行结果的比对:
@ComponentScan 中 包含了@component
@component由其他处理器解析
@ComponentScan
、@Bean
对应的Bean
工厂后处理器是ConfigurationClassPostProcessor
;@MapperScan
对应的Bean
工厂后处理器是MapperScannerConfigurer
。
模拟
Bean后置处理器与BeanFactory后置处理器对比
BeanFactory 后置处理器 : 添加更多的Bean定义信息
Bean后置处理器 :维护bean的生命周期
BeanFactory后置增强的几个实现类
@ComponentScan,@Bean,@Configuration
class org.springframework.context.annotation.ConfigurationClassPostProcessor
class org.springframework.context.event.EventListenerMethodProcessor
Bean后置增强的几个实现类
@Autowire
class org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
@Resource
class org.springframework.context.annotation.CommonAnnotationBeanPostProcessor