全面深度讲解Spring5底层原理
本文章基于B站UP主"黑马程序员"视频教程《全面深度讲解spring5底层原理》进行整理记录,仅用于个人学习/交流使用。
参考:https://www.bilibili.com/video/BV1P44y1N7QG
时间:2022年4月
作者:耿鬼不会笑
文章目录
- 全面深度讲解Spring5底层原理
- 第一章:容器与Bean
- 1.BeanFactory和ApplicationContext
- 2.BeanFactory的实现
- 3.AppliactionContext的常见实现和用法
- 4.Bean的生命周期
- 5.模板方法模式
- 6.常见的Bean后置处理器
- 7.AutowiredAnnotationBeanPostProcessor
- 8.BeanFactory后置处理器
- 9.Aware接口
- 10.@Autowired失效分析
- 11.Spring初始化和销毁
- 12.Scope
第一章:容器与Bean
1.BeanFactory和ApplicationContext
Q:BeanFactory和ApplicationContext的关系
BeanFactory是ApplicationContext的父接口,它才是Spring的核心容器
ApplicationContext间接继承了BeanFactory,通过"组合"的方式(BeanFactory是ApplicationContext的一个成员变量,ApplicationContext的部分功能是直接调用BeanFactory的功能,例如getBean),对BeanFactory的功能提供了一些扩展
Q:BeanFactory的功能
1.BeanFactory中提供的方法
2.BenaFactory的实现类
实际上,控制反转,基本的依赖注入,直至Bean的生命周期的各种功能,都由BeanFactory的实现类提供。
Q:BenaFactory的实现类之DefaultListableBeanFactory
DefaultListableBeanFactory管理所有对象(尤其是管理单例对象),是BeanFactory的一个实现
DefaultSingletonBeanFactory管理所有的单例对象
Map的key是单例对象的名字,value是单例对象
演示:获取所有以component开头的单例对象
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
.forEach(e -> {
System.out.println(e.getKey() + "=" + e.getValue());
});
Q:ApplicationContext对于BeanFactory的扩展
MessageSource演示:处理国际化资源(context.getMessage)
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 也必须存在
ResourcePatternResolver演示:根据通配符获取一个或多个资源
ApplicationEventPublisher演示:发布事件对象(基本使用)
ApplicationEventPublisher演示:场景应用:注册后发送短信,主要作用:实现组件之间的解耦
EnvironmentCapable演示:获取配置信息(系统环境变量 和 配置文件中的信息)
Q:BeanFactory和ApplianceContext的总结
到底什么是 BeanFactory
- 它是 ApplicationContext 的父接口
- 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory
BeanFactory 能干点啥
- 表面上只有 getBean
- 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供
- 例子中通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean
ApplicationContext 比 BeanFactory 多点啥
- ApplicationContext 组合并扩展了 BeanFactory 的功能
- 国际化、通配符方式获取一组 Resource 资源、整合 Environment 环境、事件发布与监听
- 新学一种代码之间解耦途径,
事件解耦
建议练习:完成用户注册与发送短信之间的解耦,用事件方式、和 AOP 方式分别实现
注意
- 如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED,这是因为这些版本的 jdk 默认不允许跨 module 反射
- 事件发布还可以异步,这个视频中没有展示,请自行查阅 @EnableAsync,@Async 的用法
2.BeanFactory的实现
Q:如何自己定义一个bean
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义(class(类型), scope(单例or多例), 初始化方法, 销毁方法)
// BeanDefinitionBuilder..xxx.xxx.xxx.getBeanDefinition()
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//(beanName,xxx)
beanFactory.registerBeanDefinition("config", beanDefinition);
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
输出结果为config,只有自己定义的bean,如果我们使用@Configuration、@Bean等注解,使用注解标注的bean也不会被扫描
总结:原始的BeanFactory没有解析注解的能力
Q:如何扫描使用注解声明的bean
1、给 BeanFactory 添加一些常用的后处理器
Tips:后置处理器有多种,这里指的是针对bean工厂的后置处理器,后面还会讲针对依赖注入的后置处理器
2、执行BeanFactory后置处理器
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义(class(类型), scope(单例or多例), 初始化方法, 销毁方法)
// BeanDefinitionBuilder..xxx.xxx.xxx.getBeanDefinition()
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//(beanName,xxx)
beanFactory.registerBeanDefinition("config", beanDefinition);
// 【重点】给 BeanFactory 添加一些常用的后处理器(对BeanFactory的扩展)
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// BeanFactory 后处理器主要功能,补充了一些 bean 定义
// beanFactory.getBeansOfType 根据类型获取多个bean
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);//执行BeanFactory后置处理器
});
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 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
@Autowired
private Bean2 bean2;
}
static class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
}
}
输出结果:
config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2
补充:当只添加了后置处理器,并没有启动后置处理器时
Q:能否取出bean1、bean2正常使用
结论:不能,bean2为null
原因:@Autowired对于BeanFactory而言也是扩展功能,需要使用【bean的后置处理器来解析@Autowired、@Value注解】
Q:如何解决上面出现的问题
解决方案:添加bean的后置处理器
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义(class(类型), scope(单例or多例), 初始化方法, 销毁方法)
// BeanDefinitionBuilder..xxx.xxx.xxx.getBeanDefinition()
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//(beanName,xxx)
beanFactory.registerBeanDefinition("config", beanDefinition);
// 【重点】给 BeanFactory 添加一些常用的后处理器(对BeanFactory的扩展)
// 把后置处理器加到bean工厂
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// BeanFactory 后处理器主要功能,补充了一些 bean 定义
// beanFactory.getBeansOfType 根据类型获取多个bean
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);//执行BeanFactory后置处理器
});
// Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...
// 建立后置处理器和bean工厂的联系
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
}
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
public Bean1() {
log.debug("构造 Bean1()");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2() {
return bean2;
}
}
static class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("构造 Bean2()");
}
}
}
Q:单例对象的创建时机
默认情况下(延迟创建):延时实例化、初始化,即当用到bean1、bean2时才会去创建
预先创建:预先处理(创建)所用的单例对象,beanFactory.preInstantiateSingletons();
Q:总结:bean工厂后置处理器 & bean后置处理器
bean工厂后置处理器:补充了一些 bean 定义
1.org.springframework.context.annotation.internalConfigurationAnnotationProcessor
- 解析@Configuration、@Bean注解
bean的后置处理器:针对每个bean的生命周期(创建、依赖注入、初始化)提供扩展功能
1.org.springframework.context.annotation.internalAutowiredAnnotationProcessor
- 解析@Autowired、@Value
2.org.springframework.context.annotation.internalCommonAnnotationProcessor
- 解析@Resource注解(JavaEE注解,非Spring注解)
Q:bean 后处理器会有排序的逻辑
当同时使用@Autowired、@Resource时,哪个会生效
AnnotationConfigUtils中含有多个bean注解解析器
@Autowired、@Resource注解会先解析哪一个?
自定义解析顺序(使用比较器)
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义(class(类型), scope(单例or多例), 初始化方法, 销毁方法)
// BeanDefinitionBuilder..xxx.xxx.xxx.getBeanDefinition()
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//(beanName,xxx)
beanFactory.registerBeanDefinition("config", beanDefinition);
// 【重点】给 BeanFactory 添加一些常用的后处理器(对BeanFactory的扩展)
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// BeanFactory 后处理器主要功能,补充了一些 bean 定义
// beanFactory.getBeansOfType 根据类型获取多个bean
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);//执行BeanFactory后置处理器
});
// Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...
// 打印 beanPostProcessor出现的顺序
// beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanPostProcessor -> {
// System.out.println(">>>"+beanPostProcessor);
// beanFactory.addBeanPostProcessor(beanPostProcessor);
// });
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
.sorted(beanFactory.getDependencyComparator())
.forEach(beanPostProcessor -> {
System.out.println(">>>>" + beanPostProcessor);
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(beanFactory.getBean(Bean1.class).getInter());
// 打印注解排序,实现order接口实现,数字小的排在前面
// 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;
//当这里写的是 @Autowired private Inter inter; 时,会报错
//原因:@Autowired是根据类型匹配,bean3、bean4都是inter
public Inter getInter() {
return bean3;
}
}
static class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("构造 Bean2()");
}
}
}
Q:BeanFactory总结
a.beanFactory 不会做的事,这些事情都需要自己来完成
-
不会主动调用 BeanFactory 后处理器
-
不会主动添加 Bean 后处理器
-
不会主动初始化单例
-
不会解析beanFactory 还不会解析 ${ } 与 #{ } //$占位符,Spring中的EL表达式
那么BeanFactory不会做的话,AppliactionContext就会自己做
b.bean 后处理器会有排序的逻辑
3.AppliactionContext的常见实现和用法
ClassPathXmlApplicationContext
从类路径下读取xml配置文件,并根据xml配置文件的内容来创建Spring中的AppliactionContext容器
// 较为经典的容器, 基于 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 version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 控制反转, 让 bean1 被 Spring 容器管理 -->
<bean id="bean1" class="com.itheima.a02.A02.Bean1"/>
<!-- 控制反转, 让 bean2 被 Spring 容器管理 -->
<bean id="bean2" class="com.itheima.a02.A02.Bean2">
<!-- 依赖注入, 建立与 bean1 的依赖关系 -->
<property name="bean1" ref="bean1"/>
</bean>
<!--添加bean后置处理器的标签->
<context:annotation-config/>
</beans>
FileSystemXmlApplicationContext
从磁盘路径下读取xml配置文件,并根据xml配置文件的内容来创建Spring中的AppliactionContext容器
这里写绝对路径、相对路径都可以
// 基于磁盘路径下 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());
}
Q:XmlApplicationContext实现机理
public static void main(String[] args) {
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);
}
}
创建DefaultListableBeanFactory对象,作为bean工厂
创建XmlBeanDefinitionReader,读取bean,读取xml标签,转化为bean定义,放到 defaultListableBeanFactory 里面
AnnotationConfigApplicationContext
基于注解(配置类)的实现方式
其中配置类也是一个bean
还会自动为你添加相关的bean后置处理器
// 较为经典的容器, 基于 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());
}
@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;
}
}
AnnotationConfigServletWebServerApplicationContext
支持Java配置类,以及Servlet服务器(web环境)的创建方式
// 较为经典的容器, 基于 java 配置类来创建, 用于 web 环境
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
//Web环境需要更多的配置信息/组件
@Configuration
static class WebConfig {
@Bean //产生内嵌的Tomcat服务器
public ServletWebServerFactory servletWebServerFactory(){
return new TomcatServletWebServerFactory();
}
@Bean //核心,DispatcherServlet,请求入口点,【前控制器】
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean //把 DispatcherServlet 注册到 Tomcat服务器 中
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean("/hello") //控制器,实现Controller接口
public Controller controller1() {
return (request, response) -> {
response.getWriter().print("hello");
return null;
};
}
}
4.Bean的生命周期
执行顺序:构造方法、依赖注入的方法、bean的初始化方法、bean的销毁方法
同时,在执行的各个阶段,可以使用 后置处理器 进行增强
@SpringBootApplication
public class A03 {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A03.class, args);
context.close();
}
}
@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("销毁");
}
}
@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;
}
}
5.模板方法模式
提高代码的扩展能力
固定不变的形成方法的主干、经常变化的抽象成接口
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); // 对依赖注入阶段的扩展
}
}
6.常见的Bean后置处理器
常见的Bean后置处理器1和2
Bean后处理器的作用:为Bean生命周期的各个阶段提供扩展
AutowiredAnnotationBeanPostProcessor.class
解析:@Autowired @Value
执行时机:依赖注入阶段
单独使用时不能解析 “值注入” 的内容
@Autowired
public void setHome(@Value("${JAVA_HOME}") String home) {
需要换一个实现,即使用
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value
CommonAnnotationBeanPostProcessor.class
解析:@Resource @PostConstruct(初始化) @PreDestroy(销毁)
执行时机:@Resource(依赖注入) 、@PostConstruct(初始化前,虽然叫初始化后) @PreDestroy(销毁前)
程序源码:
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.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value
context.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroy
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());
// 初始化容器
context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例
System.out.println(context.getBean(Bean1.class));
// 销毁容器
context.close();
}
}
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 {
}
常见的Bean后置处理器3
SpringBoot中的后置处理器
ConfigurationPropertiesBindingPostProcessor
解析:@ConfigurationProperties(prefix = "xxx")
执行时机:初始化前
SpringBoot中的属性绑定功能:根据前缀以及属性名称匹配配置文件中的信息进行绑定
/* [@ConfigurationProperties,prefix = "java",属性name、version,可以匹配到: ]
java.home=
java.version=
*/
@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 + '\'' +
'}';
}
}
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());
// 初始化容器
context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例
System.out.println(context.getBean(Bean1.class));
// 销毁容器
context.close();
/*
学到了什么
a. @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能
b. 这些扩展功能由 bean 后处理器来完成
*/
}
}
7.AutowiredAnnotationBeanPostProcessor
// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {
public static void main(String[] args) throws Throwable {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2()); // new Bean2()会认为这个是一个成熟的bean,不会再走 创建过程,依赖注入,初始化
beanFactory.registerSingleton("bean3", new Bean3());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
// 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(beanFactory);
Bean1 bean1 = new Bean1();
System.out.println(bean1);
processor.postProcessProperties(null, bean1, "bean1"); //【重点】 2.执行依赖注入,解析@Autowired @Value
System.out.println(bean1);
}
}
processor.postProcessProperties 执行过程
不直接使用 processor.postProcessProperties ,自己模拟执行过程
// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {
public static void main(String[] args) throws Throwable {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2()); // new Bean2()会认为这个是一个成熟的bean,不会再走 创建过程,依赖注入,初始化
beanFactory.registerSingleton("bean3", new Bean3());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
//【新添加的内容:${} 的解析器】
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);
// 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(beanFactory);
Bean1 bean1 = new Bean1();
// System.out.println(bean1);
// processor.postProcessProperties(null, bean1, "bean1"); // 执行依赖注入,解析@Autowired @Value
// System.out.println(bean1);
Method 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);
}
}
metadata里面有什么?
如何按类型查找值
// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {
public static void main(String[] args) throws Throwable {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2()); // new Bean2()会认为这个是一个成熟的bean,不会再走 创建过程,依赖注入,初始化
beanFactory.registerSingleton("bean3", new Bean3());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器
// 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(beanFactory);
// 3. 如何按类型查找值
// 查找属性
Field bean3 = Bean1.class.getDeclaredField("bean3");
DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
//根据成员变量的类型去找要注入谁
Object o = beanFactory.doResolveDependency(dd1, null, null, null);
System.out.println(o);
//查找方法
Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
DependencyDescriptor dd2 =
new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
//根据方法参数的类型(Bean2 bean2)到容器里面去找,看看有没有一个Bean2类型的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);
}
}
@Autowired
public void setBean2(Bean2 bean2) {
log.debug("@Autowired 生效: {}", bean2);
this.bean2 = bean2;
}
@Autowired
private Bean3 bean3;
@Autowired
public void setHome(@Value("${JAVA_HOME}") String home) {
log.debug("@Value 生效: {}", home);
this.home = home;
}
总结:metadata.inject() 做了哪些事情?
1.找到标注了@Autowired的成员变量、方法信息
2.封装成一个DependencyDescriptor
3.调用 beanFactory.doResolveDependency()方法
4.传入参数,根据属性类型、方法参数类型
进一步查找
8.BeanFactory后置处理器
BeanFactory后置处理器的作用:为BeanFactory提供扩展
常用的BeanFactory后置处理器
ConfigurationClassPostProcessor
- 解析@ComponentScan注解、扫描一些组件,注册到bean工厂里面
- 解析@Bean注解,把@Bena标注的方法,作为BeanDefinition,注册到bean工厂
- 解析@Import注解
- 解析@ImportResource注解
MapperScannerConfigurer
- 根Mybatis整合时经常用到的BeanFactory后置处理器
- 解析@MapperScan,在SSM项目中常用的注解
- 扫描Mapper接口,作为BeanDefinition,注册到bean工厂
- 很少用了,如果用SpringBoot整合,一般间接配置
/*
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.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource
context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
bd.getPropertyValues().add("basePackage", "com.itheima.a05.mapper");
});
// 初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
}
@Configuration
@ComponentScan("com.itheima.a05.component")
public class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
}
@Mapper
public interface Mapper1 {
}
模拟BeanFactory后处理器解析@ComponentScan
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.registerBean(ComponentScanPostProcessor.class); // 解析 @ComponentScan
context.registerBean(AtBeanPostProcessor.class); // 解析 @Bean
context.registerBean(MapperPostProcessor.class); // 解析 Mapper 接口
// 初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
}
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override // context.refresh
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
//【1】使用AnnotationUtils看看Config.class上是不是有注解@ComponentScan
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null) {
for (String p : componentScan.basePackages()) {
System.out.println(p);
// 【2】把获取的 包名 转化为 类路径名
// com.itheima.a05.component -> classpath*:com/itheima/a05/component/**/*.class
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
System.out.println(path);
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//【3】getResources获取类路径下的所有资源
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());
//【4】查看是不是加了对应的注解
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())) {
//【5】如果直接或间接加了注解,创建BeanDefinition
AbstractBeanDefinition bd = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
String name = generator.generateBeanName(bd, beanFactory);
//【6】加入到bean工厂里面,(bean的名字,bean)
beanFactory.registerBeanDefinition(name, bd);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
模拟BeanFactory后处理器解析@Bean
public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
//【1】
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//【2】获取类路径下的 数据元信息 MetadataReader
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itheima/a05/Config.class"));
//【3】寻找标注了@Bean注解的方法集合
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();
//【4】定义了一下配置类“config”的工厂方法
builder.setFactoryMethodOnBean(method.getMethodName(), "config");
//【5】定义自动装配模式
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
if (initMethod.length() > 0) {
builder.setInitMethodName(initMethod);
}
AbstractBeanDefinition bd = builder.getBeanDefinition();
//【6】注册bean到工厂
beanFactory.registerBeanDefinition(method.getMethodName(), bd);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
模拟BeanFactory后处理器解析@ComponentScan
Spring不能直接管理 Mapper接口,只能管理对象
需要使用工厂对象,MapperFactoryBean
基本使用方式,缺点:不能批量添加
@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;
}
}
改进:
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()) {
//生成的是MapperBeanDefinition
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 {
}
}
9.Aware接口
Aware接口提供了一种【内置】的注入手段,可以注入BeanFactory, ApplicationContext
InitializingBean接口提供了一种【内置】的初始化手段
内置的注入和初始化不受扩展功能的影响,总会被执行,因此Spring框架内部的类常用它们
Q:Aware 接口有什么作用?
Aware 接口用于注入一些与容器相关信息, 例如
-
a.BeanNameAware 注入 bean 的名字
-
b.BeanFactoryAware 注入 BeanFactory 容器
-
c.ApplicationContextAware 注入 ApplicationContext 容器
-
d.EmbeddedValueResolverAware 解析 ${}
/*
Aware 接口及 InitializingBean 接口
*/
public class A06 {
private static final Logger log = LoggerFactory.getLogger(A06.class);
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myBean", MyBean.class);
context.refresh();
context.close();
}
}
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
private static final Logger log = LoggerFactory.getLogger(MyBean.class);
//From:BeanNameAware
@Override
public void setBeanName(String name) {
log.debug("当前bean " + this + " 名字叫:" + name);
}
//From:ApplicationContextAware
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.debug("当前bean " + this + " 容器是:" + applicationContext);
}
//From:InitializingBean
@Override
public void afterPropertiesSet() throws Exception {
log.debug("当前bean " + this + " 初始化");
}
}
Q:部分功能用 @Autowired 就能实现啊, 为啥还要用 Aware 接口呢
有同学说: b、c、d 的功能用 @Autowired 就能实现啊, 为啥还要用 Aware 接口呢
简单地说:
-
a.@Autowired 的解析需要用到 bean 后处理器, 属于扩展功能
-
b.而 Aware 接口属于内置功能, 不加任何扩展, Spring 就能识别某些情况下, 扩展功能会失效, 而内置功能不会失效
/**
例1: 你会发现用 Aware 注入 ApplicationContext 成功, 而 @Autowired 注入 ApplicationContext 失败
例2: Java 配置类在添加了 bean 工厂后处理器后,
你会发现用传统接口方式的注入和初始化仍然成功, 而 @Autowired 和 @PostConstruct 的注入和初始化失败
*/
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
private static final Logger log = LoggerFactory.getLogger(MyBean.class);
//From:BeanNameAware
@Override
public void setBeanName(String name) {
log.debug("当前bean " + this + " 名字叫:" + name);
}
//From:ApplicationContextAware
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.debug("当前bean " + this + " 容器是:" + applicationContext);
}
//From:InitializingBean
@Override
public void afterPropertiesSet() throws Exception {
log.debug("当前bean " + this + " 初始化");
}
//【不生效,需要加AutowiredAnnotationBeanPostProcessor后置处理器】
@Autowired
public void aaa(ApplicationContext applicationContext) {
log.debug("当前bean " + this + " 使用@Autowired 容器是:" + applicationContext);
}
//【不生效,需要加@后置处理器】
@PostConstruct
public void init() {
log.debug("当前bean " + this + " 使用@PostConstruct 初始化");
}
}
public class A06 {
private static final Logger log = LoggerFactory.getLogger(A06.class);
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myBean", MyBean.class);
//添加后置处理器
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
context.registerBean(CommonAnnotationBeanPostProcessor.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
context.close();
}
}
Q:Aware接口的执行顺序
1.先执行扩展
2.再执行aware接口方式的依赖注入
3.再执行接口方式的初始化
10.@Autowired失效分析
正常代码
@Configuration
public class MyConfig1 {
private static final Logger log = LoggerFactory.getLogger(MyConfig1.class);
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
log.debug("注入 ApplicationContext");
}
@PostConstruct
public void init() {
log.debug("初始化");
}
}
添加@Bean后失效,@Autowired、@PostConstruct失效
@Configuration
public class MyConfig1 {
private static final Logger log = LoggerFactory.getLogger(MyConfig1.class);
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
log.debug("注入 ApplicationContext");
}
@PostConstruct
public void init() {
log.debug("初始化");
}
@Bean // beanFactory 后处理器
public BeanFactoryPostProcessor processor1() {
return beanFactory -> {
log.debug("执行 processor1");
};
}
}
context.refresh()
1.到容器里找到所有的beanFactory的后置处理器
2.添加bean的后置处理器(bean的扩展功能)
3.初始化所有的单例
解决方法:
使用内置接口
@Configuration
public class MyConfig2 implements InitializingBean, ApplicationContextAware {
private static final Logger log = LoggerFactory.getLogger(MyConfig2.class);
@Override
public void afterPropertiesSet() throws Exception {
log.debug("初始化");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.debug("注入 ApplicationContext");
}
@Bean // beanFactory 后处理器
public BeanFactoryPostProcessor processor2() {
return beanFactory -> {
log.debug("执行 processor2");
};
}
}
public class A06 {
private static final Logger log = LoggerFactory.getLogger(A06.class);
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
// context.registerBean("myConfig1", MyConfig1.class);
context.registerBean("myConfig2", MyConfig2.class);
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
context.registerBean(CommonAnnotationBeanPostProcessor.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh(); // 1. beanFactory 后处理器, 2. 添加 bean 后处理器, 3. 初始化单例
context.close();
/*
学到了什么
a. Aware 接口提供了一种【内置】 的注入手段, 可以注入 BeanFactory, ApplicationContext
b. InitializingBean 接口提供了一种【内置】的初始化手段
c. 内置的注入和初始化不受扩展功能的影响, 总会被执行, 因此 Spring 框架内部的类常用它们
*/
}
}
11.Spring初始化和销毁
Q:Spring提供的三种初始化bean的手段
第一种:实现 InitializingBean 接口,重写 afterPropertiesSet() 方法 【再执行】
第二种:使用 @PostConstruct 注解 【首先执行】
第三种:使用 @Bean(initMethod = “init3”) 注解 【最后执行】
public class Bean1 implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
@PostConstruct
public void init1() {
log.debug("初始化1");
}
@Override
public void afterPropertiesSet() throws Exception {
log.debug("初始化2");
}
public void init3() {
log.debug("初始化3");
}
}
@SpringBootApplication
public class A07_1 {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A07_1.class, args);
context.close();
}
@Bean(initMethod = "init3")
public Bean1 bean1() {
return new Bean1();
}
@Bean(destroyMethod = "destroy3")
public Bean2 bean2() {
return new Bean2();
}
}
12.Scope
Q:Scope类型有哪些
作用域 | 描述 |
---|---|
singleton | 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。 |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。 |
application | 限定一个Bean的作用域为ServletContext 的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。 |
Q:演示 request, session, application 作用域
/*
singleton, prototype, request, session, application
jdk >= 9 如果反射调用 jdk 中方法,会IllegalAccessException
jdk <= 8 不会有问题
演示 request, session, application 作用域
打开不同的浏览器, 刷新 http://localhost:8080/test 即可查看效果
如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED
*/
@SpringBootApplication
public class A08 {
public static void main(String[] args) {
SpringApplication.run(A08.class, args);
/*
学到了什么
a. 有几种 scope
b. 在 singleton 中使用其它几种 scope 的方法
c. 其它 scope 的销毁
1. 可以将通过 server.servlet.session.timeout=10s 观察 session bean 的销毁
2. ServletContextScope 销毁机制疑似实现有误
*/
}
}
@Scope("application")
@Component
public class BeanForApplication {
private static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);
@PreDestroy
public void destroy() {
log.debug("destroy");
}
}
@Scope("request")
@Component
public class BeanForRequest {
private static final Logger log = LoggerFactory.getLogger(BeanForRequest.class);
@PreDestroy
public void destroy() {
log.debug("destroy");
}
}
@Scope("session")
@Component
public class BeanForSession {
private static final Logger log = LoggerFactory.getLogger(BeanForSession.class);
@PreDestroy
public void destroy() {
log.debug("destroy");
}
}
@RestController
public class MyController {
@Lazy
@Autowired
private BeanForRequest beanForRequest;
@Lazy
@Autowired
private BeanForSession beanForSession;
@Lazy
@Autowired
private BeanForApplication beanForApplication;
@GetMapping(value = "/test", produces = "text/html")
public String test(HttpServletRequest request, HttpSession session) {
ServletContext sc = request.getServletContext();
String sb = "<ul>" +
"<li>" + "request scope:" + beanForRequest + "</li>" +
"<li>" + "session scope:" + beanForSession + "</li>" +
"<li>" + "application scope:" + beanForApplication + "</li>" +
"</ul>";
return sb;
}
}
Q:Scope创建和销毁时机
单例:
-
Spring容器创建时会把所有的单例创建出来
-
Spring容器关闭时
多例:
-
每次使用时会创建
-
不会由Spring容器进行管理,我们可以自己调用销毁方法
Request:
-
请求来的时候创建Request域的请求对象放入Web的Request域中
-
请求结束的时候销毁(调用销毁的方法)
Session:
-
会话时,会创建Session对象,放入Session域中
-
会话结束时 或 Session超时(一段时间内没有向服务器发送请求),默认30min,在配置文件中配置 server.servlet.session.timeout=10s,这并不是严格的时间,因为一次session检查就可能超过10s,这里等了一分钟
Application:
-
当首次使用ServletContext的时候,创建并放入Appliaction域中
-
预测在关闭服务器时调用销毁方法,但是SpringBoot好像没有正确实现?
Q:Scope失效问题
在单例的bean中,如果要注入其他作用域的bean的时候,会失效
常见的解决方法是加 @Lazy注解
以单例注入多例为例
有一个单例对象 E
@Component
public class E {
@Autowired
private F1 f1;
public F1 getF1() {
return f1;
}
}
要注入的对象 F1 期望是多例
@Component
@Scope("prototype")
public class F1 {
private static final Logger log = LoggerFactory.getLogger(F.class);
public F1() {
log.info("F()");
}
}
测试
/*
如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED
*/
@ComponentScan("com.itheima.a08.sub")
public class A08_1 {
private static final Logger log = LoggerFactory.getLogger(A08_1.class);
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(A08_1.class);
E e = context.getBean(E.class);
log.debug("{}", e.getF1().getClass());
log.debug("{}", e.getF1());
log.debug("{}", e.getF1());
log.debug("{}", e.getF1());
}
}
输出
com.itheima.demo.cycle.F@6622fc65
com.itheima.demo.cycle.F@6622fc65
发现它们是同一个对象,而不是期望的多例对象
问题出现的原因
对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F1,因此 E 用的始终是第一次依赖注入的 F1
解决方法1:使用@Lazy
- 仍然使用 @Lazy 生成代理
- 代理对象虽然还是同一个,但当每次使用代理对象的任意方法时,由
代理创建新的 f 对象
@Component
public class E {
@Lazy
@Autowired
private F1 f1;
public F1 getF1() {
return f1;
}
}
注意
@Lazy 加在也可以加在成员变量上,但加在 set 方法上的目的是可以观察输出,加在成员变量上就不行了
@Autowired 加在 set 方法的目的类似
输出
E: setF(F f) class com.itheima.demo.cycle.F$$EnhancerBySpringCGLIB$$8b54f2bc
F: F()
com.itheima.demo.cycle.F@3a6f2de3
F: F()
com.itheima.demo.cycle.F@56303b57
从输出日志可以看到调用 setF 方法时,f 对象的类型是代理类型
解决方法2:@Scope
在注入的目标上使用 @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}
@Component
public class E {
@Autowired
private F2 f2;
public F2 getF2() {
return f2;
}
}
log.debug("{}", e.getF2().getClass());
log.debug("{}", e.getF2());
log.debug("{}", e.getF2());
log.debug("{}", e.getF2());
解决方法3:ObjectFactory
对象工厂,声明工厂类型的成员变量,到时候注入的不是F3,而是注入F3的对象工厂
先拿到工厂,再从工厂里拿F3,f3.getObject()
@Component
public class E {
@Autowired
private ObjectFactory<F3> f3;
public F3 getF3() {
return f3.getObject();
}
}
@Scope("prototype")
@Component
public class F3 {
}
log.debug("{}", e.getF3());
log.debug("{}", e.getF3());
解决方法4:ApplicationContext
注入Spring容器ApplicationContext
直接调用getBean方法获取任意对象
@Component
public class E {
@Autowired
private ApplicationContext context;
public F4 getF4() {
return context.getBean(F4.class);
}
}
@Scope("prototype")
@Component
public class F4 {
}
log.debug("{}", e.getF4());
log.debug("{}", e.getF4());
强行总结
解决方法虽然不同,但理念上殊途同归:都是推迟其它scope bean 的获取
- @Lazy
- @Scope(value = “prototype”, proxyMode = ScopedProxyMode.TARGET_CLASS)
- ObjectFactory
- ApplicationContext