BeanFactory实现的特点
我们来着重讲一下DefaultListableBeanFactory
这个实现类:
点击查看完整代码
package com.itvayne.springbootcloudstudy.beanfactory01;
import com.sun.org.slf4j.internal.Logger;
import com.sun.org.slf4j.internal.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;
/**
* @program: cloud
* @description:
* @author: Vayne
* @create: 2023-12-05
**/
public class TestBeanFactory01 {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//我们需要对bean的定义,描述一个bean的类型,初始化方法,销毁方法,以及我们常说的单例还是多例等信息
//我们下方的Bean1和Bean2其实依赖于我们的Config类,那我们先来定义一个Config类的定义类
//定一个Config类的描述定义类(有点拗口,各自理解一下)
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//将这个描述类交给beanFactroy制作出一个Config类对象
beanFactory.registerBeanDefinition("config", beanDefinition);
//我们此时可以遍历beanFactory,查看beanFactory中有哪些bean了
for (String bean : beanFactory.getBeanDefinitionNames()) {
System.out.println(bean);
}
//通过观察我们发现,没有Bean1和Bean2,这是为什么?,说明BeanFactory并不具备解析我们@bean的能力
//那怎么让他具备呢?
//通过下面这个工具类中的registerAnnotationConfigProcessors添加注解配置的后处理器,对BeanFactory的解析能力进行增强
System.out.println("============1.解析Bean1和Bean2,加入后处理器==============");
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
//此时我们再来观察BeanFactory
for (String bean : beanFactory.getBeanDefinitionNames()) {
System.out.println(bean);
}
/**
* 发现多了这五个类,我们来说下他们分别是干嘛的
* org.springframework.context.annotation.internalConfigurationAnnotationProcessor,处理@Configuration,以及@Bean注解的后处理器
* org.springframework.context.annotation.internalAutowiredAnnotationProcessor,处理@Autowired以及@value注解的后处理器
* org.springframework.context.annotation.internalCommonAnnotationProcessor,处理@Resource注解
* org.springframework.context.event.internalEventListenerProcessor
* org.springframework.context.event.internalEventListenerFactory
*/
System.out.println("===========2.解析Bean1和Bean2===============");
//那为什么有了后处理器我们依然看不到Bean1和Bean2呢???
//我们还需要对后处理器进行调用
//根据beanFactory拿到里面所有的后处理器,然后遍历,将每个后处理器执行在BeanFactory上,然后再次打印观察
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().stream().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
.sorted(beanFactory.getDependencyComparator())
.forEach(beanPostProcessor -> {
System.out.println(">>>>>>>>>" + beanPostProcessor);
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
beanFactory.preInstantiateSingletons();
for (String bean : beanFactory.getBeanDefinitionNames()) {
System.out.println(bean);
}
System.out.println("==========================");
//通过输出结果我们看到了Bean1和Bean2,此时我们来看看@Autowired有没有生效
//System.out.println(beanFactory.getBean(Bean1.class).getBean2());
//查看结果,我们看到虽然Bean1和Bean2通过Config类的@Bean识别出来了,但是Bean1中注入的Bean2缺没有生效,说明@Autowired没有识别
//我们上面看到的Config类中识别出了Bean1和Bean2其实是利用了BeanFactory的后处理器,BeanFactoryPostProcessor
//而此时我们需要一个对于Bean的后处理器来帮我们识别Bean的各个生命周期中的注解,比如@Autowired,@Resource等等
System.out.println("============查看Bean的加载时机==============");
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
//此时我们仍然获取不到,因为在上面我们循环获取的时候,Bean已经初始化完成,所以要将Bean后处理器放在前面
//beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);放在Bean加载之前
//我们看到这个Bean其实是在我们getBean的时候才加载的,如果需要一开始就加载好就需要
//beanFactory.preInstantiateSingletons();
}
@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()");
}
}
}
public class TestBeanFactory01 {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//我们需要对bean的定义,描述一个bean的类型,初始化方法,销毁方法,以及我们常说的单例还是多例等信息
//我们下方的Bean1和Bean2其实依赖于我们的Config类,那我们先来定义一个Config类的定义类
//定一个Config类的描述定义类(有点拗口,各自理解一下)
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//将这个描述类交给beanFactroy制作出一个Config类对象
beanFactory.registerBeanDefinition("config", beanDefinition);
//我们此时可以遍历beanFactory,查看beanFactory中有哪些bean了
String[] beans = beanFactory.getBeanDefinitionNames();
for (String bean : beans) {
System.out.println(bean);
}
}
@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("Kiti 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("Miti Bean2()");
}
}
}
通过观察我们发现,没有Bean1和Bean2,这是为什么?,说明BeanFactory并不具备解析我们@bean的能力
//那怎么让他具备呢?
//通过下面这个工具类中的registerAnnotationConfigProcessors添加注解配置的后处理器,对BeanFactory的解析能力进行增强
System.out.println("==========================");
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
//此时我们再来观察BeanFactory
for (String bean : beanFactory.getBeanDefinitionNames()) {
System.out.println(bean);
}
/**
* 发现多了这五个类,我们来说下他们分别是干嘛的
* org.springframework.context.annotation.internalConfigurationAnnotationProcessor,处理@Configuration,以及@Bean注解的后处理器
* org.springframework.context.annotation.internalAutowiredAnnotationProcessor,处理Autowired注解的后处理器
* org.springframework.context.annotation.internalCommonAnnotationProcessor
* org.springframework.context.event.internalEventListenerProcessor
* org.springframework.context.event.internalEventListenerFactory
*/
System.out.println("==========================");
//那为什么有了后处理器我们依然看不到Bean1和Bean2呢???
//我们还需要对后处理器进行调用
//根据beanFactory拿到里面所有的后处理器,然后遍历,将每个后处理器执行在BeanFactory上,然后再次打印观察
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().stream().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
for (String bean : beanFactory.getBeanDefinitionNames()) {
System.out.println(bean);
}
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().stream().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
beanFactory.preInstantiateSingletons();
for (String bean : beanFactory.getBeanDefinitionNames()) {
System.out.println(bean);
}
System.out.println("==========================");
//通过输出结果我们看到了Bean1和Bean2,此时我们来看看@Autowired有没有生效
//System.out.println(beanFactory.getBean(Bean1.class).getBean2());
//查看结果,我们看到虽然Bean1和Bean2通过Config类的@Bean识别出来了,但是Bean1中注入的Bean2缺没有生效,说明@Autowired没有识别
//我们上面看到的Config类中识别出了Bean1和Bean2其实是利用了BeanFactory的后处理器,BeanFactoryPostProcessor
//而此时我们需要一个对于Bean的后处理器来帮我们识别Bean的各个生命周期中的注解,比如@Autowired,@Resource等等
for (String bean : beanFactory.getBeanDefinitionNames()) {
System.out.println(bean);
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
//此时我们仍然获取不到,因为在上面我们循环获取的时候,Bean已经初始化完成,所以要将Bean后处理器放在前面
//beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);放在Bean加载之前
//我们看到这个Bean其实是在我们getBean的时候才加载的,如果需要一开始就加载好就需要
//beanFactory.preInstantiateSingletons();
那么到此我们发现BeanFactory有个初步的了解
-
不会主动调用BeanFactroy后处理器
-
不会主动添加Bean后处理器
-
不会主动初始化实例
-
不会解析BeanFactory,也不能解析 ${ } 和 #{}
那么我们即将学习的ApplicationContext将会给我们准备好这些东西
然后我们来看一下这个后处理器,那么后处理器的顺序决定了哪个注解先被解析
比如我们@Recourse 和 @Autowired 两个注解,如果一个接口有两个实现,那么我们利用@Autowired的先ByType再ByName的特性,注入Bean1,然后利用@Resource注入Bean2
那么到底会发生什么,其实这个注入Bean1还是Bean2,取决于Bean后处理器的加载顺序,这两个对应的Bean后处理器分别是:
org.springframework.context.annotation.internalAutowiredAnnotationProcessor ->@Autowired
org.springframework.context.annotation.internalCommonAnnotationProcessor->@Resource
那么我们在添加BeanPostProcessor的时候则决定了他的顺序,而顺序其实是通过实现了Order的接口来完成的,如果需要修改顺序优先级,可以在遍历添加的时候加入.sorted(beanFactory.getDependencyCmparator())
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
//.sorted(beanFactory.getDependencyComparator())
.forEach(beanPostProcessor -> {
System.out.println(">>>>>>>>>"+beanPostProcessor);
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
ApplicationContext的常见实现和用法
来说说四个很典型的实现类
ClassPathXmlApplicationContext();
FileSystemXmlApplicationContext();
AnnotationConfigApplicationContext();
AnnotationConfigServletWebServerApplicationContext();
来看看他们于ApplicationContext之间的关系
点击查看完整代码
package com.itvayne.springbootcloudstudy.applicationcontext01;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
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.core.io.ClassPathResource;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @program: cloud
* @description:
* @author: Vayne
* @create: 2023-12-05
**/
public class ApplicationContext01 {
public static void main(String[] args) {
// testClassPathXmlApplicationContext();
// testFileSystemXmlApplicationContext();
// testAnnotationConfigApplicationContext();
testAnnotationConfigServletWebServerApplicationContext();
}
//软为经典的容器,基于 cLasspath下xmL 格式的配置文件来创建
private static void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("b01.xml");
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>");
for (String beanDefinitionName : ctx.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
//发现这个容器想比于BeanFactory来讲直接就可以获取到
System.out.println(ctx.getBean("bean2", Bean2.class).getBean1());
System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<<");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取前...");
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
System.out.println("读取后...");
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
}
//基于磁盘路径下XmL 格式的配置文件来创建
private static void testFileSystemXmlApplicationContext() {
FileSystemXmlApplicationContext ctx =
new FileSystemXmlApplicationContext(
"//Users/vayne/IdeaProjects/cloud/src/main/resources/b01.xml");
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>");
for (String beanDefinitionName : ctx.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
//发现这个容器想比于BeanFactory来讲直接就可以获取到
System.out.println(ctx.getBean("bean2", Bean2.class).getBean1());
}
//较为经典的容器,基于 java 配置类来创建
private static void testAnnotationConfigApplicationContext() {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(Config.class);
for (String beanDefinitionName : ctx.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
System.out.println(ctx.getBean("bean2", Bean2.class).getBean1());
}
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext ctx =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
}
@Configuration
static class WebConfig {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean("/hello")
public Controller controller() {
return (request, response) -> {
response.getWriter().println("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 bean1;
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bean1"
class="com.itvayne.springbootcloudstudy.applicationcontext01.ApplicationContext01$Bean1"/>
<bean id="bean2"
class="com.itvayne.springbootcloudstudy.applicationcontext01.ApplicationContext01$Bean2">
<property name="bean1" ref="bean1"/>
</bean>
</beans>
ClassPathXmlApplicationContext
我们先来看看ClassPathXmlApplicationContext();
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("b01.xml");
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>");
for (String beanDefinitionName : ctx.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
//发现这个容器想比于BeanFactory来讲直接就可以获取到
System.out.println(ctx.getBean("bean2", 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;
}
}
其实我们写过原生spring框架的朋友可能再熟悉不过了,但是现在可能编写配置文件已经成为历史
FileSystemXmlApplicationContext
再来看看FileSystemXmlApplicationContext();不同的是从磁盘路径开始的,当然相对路径绝对路径都可以
private static void testFileSystemXmlApplicationContext() {
FileSystemXmlApplicationContext ctx =
new FileSystemXmlApplicationContext(
"//Users/vayne/IdeaProjects/cloud/src/main/resources/b01.xml");
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>");
for (String beanDefinitionName : ctx.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
//发现这个容器想比于BeanFactory来讲直接就可以获取到
System.out.println(ctx.getBean("bean2", 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;
}
}
那么两种根据路径实现的容器的简单使用讲完了,那么内部是怎么实现的从xml文件读取配置加载到容器中的呢?
手写一个简单实现:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取前...");
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
System.out.println("读取后...");
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
再来看看spring的实现方式:
当然FileSystemXmlApplicationContext也是如此,只是直接获取的
通过前两个实现,我们知道ApplicationContext可以帮我们实例化单例,以及加载Bean的定义信息,这个信息可以来自配置文件
AnnotationConfigApplicationContext
简单使用
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(Config.class);
for (String beanDefinitionName : ctx.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
System.out.println(ctx.getBean("bean2", 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;
}
}
AnnotationConfigServletWebServerApplicationContext
用于web环境的一种经典容器
想要使用它,其实并不简单,web容器中我们必须最少配置三个Bean,分别是Tomcat容器工厂,前处理器DipatcherServlet,将DipatcherServlet注册到Tomcat中的Bean,这三个bean是必须的,前两者在第三者中关联起来
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext ctx =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
}
@Configuration
static class WebConfig {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean("/hello")
public Controller controller() {
return (request, response) -> {
response.getWriter().println("hello");
return null;
};
}
}
这里定义的第四个为web容器的控制器,有个隐藏规则,如果bean的名称是以"/"开头,则直接可以当作路径访问,启动之后可以使用接口调用访问到这个路径并返回"hello"信息
小结:
-
内嵌Tomcat如何工作的?
-
所有的请求都被前处理器dispatcherServlet拦截,然后经过前处理器进一步分发请求到了我们的控制器
-
其实我们这里没有使用springBoot,而其实springBoot内部的web环境也需要这样去工作,我们后面再进行学习