第二讲、Spring之容器实现
文章内容根据黑马程序员Spring视频教程所写!!!
视频地址:点击这里
BeanFactory实现
/**
* @author Becant
* 2024-04-24-20:06
* @version 1.0.0
* 测试
*/
public class TestBeanFactory {
public static void main(String[] args) {
test1();
test2();
}
@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("构造 Bean1()");}
}
}
BeanFactory后处理器
public void test1() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义 (class, scope, 初始化,销毁)
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
// 注册到bean工厂
beanFactory.registerBeanDefinition("config", beanDefinition);
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
}
test1()方法只是将Config类注册到bean工厂中,至于在Config类中注册的两个Bean为什么没有在控制台打印出来,原因是beanFactory没有解析注解的功能,下面我们来看test2()
控制台打印:
public void test2() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义 (class, scope, 初始化,销毁)
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
// 注册到BeanFactory
beanFactory.registerBeanDefinition("config", beanDefinition);
// 添加Spring内置的后处理器,这些后处理器也是bean
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// 执行BeanFactory后处理器
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
.forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
}
给bean工厂添加后处理器,执行后处理器,其中internalConfigurationAnnotationProcessor
内处理器有解析@Configuration的功能。
小疑惑:添加内置的后处理器有5个,那么执行的有多少个呢,下面来看控制台打印
只执行了2个,BeanFactoryPostProcessor只包含了两个Spring内置的两个后处理器。
小结:通过上面的案例,我们发现了其实BeanFactory没有那么多功能,很多功能都是通过后处理器实现的。BeanFactory后处理器的主要功能,就是补充了bean的一些定义。
过渡
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
,打印这段代码,发现只有Bean1被注入容器,但是Bean1里面获取不到Bean2,明明Bean2被@Autowired修饰。
原因是AutowiredAnnotationBeanPostProcessor没执行。AutowiredAnnotationBeanPostProcessor是Bean后处理器,和BeanFactory后处理器不是同一个东西。
Bean后处理器
Bean后处理器,针对bean的生命周期的各个阶段提供扩展。例如@Autowired,@Resource
通过控制台打印,我们也发现了Bean后处理器有两个,AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor,AutowiredAnnotationBeanPostProcessor可以解析@Autowired;CommonAnnotationBeanPostProcessor可以解析@Resource
// 执行Bean后处理器
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
执行了Bean后处理器,控制台的打印:
注意:当需要用到Bean时,才会去创建Bean,如果想先创建Bean,再使用则要加上beanFactory.preInstantiateSingletons();
这句代码是准备好单例Bean的功能
后处理器排序
问题引出:如果同时对一个变量加了@Autowired和@Resource,会出现什么情况呢?
修改一下代码,增加了Bean3和Bean4,以及接口inter,Bean3和Bean4实现了inter
@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 interface Inter{}
static class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
public Bean1() {log.debug("构造 Bean1()");}
@Autowired
private Bean2 bean2;
@Autowired
@Resource(name = "bean4")
// 说明:如果有多个bean对象,@Autowired会根据变量名优先获取bean,也就是获取bean3
// @Resource则根据name获取,也就是获取bean4
private Inter bean3;
public Bean2 getBean2() {return bean2;}
public Inter getInter() {return bean3;}
}
static class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {log.debug("构造 Bean2()");}
}
static class Bean3 implements Inter{
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean3() {log.debug("构造 Bean3()");}
}
static class Bean4 implements Inter{
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean4() {log.debug("构造 Bean4()");}
}
在原有的代码基础上,我们执行 System.out.println(beanFactory.getBean(Bean1.class).getInter());
控制台打印了:
为什么获取了Bean3对象而不是获取Bean4对象呢,这是根据Bean后处理器的执行顺序有关的,因为先执行了AutowiredAnnotationBeanPostProcessor,然后才执行CommonAnnotationBeanPostProcessor。故解析@Autowired。
现在,我们让CommonAnnotationBeanPostProcessor先执行,AutowiredAnnotationBeanPostProcessor后执行。
// 添加比较器
beanFactory.getBeansOfType(BeanPostProcessor.class).values()
.stream().sorted(beanFactory.getDependencyComparator())
.forEach(beanPostProcessor -> {
System.out.println(beanPostProcessor);
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
比较器
比较器怎么来的?在添加Spring内置后处理器的时候,也就是执行AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
的时候,这个方法会设置一个比较器
排序原理
数字越小,排序越优先。
ApplicationContext实现
/**
* @author Becant
* 2024-04-25-16:18
* @version 1.0.0
* ApplicationContext的实现
*/
public class A02Application {
public static void main(String[] args) {
testAnnotationConfigApplicationContext();
testAnnotationConfigServletWebServerApplicationContext();
}
@Configuration
static class WebConfig {
// 注册内嵌的Tomcat服务器
@Bean
public ServletWebServerFactory ServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
// 注册DispatcherServlet
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
// 将DispatcherServlet注册到Tomcat上
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
// 当web发来请求时,优先通过注册DispatcherServlet,再请求分发到控制器
@Bean("/hello")
public Controller controller1() {
return (request, response) -> {
response.getWriter().print("hello");
return null;
};
}
}
@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 this.bean1;
}
}
}
ClassPathXmlApplicationContext
该类已过时,日常中基本用不上。
FileSystemXmlApplicationContext
该类已过时,日常中基本用不上。
AnnotationConfigApplicationContext
测试方法:
public 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());
}
执行结果:
对于这个类,再使用这个类的构造方法时,AnnotationConfi
gApplicationContext context = new AnnotationConfigApplicationContext(Bean1.class);
Bean1这个类上没有任何注解修饰,Bean1也会被注入容器。还有,没有在Config类上添加@Configuration注解,Config,Bean1,Bean2也会被注入容器。
AnnotationConfigServletWebServerApplicationContext
测试方法:
public static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
执行结果:
接下来访问一下localhost:8080/hello
访问成功
总结
BeanFactory
BeanFactory 不会做的事:
- 不会主动调用 Beanfactory 的后处理器
- 不会主动调用 Bean的后处理器
- 不会主动初始化单例
- 不会解析beanFactory
- 不会解析${} 与 #{}
后处理器有顺序
ApplicationContext
1.常见的ApplicationContext 容器实现
2.内嵌容器、DispatcherServlet 的创建方法、作用
3.非web环境的SpringBoot下使用AnnotationConfigApplicationContext
4.web环境下的SpringBoot使用AnnotationConfigServletWebServerApplicationContext
5.web环境下的SpringBoot核心组件就是ServletWebServerFactory和DispatcherServlet