spring中的容器接口的实现类和功能

容器实现

BeanFactory 实现

这里我们就来一步步实现BeanFactory的功能。

首先创建我们需要的类

@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()");
    }
}

然后我们创建BeanFactory的一个实现类DefaultListableBeanFactory。这是BeanFactory的重要实现接口。
这个BeanFactory创建完成之后,里面是没有任何Bean的信息的,它是通过保Bean的定义,然后再有需要的时候创建Bean交给我们使用,这也是控制反转的原理。
这里面的Bean的定义信息其实也是一个类就是BeanDefinition,主要描述
Beanclassscope、初始化,销毁。
所以接下来我们来创建BeanFactory并创建Config的Bean的定义交给容器管理。

// 创建容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 创建 config 的定义信息
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
// 容器中注册bean的信息,第一个参数为bean的名称
beanFactory.registerBeanDefinition("config", beanDefinition);

接下来我们来打印一下容器中所有的Bean的定义信息。

for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
    System.out.println(beanDefinitionName);
}

输出:

config

我们发现其实我们Config类中还通过@Bean注解定义了两个Bean但是为什么这里并没有加入到容器当中呢?

这是因为现在这个容器并没有解析@Bean注解的能力,这个能力是通过一个 bean工厂后处理器来实现的,这个处理器就是internalConfigurationAnnotationProcessor

这个后处理器怎么加呢?

我们可以通过Spring提供的 AnnotationConfigUtils工具类的 registerAnnotationConfigProcessors 方法来将这个类注册到容器中,所以这个方法的参数就填入beanfactory就可以了。

// 添加 BeanFactory 后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

bean 工厂后处理器添加到容器中之后,我们打印一下容器中的BeanDefinition查看一下。

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

我们发现加入了很多Bean的定义,这些后面再逐渐介绍,其中第一个就是我们需要的。

但是我们虽然加入到了容器中,就像我们之前说的,容器一开始只是保存这些Bean的定义,只有当我们需要的时候才创建。所以这里我们需要启动这个后处理器。

首先我们需要获取到这个后处理器,像这些bean工厂的后处理器都有一个共同的类型,那就是BeanFactoryPostProcessor,我们可以通过类型获取到这些处理器,然后再依次的在容器中添加这些功能。

// 启动 BeanFactory 后处理器
for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()) {
    beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
}

这时我们在打印容器中Bean的信息

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

发现 bean1、bean2 也加入了容器。

既然bean1、bean2已经加入到了容器,并且bean1也依赖注入了bean2,那么我们应该也可以获取到bean2。

我们来尝试一下:

System.out.println(beanFactory.getBean(Bean1.class).getBean2());

输出:

[DEBUG] 13:12:16.725 [main] c.sahuid.a02.TestBeanFactory$Bean1  - 构造了Bean1() 
null

我们发现 bean1 通过构造方法被创建了,但是 bean 2 为 null

这是因为当前的beanFactory并没有解析@AutoWired@Resource这两个注解的能力,这两个注解也是后处理对容器增强的功能,这种后处理器名为 bean 后处理器。而解析这两个的后处理器就是上面我们加入到容器中的一堆后处理器中的第二个和第三个:internalAutowiredAnnotationProcessorinternalCommonAnnotationProcessor,分别用来解析@AutoWired@Resource的。

他们也是属于同一个类型BeanPostProcessor,我们可以通过像启动 bean工厂后处理器的方式一样,启动 bean 后处理器

// 启动 Bean 后处理器
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

输出:

[DEBUG] 13:20:50.173 [main] c.sahuid.a02.TestBeanFactory$Bean1  - 构造了Bean1() 
[DEBUG] 13:20:50.187 [main] c.sahuid.a02.TestBeanFactory$Bean2  - 构造了Bean2() 
com.sahuid.a02.TestBeanFactory$Bean2@63355449

这里我们可以发现,目前的容器都是我们在我们使用的时候才帮我们创建出来对象。

for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
    System.out.println(beanDefinitionName);
}

System.out.println("=======");

System.out.println(beanFactory.getBean(Bean1.class).getBean2());

输出:

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] 13:21:22.509 [main] c.sahuid.a02.TestBeanFactory$Bean1  - 构造了Bean1() 
[DEBUG] 13:21:22.524 [main] c.sahuid.a02.TestBeanFactory$Bean2  - 构造了Bean2() 
com.sahuid.a02.TestBeanFactory$Bean2@63355449

里我们也可以设置在加载 bean 定义的时候就帮我们创建好对象。
加入一个这个方法preInstantiateSingletons即可

beanFactory.preInstantiateSingletons();

输出:

[DEBUG] 13:24:37.564 [main] c.sahuid.a02.TestBeanFactory$Bean1  - 构造了Bean1() 
[DEBUG] 13:24:37.578 [main] c.sahuid.a02.TestBeanFactory$Bean2  - 构造了Bean2() 
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
=======
com.sahuid.a02.TestBeanFactory$Bean2@50b472aa

这里我们就可以总结一下:
我们可以 发现beanFactory不会做的事情:

  • 不会主动调用 beanFactory 后处理器
  • 不会主动添加 bean 后处理器
  • 不会主动初始化单例
  • 还不会解析 ${} 和 #{}

ApplicationContext 实现

这里主要介绍4种ApplicationContext实现:

  • ClassPathXmlApplicationContext
  • FileSystemXmlApplicationContext
  • AnnotationConfigApplicationContext
  • AnnotationConfigServletWebServerApplicationContext

ClassPathXmlApplicationContext 实现

较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建

首先创建相关的类

static class Bean1 {

}

static class Bean2 {

    private Bean1 bean1;

    public Bean1 getBean1() {
        return bean1;
    }

    public void setBean1(Bean1 bean1) {
        this.bean1 = bean1;
    }
}

这里我们在resource目录下创建一个配置 bean 的 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"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="bean1" class="com.sahuid.a02.A02Application.Bean1"/>

  <bean id="bean2" class="com.sahuid.a02.A02Application.Bean2">
    <property name="bean1" ref="bean1"/>
  </bean>
</beans>

然后我们就创建这个类读取配置文件即可

ClassPathXmlApplicationContext context
= new ClassPathXmlApplicationContext("b01.xml");
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
    System.out.println(beanDefinitionName);
}

输出:

bean1
bean2

FileSystemXmlApplicationContext 实现

基于磁盘路线下 xml 格式的配置文件来创建

这里可以写相对路径也可以写相对路径

FileSystemXmlApplicationContext context
= new FileSystemXmlApplicationContext("D:\\Code\\spring-study\\show\\src\\main\\resources\\b01.xml");
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
    System.out.println(beanDefinitionName);
}

输出:

bean1
bean2

这两种方式底层是如何实现的呢?

其实就是还是创建的DefaultListableBeanFactory,只不过增加了一个XmlBeanDefinitionReader类,用这个类来读取xml文件的bean的配置信息,然后写入到beanfactory中。

具体模拟实现:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取之前");
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
    System.out.println(beanDefinitionName);
}
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
// 读取之后
System.out.println("读取之后");
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
    System.out.println(beanDefinitionName);
}

这两个方式的区别就在于读取方式
通过classPath的是:

reader.loadBeanDefinitions(new ClassPathResource());

过fileSystem的是:

reader.loadBeanDefinitions(new FileSystemResource());

AnnotationConfigApplicationContext

较为经典的容器, 基于 Java 配置类来创建

这里我们先创建一个配置类,然后创建bean1和bean2这两个bean

@Configuration
static class Config{
    @Bean
    public Bean1 bean1(){
        return new Bean1();
    }

    @Bean
    public Bean2 bean2(){
        return new Bean2();
    }
}

然后传入到AnnotationConfigServletWebServerApplicationContext的构造函数参数中

AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(Config.class);
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
    System.out.println(beanDefinitionName);
}

输出:

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

我们可以发现这个ApplicationContext的实现类,自动添加上了beanfactory 和 bean 的后处理器,所以可以自己解析@Bean注解

AnnotationConfigServletWebServerApplicationContext

较为经典的容器,基于 Java 配置类来创建,用于 web 环境

这是我们用于web环境的容器,这个容器也需要一个配置类,不过这个配置类配置的Bean 比较多。

@Configuration
static class WebConfig{
    @Bean
    public ServletWebServerFactory servletWebServerFactory(){
        return new TomcatServletWebServerFactory(10000);
    }

    @Bean
    public DispatcherServlet dispatcherServlet(){
        return new DispatcherServlet();
    }

    @Bean
    public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){
        return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
    }

    @Bean("/hello")
    public Controller controller1(){
        return new Controller() {
            @Override
            public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
                response.getWriter().println("hello");
                return null;
            }
        };
    }
}

ServletWebServerFactory:这个功能相当于是指定当前程序运行在什么服务上,我们都知道 Spring Boot 有内置的 Tomcat 服务器,所以我们在这里就创建一个 Tomcat 的服务,参数可以指定端口。

DispatcherServlet:这是在 Spring MVC 中非常重要的一个类,所有的请求第一步都是先进入到这个类中,相当于这个类是服务的一个入口,然后通过这个类再去匹配不同的请求。

DispatcherServletRegistrationBean:上面我们创建了服务和入口,我们需要将两个类进行绑定,而这个就是用来绑定两者的关系,第二个参数是个访问路径,带有/的都能访问进来

Controller:这个就相当于是一个我们的Controller的接口,只不过这里直接通过Bean管理了,然后访问@Bean后面的值就可以访问到接口。

接下来我们创建出来容器

AnnotationConfigServletWebServerApplicationContext context
= new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
    System.out.println(beanDefinitionName);
}

进行页面访问:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值