感谢黑马满老师的讲解,通俗易懂,很适合去学习。
视频地址:Spring高级49讲-导学_哔哩哔哩_bilibili
一、容器接口
学习目标:
- BeanFactory能做哪些事
- ApplicationContext有哪些扩展功能
- 事件解耦
1、BeanFactory 与 ApplicationContext 的区别
通过这个示例结合 debug 查看 ApplicationContext 对象的内部结构
@SpringBootApplication
public class A01 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
context.getBean("aaa");
System.out.println(context);
}
}
BeanFactory 接口典型功能:getBean方法
//AbstractApplicationContext类
public Object getBean(String name) throws BeansException {
this.assertBeanFactoryActive();
//内部调用了beanFactory的getBean方法
return this.getBeanFactory().getBean(name);
}
debug查看context内部结构
ConfigurableApplicationContext继承关系:
总结:到底什么是 BeanFactory
-
它是 ApplicationContext 的父接口
-
它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory
2、BeanFactory 能干啥
BeanFactory接口:
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> var1);
<T> ObjectProvider<T> getBeanProvider(ResolvableType var1);
boolean containsBean(String var1);
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;
String[] getAliases(String var1);
}
表面上只有 getBean,实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供。
查看它的实现类
我们知道BeanFactory能管理所有的bean,最为熟悉的就是单例bean,其中DefaultSingletonBeanRegistry就管理了所有的单例对象。
通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean
@SpringBootApplication
public class A01 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
//获取beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//获取beanFactory对象的singletonObjects属性
Map<String,Object> map = (Map<String,Object>) singletonObjects.get(beanFactory);
//由于对象太多,我们只打印名称以component开头的对象
map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
.forEach( e -> {
System.out.println(e.getKey() + "=" + e.getValue());
});
}
}
@Component
public class Component1 {
}
@Component
public class Component2 {
}
结果:
component1=com.itheima.a01.Component1@16eedaa6
component2=com.itheima.a01.Component2@28501a4b
3、ApplicationContext 比 BeanFactory 多点啥
查看类图,可以发现ApplicationContext多实现了四个接口,也就可以回答下面的问题了。
ApplicationContext 比 BeanFactory 多点啥:
-
ApplicationContext 组合并扩展了 BeanFactory 的功能
-
国际化、通配符方式获取一组 Resource 资源、整合 Environment 环境、事件发布与监听
1)MessageSource接口:国际化
@SpringBootApplication
public class A01 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
System.out.println(context.getMessage("hi", null, Locale.CHINA));
System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
System.out.println(context.getMessage("hi", null, Locale.JAPANESE));
}
国际化文件均在 src/resources 目录下
messages.properties(空),空的 messages.properties 也必须存在。
messages_en.properties
hi=Hello
messages_ja.properties
hi=こんにちは
messages_zh.propertieshi=你好
hi=你好
结果:
你好
Hello
こんにちは
2)ResourcePatternResolver接口:通配符方式获取一组 Resource 资源
@SpringBootApplication
public class A01 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
//Resource[] resources = context.getResources("classpath:application.properties");
//classpath:META-INF/spring.factories:在类路径找,如果在jar包中是找不到的
//classpath*:META-INF/spring.factories:这样就可以
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
for (Resource resource : resources) {
System.out.println(resource);
}
}
结果:第一个是自己模块的,剩下的是导入的jar中的
URL [file:/E:/spring%e6%ba%90%e7%a0%81/%e4%bb%a3%e7%a0%81/show/target/classes/META-INF/spring.factories]
URL [jar:file:/D:/developer_tools/maven/repository/org/springframework/boot/spring-boot/2.5.5/spring-boot-2.5.5.jar!/META-INF/spring.factories]
URL [jar:file:/D:/developer_tools/maven/repository/org/springframework/boot/spring-boot-autoconfigure/2.5.5/spring-boot-autoconfigure-2.5.5.jar!/META-INF/spring.factories]
URL [jar:file:/D:/developer_tools/maven/repository/org/springframework/spring-beans/5.3.10/spring-beans-5.3.10.jar!/META-INF/spring.factories]
URL [jar:file:/D:/developer_tools/maven/repository/org/mybatis/spring/boot/mybatis-spring-boot-autoconfigure/2.2.0/mybatis-spring-boot-autoconfigure-2.2.0.jar!/META-INF/spring.factories]
URL [jar:file:/D:/developer_tools/maven/repository/com/alibaba/druid-spring-boot-starter/1.2.8/druid-spring-boot-starter-1.2.8.jar!/META-INF/spring.factories]
URL [jar:file:/D:/developer_tools/maven/repository/org/springframework/spring-test/5.3.10/spring-test-5.3.10.jar!/META-INF/spring.factories]
3)EnvironmentCapable接口:整合 Environment 环境
@SpringBootApplication
public class A01 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
//不区分大小写
System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("server.port"));
}
结果:
D:\developer_tools\jdk\1.8.0_131
8082
4)ApplicationEventPublisher接口:事件发布与监听
Spring中任何一个组件都可以作为监听器,这里用Component2
事件类
public class UserRegisteredEvent extends ApplicationEvent {
public UserRegisteredEvent(Object source) {
super(source);
}
}
发送事件
@SpringBootApplication
public class A01 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
//发送事件
context.publishEvent(new UserRegisteredEvent(context));
}
监听器
@Component
public class Component2 {
private static final Logger log = LoggerFactory.getLogger(Component2.class);
//监听器,方法名任意,参数要求:与发送的事件类型一致
@EventListener
public void aaa(UserRegisteredEvent event) {
log.debug("{}", event);
}
}
结果
[DEBUG] 10:00:42.236 [main] com.itheima.a01.Component2
-com.itheima.a01.UserRegisteredEvent
[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@747edf66,
started on Tue Nov 01 10:00:39 CST 2022]
4、事件解耦
完成用户注册与发送短信之间的解耦
发送事件
@Component
public class Component1 {
private static final Logger log = LoggerFactory.getLogger(Component1.class);
//具备发送事件的功能
@Autowired
private ApplicationEventPublisher context;
public void register() {
log.debug("用户注册");
//发布事件
context.publishEvent(new UserRegisteredEvent(this));
}
}
监听事件
@Component
public class Component2 {
private static final Logger log = LoggerFactory.getLogger(Component2.class);
//监听器,方法名任意,参数要求:与发送的事件类型一致
@EventListener
public void aaa(UserRegisteredEvent event) {
log.debug("{}", event);
log.debug("发送短信");
}
}
从容器获取Component1调用其方法
@SpringBootApplication
public class A01 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
context.getBean(Component1.class).register();
}
二、容器实现
学习目标:
- BeanFactory实现的特点
- ApplicationContext的常见实现和用法
- 内嵌容器,注册DispatcherServlet
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 的功能,并非继承而来
1、DefaultListableBeanFactory演示
原始的DefaultListableBeanFactory
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//bean的定义(class,scope,初始化,销毁)
AbstractBeanDefinition beanDefinition
= BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//注册beandefinition对象
//参数一:名字 ,参数二:具体bean
beanFactory.registerBeanDefinition("config", beanDefinition);
//输出容器中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 {
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()");
}
}
}
结果:容器中只有一个config对象,原始的beanFactory并没有解析注解的能力
config
1)给BeanFactory添加一些常用的后处理器
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//bean的定义(class,scope,初始化,销毁)
AbstractBeanDefinition beanDefinition
= BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//注册beandefinition对象
//参数一:名字 ,参数二:具体bean
beanFactory.registerBeanDefinition("config", beanDefinition);
//给BeanFactory添加一些常用的后处理器,仅仅是注册后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
//输出容器中bean的名字
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
结果:仅仅是注册了后处理器,还没有执行,此时并不具备解析注解的能力
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
2)执行BeanFactory后置处理器
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//bean的定义(class,scope,初始化,销毁)
AbstractBeanDefinition beanDefinition
= BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//注册beandefinition对象
//参数一:名字 ,参数二:具体bean
beanFactory.registerBeanDefinition("config", beanDefinition);
//给BeanFactory添加一些常用的后处理器,仅仅是注册后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
//执行BeanFactory后置处理器
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
.stream().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
}
}
结果: bean1,bean2成功注册进容器,但是bean1获取不到bean2对象,结果为null。
并且可以发现是懒加载,第一次用到的时候才创建对象实例
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
[DEBUG] 15:34:11.550 [main] c.itheima.a02.TestBeanFactory$Bean1 - 构造 Bean1()
null
原因:输出刚才执行的后处理器,发现没有执行@Autowire后处理器
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
[org.springframework.context.annotation.ConfigurationClassPostProcessor@24313fcc,
org.springframework.context.event.EventListenerMethodProcessor@7d20d0b]
3)执行Bean后处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowire,@Resource
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//bean的定义(class,scope,初始化,销毁)
AbstractBeanDefinition beanDefinition
= BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//注册beandefinition对象
//参数一:名字 ,参数二:具体bean
beanFactory.registerBeanDefinition("config", beanDefinition);
//给BeanFactory添加一些常用的后处理器,仅仅是注册后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
//执行BeanFactory后置处理器
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
.stream().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
//执行Bean后处理器
beanFactory.getBeansOfType(BeanPostProcessor.class).values()
.forEach(beanPostProcessor -> {
//查看Bean后处理器顺序
System.out.println(">>>>" + beanPostProcessor);
//添加Bean后处理器
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
}
}
结果:
>>>>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@76908cc0
>>>>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@2473d930
[DEBUG] 15:50:59.132 [main] c.itheima.a02.TestBeanFactory$Bean1 - 构造 Bean1()
[DEBUG] 15:50:59.149 [main] c.itheima.a02.TestBeanFactory$Bean2 - 构造 Bean2()
com.itheima.a02.TestBeanFactory$Bean2@188715b5
我们可以在获取bean前调用下面的方法准备好单例,就不用到使用时才去创建实例。
beanFactory.preInstantiateSingletons(); //准备好所有单例
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");//为了区分是在获取前创建的
结果:
>>>>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@76908cc0
>>>>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@2473d930
[DEBUG] 15:56:20.124 [main] c.itheima.a02.TestBeanFactory$Bean1 - 构造 Bean1()
[DEBUG] 15:56:20.138 [main] c.itheima.a02.TestBeanFactory$Bean2 - 构造 Bean2()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
com.itheima.a02.TestBeanFactory$Bean2@43195e57
此时增加bean3,bean4
@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();
}
}
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()");
}
}
interface Inter {
}
static class Bean3 implements Inter {
}
static class Bean4 implements Inter {
}
关于@Autowire的一些点,这里说明一下
我要在bean1里面这样注入,能成功吗?注入失败,容器中bean3,bean4都匹配
@Autowired
private Inter inter;
但是这样就可以,当有多个bean匹配时,通过名字进一步匹配
@Autowired
private Inter bean3
可以发现我刚刚打印了Bean后处理器的顺序,这个有什么用?看这个例子,请问注入的是bean3还是bean4呢
注入bean3,与后处理器顺序有关,先解析Autowire
@Autowired
@Resource(name = "bean4")
private Inter bean3;
设置Bean后处理器顺序,通过优先级排序。
//Bean后处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowire,@Resource
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
.sorted(beanFactory.getDependencyComparator()).
forEach(beanPostProcessor -> {
//添加Bean后处理器
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
查看源码,它们都实现了 Ordered 接口,设置Ordered大小进行排序,ordered小的优先级高
//AutowiredAnnotationBeanPostProcessor类
private int order = 2147483645;
//CommonAnnotationBeanPostProcessor类
public CommonAnnotationBeanPostProcessor() {
this.setOrder(2147483644);
this.setInitAnnotationType(PostConstruct.class);
this.setDestroyAnnotationType(PreDestroy.class);
this.ignoreResourceType("javax.xml.ws.WebServiceContext");
}
public interface Ordered {
int HIGHEST_PRECEDENCE = -2147483648;
int LOWEST_PRECEDENCE = 2147483647;
int getOrder();
}
beanFactory.getDependencyComparator()是什么?
查看源码:可以发现
- 实现了 PriorityOrdered 接口的优先级最高
- 实现了 Ordered 接口与加了 @Order 注解的平级, 按数字升序
- 其它的排在最后
public class AnnotationAwareOrderComparator extends OrderComparator {
}
public class OrderComparator implements Comparator<Object> {
public int compare(@Nullable Object o1, @Nullable Object o2) {
return this.doCompare(o1, o2, (OrderComparator.OrderSourceProvider)null);
}
private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderComparator.OrderSourceProvider sourceProvider) {
boolean p1 = o1 instanceof PriorityOrdered;
boolean p2 = o2 instanceof PriorityOrdered;
if (p1 && !p2) {
return -1;
} else if (p2 && !p1) {
return 1;
} else {
int i1 = this.getOrder(o1, sourceProvider);
int i2 = this.getOrder(o2, sourceProvider);
return Integer.compare(i1, i2);
}
}
}
总结
beanFactory 不会做的事:
- 不会主动调用 BeanFactory 后处理器
- 不会主动添加 Bean 后处理器
- 不会主动初始化单例
- 不会解析beanFactory 还不会解析 ${ } 与 #{ }
bean 后处理器会有排序的逻辑
2、常见 ApplicationContext 实现
1)ClassPathXmlApplicationContext
从类路径查找 XML 配置文件,创建容器
public class A02Application {
public static void main(String[] args) {
testClassPathXmlApplicationContext();
}
// 较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
private static void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("b01.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
static class Bean1 {
}
static class Bean2 {
private Bean1 bean1;
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
public Bean1 getBean1() {
return bean1;
}
}
}
b01.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="bean1" class="com.itheima.a02.A02Application.Bean1"/>
<!-- 控制反转, 让 bean2 被 Spring 容器管理 -->
<bean id="bean2" class="com.itheima.a02.A02Application.Bean2">
<!-- 依赖注入, 建立与 bean1 的依赖关系 -->
<property name="bean1" ref="bean1"/>
</bean>
</beans>
结果:
bean1
bean2
com.itheima.a02.A02Application$Bean1@4b168fa9
我们在Spring基础中都学过这个标签,作用是让系统能够识别相应的注解
<context:annotation-config/>
现在我们看看到底是为什么呢?
加上这个标签,打印容器中的bean,发现多了几个后处理器,用来解析@Configuration,@Autowire,@Resourse等注解
bean1
bean2
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
2)FileSystemXmlApplicationContext
基于磁盘路径下 xml 格式的配置文件来创建
public class A02Application {
public static void main(String[] args) {
testFileSystemXmlApplicationContext();
}
private static void testFileSystemXmlApplicationContext() {
FileSystemXmlApplicationContext context =
new FileSystemXmlApplicationContext("src\\main\\resources\\b01.xml");
//相对路径(模块开始):src\main\resources\b01.xml
//绝对路径:E:\spring源码\代码\show\src\main\resources\b01.xml
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
}
结果:
bean1
bean2
com.itheima.a02.A02Application$Bean1@4b168fa9
测试的时候遇到了一个报错,如下
Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException:
IOException parsing XML document from file
[D:\workspace_idea2\spring_yuanli\src\main\resources\b01.xml];
nested exception is java.io.FileNotFoundException: src\main\resources\b01.xml
原因:这个项目是导进去的,与默认的工作目录不一样导致的。
处理方法:Edit Configurations -> 修改工作目录
原理:前面说过,ApplicationContext内部还是需要beanFactory的支持
比DefaultListableBeanFactory多的功能,加载bean的定义信息
public class A02Application {
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("b01.xml"));
//reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\b01.xml"));
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
}
结果:
读取之前...
读取之后...
bean1
bean2
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
3)AnnotationConfigApplicationContext
Spring boot 中非 web 环境容器,较为经典的容器, 基于 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;
}
}
结果:发现该容器已经配置了解析@Configuration,@Autowire,@Resourse等注解的后处理器
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
a02Application.Config
bean1
bean2
com.itheima.a02.A02Application$Bean1@6025e1b6
4)AnnotationConfigServletWebServerApplicationContext
Spring boot 中 servlet web 环境容器,基于 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 {
//内嵌一个基于servlet的Web容器,即提供web服务器的组件
@Bean
public ServletWebServerFactory servletWebServerFactory(){
return new TomcatServletWebServerFactory();
}
//前端控制器
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
//Servlet是运行在Tomcat服务器上的,需要将他们两个关联
//将DispatcherServlet注册到Tomcat服务器
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
//让除了jsp的所有请求都先经过DispatcherServlet
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
//以 /开头可以将它的名称作为访问路径
@Bean("/hello")
//注意:是org.springframework.web.servlet.mvc.Controller接口,而不是注解
public Controller controller1() {
return new Controller() {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().print("hello");
return null;
}
};
}
}
结果:发现该容器已经配置了解析@Configuration,@Autowire,@Resourse注解的后处理器,并且我们可以通过 localhost:8080/hello进行访问
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
a02Application.WebConfig
servletWebServerFactory
dispatcherServlet
registrationBean
/hello
总结
学到了什么?
- 常见的 ApplicationContext 容器实现
- 内嵌容器、DispatcherServlet 的创建方法、作用