第二讲:容器实现
目录
1.BeanFactory后处理器 --解决一些bean的定义
2.Bean后处理器 --针对bean的声明周期各个阶段提供扩展 例如@Autowired @Resource注解
2.1 ClassPathXmlApplicationContext 基于classpath下的xml格式的配置文件
2.2 FileSystemXmlApplicationContext 基于磁盘路径下的xml格式的配置文件
2.3 AnnotationConfigApplicationContext 注解配置的容器
2.4 AnnotationConfigServletWebServerApplicationContext web容器
学习目标
1.BeanFactory的实现特点
2.ApplicationContext的常见实现和用法
3.内嵌容器、注册DispatcherServlet
一、BeanFactory实现
package com.itheima.a02;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import javax.annotation.Resource;
public class TestBeanFactory {
public static void main(String[] args) {
//BeanFactory的实现 ---默认Bean工厂单子 核心容器beanFactory
DefaultListableBeanFactory beanFactory =new DefaultListableBeanFactory();
//因为beanFactory刚才是创建的时候是没有bean的,因此我要定义好bean 在容器中
//bean的定义 class scope 初始化方法 销毁方法 等等 --类似于配置文件中配置bean
// BeanDefinitionBuilder ---bean定义的构建者 genericBeanDefinition--通用的Bean定义
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//将bean的 bean的定义模板对象 注册到beanFactory容器中
beanFactory.registerBeanDefinition("config",beanDefinition);
//将beanFactory容器中的 bean定义名字进行遍历
for (String s : beanFactory.getBeanDefinitionNames()) {
System.out.println(s);
}
}
@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类中 设置了@Bean 为什么没有的 ---因为现在beanFactory是没有解析 @Configuration和@Bean注解的能力的
1.BeanFactory后处理器 --解决一些bean的定义
加入这段代码后
//AnnotationConfigUtils 注解配置工具类
//registerAnnotationConfigProcessors 注册注解配置处理器
//---给beanFactory 添加一些常用的后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
for (String s : beanFactory.getBeanDefinitionNames()) {
System.out.println(s);
}
出现的结果是
但是我们发现 加入这些后处理器,结果并没有出现 @Bean注解下的类
这是因为 上面这段代码只是将 后处理器加入到BeanFactory中 并没有进行运行
加入这段代码 将后处理器运行在容器中
//通过 BeanFactory后处理器类的类型来获取全部的后处理器 然后遍历 将这些后处理器运行在beanFactory容器中
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().stream().
forEach(beanFactoryPostProcessor ->beanFactoryPostProcessor.postProcessBeanFactory(beanFactory));
for (String s : beanFactory.getBeanDefinitionNames()) {
System.out.println(s);
}
得到结果
加入这段代码后
//获取bean1 然后调用bean1的getBean2() 方法 System.out.println(beanFactory.getBean(Bean1.class).getBean2());
得到结果
我们发现获取到的bean2是null ---这代表@Autowired这个依赖注入的注解没有实现
2.Bean后处理器 --针对bean的声明周期各个阶段提供扩展 例如@Autowired @Resource注解
添加代码
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
.forEach(beanPostProcessor -> beanFactory.addBeanPostProcessor(beanPostProcessor));
得到结果 说明bean后处理器生效了
3.初始化单例对象
我么只需要加入一行代码beanFactory.preInstantiateSingletons(); //提前准备好所有的单例
4.bean后处理器的排序的逻辑
当同时添加@Autowired 和@Resource的时候 会先执行 @Autowired
我们发现是Autowired在前 因此是指向的是@Autowired
这里我们可以设置来改变后处理器的顺序
基本原理是
beanFactory学到了什么
1.不会主动调用BeanFactory的后处理器 来解析@Configuration和@Bean等注解
2.不会主动添加Bean的后处理 来解析@Autowired注解
3.不会主动初始化单例的对象
4.不会解析beanFactory中的${}和#{}
5.bean后处理器有一个排序的逻辑
二、ApplicationContext的实现
2.1 ClassPathXmlApplicationContext 基于classpath下的xml格式的配置文件
2.2 FileSystemXmlApplicationContext 基于磁盘路径下的xml格式的配置文件
下面演示ClassPathXmlApplicationContext(ApplicationContext接口的实现)比BeanFactory多出功能的实现
public static void main(String[] args) {
// testClassPathXmlApplicationContext();
// testFileSystemXmlApplicationContext();
//默认beanFactory表单
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
//读取前
for (String s : beanFactory.getBeanDefinitionNames()) {
System.out.println(s);
}
//xml文件bean定义的读取者 --存入到哪里
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(beanFactory);
//将xml文件信息加载进入
reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
System.out.println("----------");
//读取后
for (String s : beanFactory.getBeanDefinitionNames()) {
System.out.println(s);
}
}
结果:
2.3 AnnotationConfigApplicationContext 注解配置的容器
先要创建一个注解类 进行配置bean
@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;
}
}
我们有注意到,上面的ClassPathXmlApplicationContext 并没有自动加入
未进行配置时:
配置后:
如果要加入只需要在xml配置文件中加入
2.4 AnnotationConfigServletWebServerApplicationContext web容器
// ⬇️较为经典的容器, 基于 java 配置类来创建, 用于 web 环境
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context=
new AnnotationConfigServletWebServerApplicationContext(webConfig.class);
}
@Configuration
static class webConfig{
@Bean
//web服务器工厂控制器
public ServletWebServerFactory servletWebServerFactory (){
return new TomcatServletWebServerFactory();
}
@Bean //前端控制器
public DispatcherServlet dispatcherServlet(){
return new DispatcherServlet();
}
@Bean //将DispathcherServlet注册到tomcat中
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){
return new DispatcherServletRegistrationBean(dispatcherServlet,"/");
}
//控制器
@Bean("/hello")
public Controller controller(){
return new Controller() {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().write("hello");
return null;
}
};
}
}
这个其实就是内嵌了web服务器 是springboot的web服务器的本质
总结
ClassPathXmlApplicationContext 基于classpath下的xml格式的配置文件
FileSystemXmlApplicationContext 基于磁盘路径下的xml格式的配置文件
AnnotationConfigApplicationContext 注解配置的容器
AnnotationConfigServletWebServerApplicationContext web容器
前两种是具有xml配置文件的容器 ---现在已经不常用了
后两种比较常用 一个是非web环境的容器 一个是web环境的容器