【黑马】Spring
技术原理第二讲
BeanFactory
与他的后处理器小伙伴们
我们在第一讲的时候,已经了解到关于
BeanFactory
中的方法,也知道了BeanFactory
是spring
容器中最基本的接口,那么关于spring
中众多的注解以及其他功能是谁来处理的呢?
首先:介绍一下关于BeanFactory
的实现类DefaultListableBeanFactory
,通过下面的代码可以得到ConfigurableApplicationContext
接口实现类中的BeanFactory
的实现类型.
ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// class org.springframework.beans.factory.support.DefaultListableBeanFactory
System.out.println(beanFactory.getClass());
也可以跳转到ConfigurableApplicationContext
实现类中观察。
首先我们需要明确BeanFactory
的功能
beanFactory
不会做的事
- 不会主动调用
BeanFactory
后处理器 - 不会主动添加
Bean
后处理器 - 不会主动初始化单例
- 不会解析,
BeanFactory
不会解析${}和#{}
那么谁来做这些事情呢?答案就是后处理器小伙伴们。
首先看一段代码:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AbstractBeanDefinition singleton = BeanDefinitionBuilder
.genericBeanDefinition(Config.class)
.setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config", singleton);
Config
类
@Configuration
static class Config{
@Bean
public Bean1 bean1(){
return new Bean1();
}
@Bean
public Bean2 bean2(){
return new Bean2();
}
}
static class Bean1{
public Bean1() {
System.out.println("构造bean1");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2(){
return bean2;
}
}
static class Bean2{
public Bean2() {
System.out.println("构造bean2");
}
}
输出
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
// config
解释:
DefaultListableBeanFactory
为BeanFacotry
的实现类,所以第一句话是创建了一个bean
工厂
AbstractBeanDefinition
为BeanDefinition
的实现类,第一讲时,我们介绍过关于BeanDefinition
,简单理解为容器中的Bean
对象的数据,通过genericBeanDefinition
方法和getBeanDefinition
获取到关于这个bean
的BeanDefinition
beanFactory.registerBeanDefinition("config", singleton);
将BeanDefinition
放入bean
工厂之中。至此
bean
工厂中已经包含了我们创建的Config
类了
说到这里,还是没有提到BeanFactory
的后处理器小伙伴,那么这些后处理器到底是用来做什么的呢?
我们可以回过头去看看之前定义的
Config
类,可以看到里面包含了@Bean
注解以及@Autowired
,不是说已经将Config
类塞进BeanFactory
中了吗?为什么依赖注入和@Bean
注解没有生效呢?
Map<String, BeanFactoryPostProcessor> beansProcessor =
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
在beansProcessor
这个map
集合中,里面包含了BeanFactory
的后处理器小伙伴
// 输出
//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
通过遍历,我们将后处理器与bean
工厂建立连接
beansOfType.values().stream().forEach(bean -> {
bean.postProcessBeanFactory(beanFactory);
});
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
至此,我们的后处理小伙伴就加入了bean
工厂,他们带来的效果则是处理了@Bean
注解以及@Autowired
注解等等。
后处理器也是有优先级的,例如我们的@Autowired
和@Resource
,@Autowired
注解是在internalConfigurationAnnotationProcessor
后处理器中处理的,而@Resource
则是在internalCommonAnnotationProcessor
后处理器中处理的,那么他们的优先级是多少呢?【优先级越小 优先级越高】
从CommonAnnotationBeanPostProcessor
源码中可以看出,order
为2147483644
而@Autowired
是在AutowiredAnnotationBeanPostProcessor
中解析的,它的优先级则为2147483645
ApplicationContext
的实现
ClassPathXmlApplicationContext
: 基于classpath
下xml
格式的配置文件来创建FileSystemXmlApplicationContext
: 基于磁盘路径下xml
格式的配置文件来创建AnnotationConfigApplicationContext
: 基于java
配置类来创建AnnotationConfigServletWebServerApplication
: 基于java
配置类来创建,并且还可以用于web
环境
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">
<context:annotation-config />
<bean id="bean1" class="com.qiao.Bean1" />
<bean id="bean2" class="com.qiao.Bean2">
<property name="bean1" ref="bean1"/>
</bean>
</beans>
ClassPathXmlApplicationContext
代码
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
FileSystemXmlApplicationContext
代码
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("src\\main\\resources\\b01.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
AnnotationConfigApplicationContext
代码
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
AnnotationConfigServletWebServerApplication
代码,模拟了 springboot web
项目内嵌Tomcat
的工作原理
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 防止程序终止
System.in.read();
配置类:
@Configuration
class WebConfig {
@Bean
// 1. WebServer工厂
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
// 2. web项目必备的DispatcherServlet
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
// 3. 将DispatcherServlet注册到WebServer上
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
// 如果bean中的内容为/ 开头,则为路径
@Bean("/hello")
public Controller controller1() {
return (request, response) -> {
response.getWriter().println("hello");
return null;
};
}
}
Spring技术内幕补充
IoC
容器的初始化过程
IoC容器的初始化是由refresh()方法来启动的,其中包含了Resouce
定位、载入和注册三个基本的过程。
ClassPathResource res = new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
第一个过程是Resouce
定位过程。可以理解为BeanDefinition
的资源定位
ClassPathResource res = new ClassPathResource("beans.xml");
这段代码的作用是,让spring在类路径中寻找以文件形式存在的BeanDefinition
信息。而spring是通过BeanDefinitionReader
来对这些信息进行处理的,当我们再回头查看ApplicationContext
的实现类时,我们发现,每个实现类都实现了ResourceLoader
接口,用于资源定位
下面是ClassPathXmlApplicationContext
的构造器源码,可以看出调用了refresh
方法出发了整个BeanDefinition
的定位过程
public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent) throws BeansException {
super(parent);
Assert.notNull(paths, "Path array must not be null");
Assert.notNull(clazz, "Class argument must not be null");
this.configResources = new Resource[paths.length];
for(int i = 0; i < paths.length; ++i) {
this.configResources[i] = new ClassPathResource(paths[i], clazz);
}
this.refresh();
}
第二个过程是BeanDefinition
的载入。把用户定义号的Bean表示成IoC
容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition
。这个BeanDefinition
实际上就是类对象在IoC
容器中的抽象,通过BeanDefinition
,使IoC
容器更好的管理Bean。
而在上面的
ClassPathXmlApplicationContext
源码中,我们并没有看到关于beanDefinitionReader
(BeanDefinition
的读入器)的定义,因为在设计的时候将读入器相关内容放在了ClassPathXmlApplicationContext
的基类中
上面的代码片段中,可以清晰的看到XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
这段代码,同时下面也将读入器放到了DefaultListableBeanFactory
中以及对BeanDefinition
的载入
第三个过程是向IoC
容器注册这些BeanDefinition
。通过调用BeanDefinitionRegistry
接口的实现来完成的。
值得注意的是,
bean
定义的载入,是不包含依赖注入的,这也说明了之前我们在不使用后处理器的时候,只注册了Config
类