Spring高级
一、容器与bean a01 、容器接口(BeanFactory与ApplicationContext的区别与联系) ①、到底什么是 BeanFactory?BeanFactory接口:
首先我们先了解一下BeanFactory接口:
我们写一段springboot项目的启动类如下:
@SpringBootApplication
public class A01Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
}
}
- 上述代码中 SpringApplication.run(A01Application.class, args)方法返回一个容器对象,我们使用idea查看这个容器类的继承关系(快捷键:Ctrl + alt + u)如下:
-
我们发现ConfigurableApplicationContext继承与ApplicationContext
-
ApplicationContext继承与BeanFactory
到底什么是BeanFactory?
-
它是ApplicationContext的父接口
-
它才是 Spring 的核心容器,主要的 ApplicationContext 实现都 [组合]了它的功能
在web环境下,我们打印ConfigurableApplicationContext的class对象发现它其实是AnnotationConfigServletWebServerApplicationContext类型
我们接着查看AnnotationConfigServletWebServerApplicationContext类的继承图,
按图所示:AnnotationConfigServletWebServerApplicationContext又间接继承了GenericApplicationContext
我们跟进GenericApplicationContext源码:发现beanFactory是该类的成员变量。
②、BeanFactory的功能BeanFactory接口中的方法:
- 表面上只有 getBean
- 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供
我们可以查看springboot默认的ConfigurableApplicationContext类中的BeanFactory的实际类型
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 查看实际类型
// class org.springframework.beans.factory.support.DefaultListableBeanFactory
System.out.println(beanFactory.getClass());
}
从打印结果可以了解到实际类型为DefaultListableBeanFactory,所以这里以BeanFactory的一个实现类DefaultListableBeanFactory作为出发点,进行分析。
这里我们暂且不细看DefaultListableBeanFactory,先看DefaultListableBeanFactory的父类DefaultSingletonBeanRegistry,先选中它,然后双击,可以跳转到对应的源码,可以看到有个私有的成员变量singletonObjects
知道了这些关系,我们可以利用反射获取singletonObjects进行查看代码如下:
- 首先我们人为往spring容器中添加两个bean
//bean1
package com.colin.a01;
import org.springframework.stereotype.Component;
@Component
public class Component1 {
}
//bean2
package com.colin.a01;
import org.springframework.stereotype.Component;
@Component
public class Component2 {
}
- 利用反射查看我们定义的bean是否在singletonObjects中
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
Map map = (Map) singletonObjects.get(beanFactory);
System.out.println(beanFactory.getClass());
map.entrySet().stream().filter(entry -> entry.getKey().startsWith("component")).forEach(System.out::println);
}
结果如下:说明单例池在BeanFactory。
③、ApplicationContext 比 BeanFactory 多点啥?-
多实现了四个接口:
- MessageSource: 国际化功能,支持多种语言
- ResourcePatternResolver: 通配符匹配资源路径
- EnvironmentCapable: 环境信息,系统环境变量,.properties、.application.yml等配置文件中的值
- ApplicationEventPublisher: 发布事件对象
蓝色框标识:
-
我们在resource下建立三个语言翻译的properties
其实还应该有一个默认语言环境的properties名字为messages.properties当所有语言环境都不匹配时它就生效
图省事没有创建,真实开发一定要创建
内容如下: -
我们编写java代码从测试: 其中GenericApplicationContext是干净的ApplicationContext 什么功能都没有
我们编程注入一个bean ,bean的类型为MessageSource
调用GenericApplicationContext的refresh方法加载或刷新配置
使用Locale指定语言环境
说明: 真实开发,语言环境由前台浏览器携带请求头传递,这里只是演示,真实开发是使用@Bean注入messageSource只是演示
public static void main(String[] args) {
//干净的ApplicationContext 什么功能都没有
GenericApplicationContext context = new GenericApplicationContext();
//编程注入一个bean bean的类型为MessageSource
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));
}
- 结果如下: 我们发现由于语言环境的不同spring帮我们返回来不同的键值。实现了国际化的功能
注意:
⑵、演示ResourcePatternResolver: 通配符匹配资源路径
- ApplicationContext 中 MessageSource bean 的名字固定为 messageSource
- 使用 SpringBoot 时,国际化文件名固定为 messages
- 空的 messages.properties 也必须存在
- 获取类路径下的messages开头的配置文件
//测试ResourcePatternResolver
Resource[] resources = context.getResources("classpath:messages*.properties");
for (Resource resource : resources) {
System.out.println(resource);
}
运行结果:
- 获取spring相关jar包中的spring.factories配置文件
//测试获取spring相关jar包中的spring.factories配置文件
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
for (Resource resource : resources) {
System.out.println(resource);
}
运行结果:
获取系统环境变量中的java_home和项目的application.yml中的server.port属性:
//测试获取系统环境变量中的`java_home`和项目的`application.yml`中的`server.port`属性
System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("server.port"));
结果如下:
我们模拟用户注册发布事件:
- 定义一个用户注册事件类,继承自ApplicationEvent类
import org.springframework.context.ApplicationEvent;
public class UserRegisteredEvent extends ApplicationEvent {
public UserRegisteredEvent(Object source) {
super(source);
}
}
- 定义一个监听器类,主要用来监听事件,处理事件。类头上需要加@Component注解,将该类交给spring管理,定义一个处理事件的方法,参数类型为用户注册事件类的对象,方法头上需要加上@EventListener注解
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class UserRegisteredListener {
@EventListener
public void userRegist(UserRegisteredEvent event) {
System.out.println("UserRegisteredEvent...");
log.debug("{}", event);
}
}
- 定义一个service 用来模拟用户注册业务,注册完毕发送用户注册事件
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class UserService {
@Autowired
private ApplicationEventPublisher context;
public void register(String username, String password) {
log.debug("新用户注册,账号:" + username + ",密码:" + password);
context.publishEvent(new UserRegisteredEvent(this));
}
}
- 在启动类模拟调用
//模拟调用事件
UserService userService = context.getBean(UserService.class);
userService.register("colin","123456");
结果如下:
a02 、容器的实现学到了什么?
a. BeanFactory 与 ApplicationContext 并不仅仅是简单接口继承的关系, ApplicationContext 组合并扩展了 BeanFactory 的功能
b. 又新学一种代码之间解耦途径(发布事件)
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接着第一讲中的内容,执行以下代码,可以了解到
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args); // org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 查看实际类型 // class org.springframework.beans.factory.support.DefaultListableBeanFactory System.out.println(beanFactory.getClass());
ConfigurableApplicationContext类内部组合的BeanFactory实际类型为DefaultListableBeanFactory
spring底层创建实体类就是依赖于这个类,所以它是BeanFactory接口最重要的一个实现类。
-
模拟一下spring使用DefaultListableBeanFactory类创建其他实体类对象的过程。
-
准备工作首先我们在类中定义一个配置类,并增加几个bean的声明
package com.colin.a02; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; 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.Bean; import org.springframework.context.annotation.Configuration; import javax.annotation.Resource; public class TestBeanFactory { public static void main(String[] args) { } @Configuration static class Config { @Bean public Bean1 bean1() { return new Bean1(); } @Bean public Bean2 bean2() { return new Bean2(); } } static class Bean1 { public Bean1() { log.debug("构造 Bean1()"); } } static class Bean2 { } } -
紧接着我们在main方法中利用DefaultListableBeanFactory和bean的定义信息BeanDefinition把配置类注册到spring容器。打印所有的bean的定义信息。
public static void main(String[] args) { //创建BeanFactory重要的实现类DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder .genericBeanDefinition(Config.class) .setScope("singleton") .getBeanDefinition(); //将bean定义注册到beanfactory beanFactory.registerBeanDefinition("config",beanDefinition); //查看所有bean的定义信息 for (String name : beanFactory.getBeanDefinitionNames()) { System.out.println(name); } }结果如下:
我们通过结果发现:容器中只有config类,但在配置类中的@Bean标注的Bean1、Bean2都不在容器中。这是因为beanFactory没有能力处理@Configuration和@Bean注解,我们需要为beanFactory添加一些Bean工厂的后处理器,让后处理器处理这些事情。
-
我们为beanFactory注入常用的处理器,并再次打印容器中bean的所有定义
// 给 BeanFactory 添加一些常用的后处理器 AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); //查看所有bean的定义信息 for (String name : beanFactory.getBeanDefinitionNames()) { System.out.println(name); }结果如下:
我们发现除了config,又多了很多bean的定义信息,这些多的bean就是AnnotationConfigUtils为我们添加的。但还是没有我们自己定义的bean信息,这是因为,我们只是将后处理器放入到了容器中,并没有调用它们。 -
我们接着手动调用这些后处理器(Bean工厂的后处理器都实现了BeanFactoryPostProcessor接口)
//调用 BeanFactory 的后处理器 // BeanFactory 后处理器主要功能,补充了一些 bean 定义 System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> { //查看类型 System.out.println(beanFactoryPostProcessor.getClass()); //手动调用后处理器 beanFactoryPostProcessor.postProcessBeanFactory(beanFactory); }); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); //再次查看所有bean的定义信息 for (String name : beanFactory.getBeanDefinitionNames()) { System.out.println(name); }输出结果:
可以看到:我们手动调用了以下后处理器。
- org.springframework.context.annotation.ConfigurationClassPostProcessor
- org.springframework.context.event.EventListenerMethodProcessor
再次查看所有bean的定义信息我们看到了bean1、bean2
-
现在我们继续研究,我们已经看到了bean1的定义,那下面我们试一下这些bean能不能正常使用
System.out.println(beanFactory.getBean(Bean1.class));
结果:
这里有一个细节(打印了 构造 Bean1()):说明只有我们getBean的时候 BeanFactory才会真正的根据bean的定义信息创建这个bean,BeanFactory并不会主动创建bean
-
可以看到可以使用,那我们改造一下代码,改变一下代码把依赖注入加上
//改造bean1 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; } }调用bean1的getBean2方法
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
结果:
我们发现打印为null,这是因为BeanFactory没有处理@Autowired的能力,那是不是缺少相应的后处理器呢?这里注意的是BeanFactory 后处理器主要功能,补充了一些 bean 定义。 而依赖注入是创建bean的时期要处理的。并不是用来补充 bean的 定义。
我们是要添加后处理器,但这个后处理器不是针对BeanFactory的,而是针对bean,我们把针对 bean 的生命周期的各个阶段提供扩展的后处理器称为Bean 后处理器
-
AnnotationConfigUtilsy已经为我们添加了Bean 后处理器的定义信息,我们要将Bean 后处理器设置给BeanFactory让其发挥作用
Bean 后处理器都实现了BeanPostProcessor接口
// System.out.println(beanFactory.getBean(Bean1.class)); // System.out.println(beanFactory.getBean(Bean1.class).getBean2()); //给beanFactory设置bean后处理器 beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().forEach( beanPostProcessor -> { System.out.println(">>>>>" + beanPostProcessor); beanFactory.addBeanPostProcessor(beanPostProcessor); } ); //再次查看bean1 System.out.println(beanFactory.getBean(Bean1.class)); System.out.println(beanFactory.getBean(Bean1.class).getBean2());结果:
我们可以看到:
给beanFactory设置bean后处理器:
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor bean1也已经有了bean2:
com.colin.a02.TestBeanFactory B e a n 1 @ 33 d 512 c 1 < b r / > c o m . c o l i n . a 02. T e s t B e a n F a c t o r y Bean1@33d512c1
com.colin.a02.TestBeanFactory Bean1@33d512c1
com.colin.a02.TestBeanFactoryBean2@515c6049`
-
-
探究Bean后处理器有排序的逻辑
-
准备工作定义一个接口Inter,再定义两个Bean,名称分别为Bean3和Bean4,都继承Inter,接着在Config中通过@Bean注解将Bean3和Bean4都加进Bean工厂中,然后在Bean1中定义一个Inter对象,通过@Autowired注解将实现类注入进来。
@Configuration static class Config { @Bean public Bean1 bean1() { return new Bean1(); } @Bean public Bean2 bean2() { return new Bean2(); } //将bean3加进Bean工厂 @Bean public Bean3 bean3() { return new Bean3(); } //将bean4加进Bean工厂 @Bean public Bean4 bean4() { return new Bean4(); } } //定义接口 interface Inter { } //bean3 static class Bean3 implements Inter { } //bean4 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; } //注入Inter @Autowired private Inter bean3; public Inter getInter() { return bean3; } } static class Bean2 { } -
由于BeanFactory不会主动创建bean,所以我们调用BeanFactory类中重要的方法preInstantiateSingletons()
preInstantiateSingletons()方法会准备好了所有的单例bean
我们从容器中取出bean1 然后调用getInter() 看成员变量bean3注入的类型到底是什么类型?
public static void main(String[] args) { //.....以前代码 //调用BeanFactory类中重要的方法preInstantiateSingletons准备好了所有的单例 beanFactory.preInstantiateSingletons(); // 准备好所有单例 //取出bean1 Bean1 bean1 = beanFactory.getBean(Bean1.class); //调用getInter() System.out.println(bean1.getInter()); }结果如下:
- 我们发现注入的是bean3。
- 我们分析一下原因:@Autowired是根据类型注入,但容器中是Inter类型的有两个,bean3、bean4,所以@Autowired会根据变量的名称去配置bean的名称,所以注入的是bean3
-
我们接着实验,我们除了使用@Autowired注解还可以使用@Resource注解实现依赖注入。所以我们去除@Autowired使用@Resource并指明bean的名字,查看结果。
//改造bean1 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; } @Resource(name = "bean4") private Inter bean3; public Inter getInter() { return bean3; } }确实注入对象变成了bean4
-
我们接着如果@Autowired和@Resource同时存在(正常开发,不会怎么干,这只是实验),会注入什么对象呢?
//改造bean1 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; } }结果又变成了bean3,这是为什么呢? 我们研究一下。
-
首先我们知道,BeanFactory并部具备处理这些依赖注入相关注解的能力,处理@Autowired和@Resource都是bean后处理器工作。而这些后处理器是我们调用AnnotationConfigUtils工具类内的方法添加的。
-
以往学习我们知道AnnotationConfigUtils为BeanFactory添加的bean后处理器主要有两个org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor(处理@Autowired)
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor(处理@Resource) -
我们点进去AnnotationConfigUtils的registerAnnotationConfigProcessors()方法看一下源码
- 我们发现AutowiredAnnotationBeanPostProcessor确实比CommonAnnotationBeanPostProcessor顺序靠前
-
AnnotationConfigUtils不仅为BeanFcatory添加了一些常用后处理器,同时也为BeanFcatory添加了默认的后处理排序规则, 补充后处理器内有order属性就是排序的依据,数字越小,排名越往前,反之越靠后。
-
我们通过代码试着改变一下后处理顺序,看一下效果
//改变bean后处理顺序前 System.out.println("改变顺序前"); beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().forEach( beanPostProcessor -> { System.out.println(">>>>>" + beanPostProcessor); } ); //改变顺序后 System.out.println("改变顺序后"); beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream() .sorted(beanFactory.getDependencyComparator()) .forEach(beanPostProcessor -> { System.out.println(">>>>" + beanPostProcessor); beanFactory.addBeanPostProcessor(beanPostProcessor); }); //调用BeanFactory类中重要的方法preInstantiateSingletons准备好了所有的单例 beanFactory.preInstantiateSingletons(); // 准备好所有单例 //取出bean1 Bean1 bean1 = beanFactory.getBean(Bean1.class); //调用getInter() System.out.println(bean1.getInter());结果:如下图所示,改变顺序后CommonAnnotationBeanPostProcessor排在了AutowiredAnnotationBeanPostProcessor之前,依赖注入只会进行一次,所以在依赖注入时先是@Resource被解析完成了依赖注入,@Autowired后被解析,但依赖注入已经完成,所以不起效果,所以注入的对象是bean4
-
-
-
②、常见 ApplicationContext 实现学到了什么:
beanFactory 不会做的事
a.不会主动调用 BeanFactory 后处理器
b.不会主动添加 Bean 后处理器
c.不会主动初始化单例
d.不会解析: beanFactory 还不会解析 ${ } 与 #{ }
e. bean 后处理器会有排序的逻辑
-
四个重要的ApplicationContext接口的实现类
- ClassPathXmlApplicationContext
- FileSystemXmlApplicationContext
- AnnotationConfigApplicationContext
- AnnotationConfigServletWebServerApplication
-
在resources下新建a02.xml
-
main方法测试
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.mvc.Controller; public class A02 { private static final Logger log = LoggerFactory.getLogger(A02.class); public static void main(String[] args) { testClassPathXmlApplicationContext(); } // ⬇️较为经典的容器, 基于 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()); }测试结果:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;
public class A02 {
private static final Logger log = LoggerFactory.getLogger(A02.class);
public static void main(String[] args) {
testFileSystemXmlApplicationContext();
}
// ⬇️基于磁盘路径下 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());
}
}
测试结果:
⑶、测试AnnotationConfigApplicationContext
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;
public class A02 {
private static final Logger log = LoggerFactory.getLogger(A02.class);
public static void main(String[] args) {
testAnnotationConfigApplicationContext();
}
// ⬇️较为经典的容器, 基于 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;
}
}
static class Bean1 {
}
static class Bean2 {
private Bean1 bean1;
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
public Bean1 getBean1() {
return bean1;
}
}
}
测试结果:
⑷、测试AnnotationConfigServletWebServerApplication
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;
public class A02 {
private static final Logger log = LoggerFactory.getLogger(A02.class);
public static void main(String[] args) {
testAnnotationConfigServletWebServerApplicationContext();
}
// ⬇️较为经典的容器, 基于 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
@Bean("/hello")
public Controller controller1() {
return (request, response) -> {
response.getWriter().print("hello");
return null;
};
}
}
}
测试结果:
访问浏览器http://localhost:8080/hello
⑸、模拟ApplicationContext 实现都干了什么?-
以ClassPathXmlApplicationContext示例
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 ClassPathResource("a02.xml")); for (String name : beanFactory.getBeanDefinitionNames()) { System.out.println(name); } }测试结果:
a03 、Bean 的生命周期学到了什么
a. 常见的 ApplicationContext 容器实现
b. 内嵌容器、DispatcherServlet 的创建方法、作用
- 一个受 Spring 管理的 bean,生命周期主要阶段有
- 创建:根据 bean 的构造方法或者工厂方法来创建 bean 实例对象
- 依赖注入:根据 @Autowired,@Value 或其它一些手段,为 bean 的成员变量填充值、建立关系
- 初始化:回调各种 Aware 接口,调用对象的各种初始化方法
- 销毁:在容器关闭时,会销毁所有单例对象(即调用它们的销毁方法)
- prototype 对象也能够销毁,不过需要容器这边主动调用
一些资料会提到,生命周期中还有一类 bean 后处理器:BeanPostProcessor,会在 bean 的初始化的前后,提供一些扩展逻辑。但这种说法是不完整的因为bean 后处理器可能在bean的各个生命周期里发挥作用。
①、演示1 - bean 生命周期-
springboot项目启动类
@SpringBootApplication public class A03 { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A03.class, args); context.close(); } } -
定义一个LifeCycleBean,加上@Component注解,再编写一些方法,给这些方法加上Bean的生命周期过程中的注解
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; @Slf4j @Component public class LifeCycleBean { public LifeCycleBean() { log.info("构造方法"); } @Autowired public void autowire(@Value("${java_home}") String home) { log.info("依赖注入:{}", home); } @PostConstruct public void init() { log.info("初始化方法"); } @PreDestroy public void destory() { log.info("销毁方法"); } }结果打印:
执行顺序是:
构造方法 》》 依赖注入 》》 初始化方法 》》 销毁方法
-
下面我们编写自定义Bean的后处理器,对lifeCycleBean的生命周期过程进行扩展,编写自定义Bean的后处理器需要实现InstantiationAwareBeanPostProcessor和DestructionAwareBeanPostProcessor接口,并加上@Component注解。
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 void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) { log.info("<<<<<< 销毁之前执行 (postProcessBeforeDestruction), 如 @PreDestroy"); } } @Override public Object postProcessBeforeInstantiation(Class> beanClass, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) { log.info("<<<<<< 实例化之前(未调用构造方法)执行(postProcessBeforeInstantiation), 这里返回的对象会替换掉原本的 bean"); } return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) { log.info("<<<<<< 实例化之后(调用构造方法后)执行(postProcessAfterInstantiation), 这里如果返回 false 会跳过依赖注入阶段"); // return false; } return true; } @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) { log.info("<<<<<< 依赖注入阶段执行(postProcessProperties), 如 @Autowired、@Value、@Resource"); } return pvs; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) { log.info("<<<<<< 初始化之前执行(postProcessBeforeInitialization), 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) { log.info("<<<<<< 初始化之后执行(postProcessAfterInitialization), 这里返回的对象会替换掉原本的 bean, 如代理增强"); } return bean; } }结果打印:
把postProcessAfterInstantiation返回值改为false,让bean跳过依赖注入阶段结果:依赖注入语句无打印
- 提高代码的扩展性。
import java.util.ArrayList;
import java.util.List;
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 processors = new ArrayList<>();
public void addBeanPostProcessor(BeanPostProcessor processor) {
processors.add(processor);
}
}
static interface BeanPostProcessor {
public void inject(Object bean); // 对依赖注入阶段的扩展
}
}



