【Spring高级】第2讲:容器实现类

目录

  • BeanFactory实现
    • BeanDefinition
    • 后置处理器
    • 单例bean创建
    • 后置处理器顺序
    • 总结
  • ApplicationContext实现
    • ClassPathXmlApplicationContext
    • FileSystemXmlApplicationContext
    • AnnotationConfigApplicationContext
    • AnnotationConfigServletWebServerApplicationContext

BeanFactory实现

BeanDefinition

BeanDefinition顾名思义就是Bean的一些定义信息。我们平时使用的配置类、xml、组件扫描等方式都是生成 BeanDefinition 对象注册到 beanFactory 当中。

前面我们讲过DefaultListableBeanFactory,他是BeanFactory最重要的实现,其类图如下

在这里插入图片描述

下面以DefaultListableBeanFactory为例演示。

有如下示例:

public class TestBeanFactory {public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();}@Configurationstatic class Config {@Beanpublic Bean1 bean1() {return new Bean1();}}static class Bean1 {public Bean1() {System.out.println("Bean1 构造方法");}}static class Bean2 {public Bean2() {System.out.println("Bean2 构造方法");}}}

我们直接通过new创建一个beanFactory,但是其实他的功能非常简单,其内部也没有任何的Bean。

但是我们可以通过添加一些BeanDefinition,然后beanFactory利用BeanDefinition来创建Bean。

首先来看下如何添加BeanDefinition:

public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 创建Config的BeanDefinitionAbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();// 把beanDefinition注册到beanFactorybeanFactory.registerBeanDefinition("config", beanDefinition);// 查看beanFactory中beanDefinition的名字for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}
}

运行结果:

config

发现结果中只有config,说明我们的BeanDefinition已经注册到beanFactory中了。但是此时还是没有任何的Bean,因为还没有去使用BeanDefinition创建Bean。要创建Bean,不是beanFactory能够完成的,需要借助于后处理器,他能识别@Configuration注解和@Bean注解,从而创建对应的Bean。

后置处理器

添加后置处理器的代码如下:

// 给beanFactory添加一些内置的后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

添加后再打印出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

可以看到,除了config,还添加了5个内置的处理器的BeanDefinition,其中internalConfigurationAnnotationProcessor就是来处理@Configuration注解的。

但是上面的步骤只是把BeanDefinition加到了beanFactory中,还并没有开始被使用。下面看下怎么运行后置处理器:

public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 创建Config的BeanDefinitionAbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();// 把beanDefinition注册到beanFactorybeanFactory.registerBeanDefinition("config", beanDefinition);// 给beanFactory添加一些内置的后处理器AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);// 拿到beanFactory的后置处理器,这里有很多,其中一个就是internalConfigurationAnnotationProcessorMap<String, BeanFactoryPostProcessor> processorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);// 执行后置处理器,当internalConfigurationAnnotationProcessor运行,就会把Bean1和Bean2的beanDefinition注册到beanFactoryprocessorMap.values().stream().forEach(processor -> {processor.postProcessBeanFactory(beanFactory);});// 查看beanFactory中beanDefinition的名字for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}}

运行结果如下:

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成功被注册到了beanFactory。

所以,beanFactory本身功能比较简单,但是可以通过一些后置处理器,补充了一些Bean的定义,即BeanDefinition。

上面是把BeanDefinition成功注册到了beanFactory,那实际我们的Bean是否已经成功创建了呢?使用如下代码:

// 获取Bean
Bean1 bean1 = beanFactory.getBean(Bean1.class);
System.out.println(bean1.getBean2());

结果如下:

Bean1 构造方法
null

可以看到,Bean1被成功创建了,且是使用getBean时,调用了其构造方法。但是他的bean2属性却是null,缺少了依赖注入的功能。这是因为还缺少了其他的后置处理器,就是Bean的后置处理器,他能够解析Autowired注解。注意他与BeanFactory的后置处理器不同,beanFactory的后置处理器主要补充BeanDefinition,而Bean的后置处理器主要对Bean的生命周期内进行一些处理。

注意上面打印的BeanDefinition,有一个internalAutowiredAnnotationProcessor,他就是来处理Autowired注解的。

public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 创建Config的BeanDefinitionAbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();// 把beanDefinition注册到beanFactorybeanFactory.registerBeanDefinition("config", beanDefinition);// 给beanFactory添加一些内置的后处理器,注意只是把后处理器添加到了beanFactory而已,后处理器还不会起作用AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);// 拿到beanFactory的后置处理器,这里有很多,其中一个就是internalConfigurationAnnotationProcessorMap<String, BeanFactoryPostProcessor> processorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);// 执行后置处理器,当internalConfigurationAnnotationProcessor运行,就会把Bean1和Bean2的beanDefinition注册到beanFactoryprocessorMap.values().stream().forEach(processor -> {processor.postProcessBeanFactory(beanFactory);});// Bean 的后处理器,针对Bean的生命周期的各个阶段提供扩展,例如@Autowired。// 注意这一步,是把后处理器与beanFactory绑定,这样创建Bean时,后处理器才能起作用beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().forEach(beanFactory::addBeanPostProcessor);// 查看beanFactory中beanDefinition的名字for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}System.out.println("---------- 分割线 ----------");// 获取BeanBean1 bean1 = beanFactory.getBean(Bean1.class);System.out.println(bean1.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
---------- 分割线 ----------
16:47:57.859 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
16:47:57.860 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
Bean1 构造方法
16:47:57.907 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
Bean2 构造方法
com.cys.demo02.TestBeanFactory$Bean2@3e11f9e9

可以看到,成功把bean2注入到了bean1。

单例bean创建

另外从上面的例子还可以看到,上面的分割线之后,bean1和bean2才开始创建,且他们都是单例的。也就说,只有当真正需要bean的时候,他才会去创建。我们可以使用方法preInstantiateSingletons,主动去创建那些单例bean。代码如下:

public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 省略代码...// 主动创建单例beanbeanFactory.preInstantiateSingletons();System.out.println("---------- 分割线 ----------");// 获取BeanBean1 bean1 = beanFactory.getBean(Bean1.class);System.out.println(bean1.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
16:56:37.614 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
16:56:37.624 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
Bean1 构造方法
16:56:37.663 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
Bean2 构造方法
---------- 分割线 ----------
com.cys.demo02.TestBeanFactory$Bean2@49049a04

可以看到,bean的创建在分割线之前了,也就是这些单例bean被提前创建好了。

后置处理器顺序

处理是有顺序的,不同的顺序对解析结果影响也不同。

看如下一个例子:

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 java.net.InterfaceAddress;
import java.util.Map;public class TestBeanFactoryPostProcessor {public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 创建Config的BeanDefinitionAbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();// 把beanDefinition注册到beanFactorybeanFactory.registerBeanDefinition("config", beanDefinition);// 给beanFactory添加一些内置的后处理器AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);// 拿到beanFactory的后置处理器,这里有很多,其中一个就是internalConfigurationAnnotationProcessorMap<String, BeanFactoryPostProcessor> processorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);// 执行后置处理器,当internalConfigurationAnnotationProcessor运行,就会把Bean1和Bean2的beanDefinition注册到beanFactoryprocessorMap.values().stream().forEach(processor -> {processor.postProcessBeanFactory(beanFactory);});// Bean 的后处理器,针对Bean的生命周期的各个阶段提供扩展,例如@AutowiredbeanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().forEach(beanFactory::addBeanPostProcessor);// 查看beanFactory中beanDefinition的名字
//        for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
//            System.out.println(beanDefinitionName);
//        }// 主动创建单例beanbeanFactory.preInstantiateSingletons();System.out.println("---------- 分割线 ----------");// 获取BeanBean1 bean1 = beanFactory.getBean(Bean1.class);System.out.println(bean1.getInter());}@Configurationstatic class Config {@Beanpublic Bean1 bean1() {return new Bean1();}@Beanpublic Bean2 bean2() {return new Bean2();}@Beanpublic Bean3 bean3() {return new Bean3();}@Beanpublic Bean4 bean4() {return new Bean4();}}interface Inter {};static class Bean3 implements Inter{};static class Bean4 implements Inter{};static class Bean1 {public Bean1() {System.out.println("Bean1 构造方法");}@Autowiredprivate Bean2 bean2;public Bean2 getBean2() {return bean2;}@Autowiredprivate Inter inter;public Inter getInter() {return inter;}}static class Bean2 {public Bean2() {System.out.println("Bean2 构造方法");}}
}

与前面不同的是,又重新定义了Bean3和Bean4,他们都实现了接口Inter,然后在Bean1中,使用了@Autowired注入一个Inter类型的Bean,由前面Spring的基础知识,我们知道,Autowired是根据诶行匹配的,由于Bean3和Bean4都实现了Inter,容器不知道到底是注入Bean3还是Bean4,会报错。

以前的解决办法可以使用@Qualifier("bean3")指定注入bean3。除此以外,我们还可以直接修改Bean1中成员变量的名字,如下:

static class Bean1 {public Bean1() {System.out.println("Bean1 构造方法");}@Autowiredprivate Bean2 bean2;public Bean2 getBean2() {return bean2;}@Autowiredprivate Inter bean3;public Inter getInter() {return bean3;}}

运行后发现这样也是可以的,这是因为容器当发现多个同一类型的bean时,容器会去比较成员变量的名字与bean的名字进行匹配,注入能够匹配名字的bean。

还有一种,如果使用@Resource,他也是按照类型匹配的,但是他有个name属性,可以指定注入的bean的名字:

static class Bean1 {public Bean1() {System.out.println("Bean1 构造方法");}@Autowiredprivate Bean2 bean2;public Bean2 getBean2() {return bean2;}@Resource(name = "bean4")private Inter bean3;public Inter getInter() {return bean3;}}

可以发现,最后注入的还是bean4。

那么问题来了,如果同时使用@Autowired和@Resource会怎么样呢?

static class Bean1 {public Bean1() {System.out.println("Bean1 构造方法");}@Autowiredprivate Bean2 bean2;public Bean2 getBean2() {return bean2;}@Autowired@Resource(name = "bean4")private Inter bean3;public Inter getInter() {return bean3;}}

运行结果如下:

Bean1 构造方法
17:37:36.885 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
Bean2 构造方法
17:37:36.891 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
17:37:36.891 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean4'
---------- 分割线 ----------
com.cys.demo02.TestBeanFactoryPostProcessor$Bean3@506ae4d4

可以发现,最后结果是bean3,这是为什么呢?

这就和后置处理器的顺序有关了。由于AutowiredAnnotationBeanPostProcessor是先添加到beanFactory,所以他先解析,后面的就不会再解析了。

代码验证,打印出顺序:

public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 创建Config的BeanDefinitionAbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();// 把beanDefinition注册到beanFactorybeanFactory.registerBeanDefinition("config", beanDefinition);// 给beanFactory添加一些内置的后处理器AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);// 拿到beanFactory的后置处理器,这里有很多,其中一个就是internalConfigurationAnnotationProcessorMap<String, BeanFactoryPostProcessor> processorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);// 执行后置处理器,当internalConfigurationAnnotationProcessor运行,就会把Bean1和Bean2的beanDefinition注册到beanFactoryprocessorMap.values().stream().forEach(processor -> {processor.postProcessBeanFactory(beanFactory);});// Bean 的后处理器,针对Bean的生命周期的各个阶段提供扩展,例如@AutowiredbeanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().forEach(processor -> {System.out.println("processor:" + processor);beanFactory.addBeanPostProcessor(processor);});// 主动创建单例beanbeanFactory.preInstantiateSingletons();System.out.println("---------- 分割线 ----------");// 获取BeanBean1 bean1 = beanFactory.getBean(Bean1.class);System.out.println(bean1.getInter());}

打印结果如下:

processor:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@49c7b90e
processor:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@10d307f1

可以看到AutowiredAnnotationBeanPostProcessor先添加,然后是CommonAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor就是用来解析@Resource的。

我们也可以通过beanFactory的getDependencyComparator方法改变后置处理器的顺序:

beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().sorted(beanFactory.getDependencyComparator()).forEach(processor -> {System.out.println("processor:" + processor);beanFactory.addBeanPostProcessor(processor);
});

getDependencyComparator方法是由前面的AnnotationConfigUtils工具类提供给beanFactory的:

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);if (beanFactory != null) {if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);}if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());}}// 省略代码...
}

核心代码就是

beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);

也就是AnnotationAwareOrderComparator

public class AnnotationAwareOrderComparator extends OrderComparator {/*** Shared default instance of {@code AnnotationAwareOrderComparator}.*/public static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();/*** This implementation checks for {@link Order @Order} or* {@link javax.annotation.Priority @Priority} on various kinds of* elements, in addition to the {@link org.springframework.core.Ordered}* check in the superclass.*/@Override@Nullableprotected Integer findOrder(Object obj) {Integer order = super.findOrder(obj);if (order != null) {return order;}return findOrderFromAnnotation(obj);}@Nullableprivate Integer findOrderFromAnnotation(Object obj) {AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass());MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY);Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);if (order == null && obj instanceof DecoratingProxy) {return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass());}return order;}// 省略代码...
}

可以看到,它是一个单例对象,里面有findOrder来获取处理器的顺序。

看下AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor都有个order的属性和getOrder的方法。CommonAnnotationBeanPostProcessor的是在父类InitDestroyAnnotationBeanPostProcessorzh中。

总结

  1. beanFactory 可以通过 registerBeanDefinition 注册一个 BeanDefinition 对象
    • 我们平时使用的配置类、xml、组件扫描等方式都是生成 BeanDefinition 对象注册到 beanFactory 当中
    • BeanDefinition 描述了这个 bean 的创建蓝图:scope 是什么、用构造还是工厂创建、初始化销毁方法是什么,等等
  2. beanFactory 需要手动调用 beanFactory 后处理器对它做增强
    • 例如通过解析 @Bean、@ComponentScan 等注解,来补充一些 BeanDefinition
  3. beanFactory 需要手动添加 bean 后处理器,以便对后续 bean 的创建过程提供增强
    • 例如 @Autowired,@Resource 等注解的解析都是 bean 后处理器完成的
    • bean 后处理的添加顺序会对解析结果有影响
  4. beanFactory 需要手动调用方法来初始化单例

因为beanFactory上面的一些局限性,一般我们使用ApplicationContext比较多,因为beanFactory需要手动做的事,他都帮我们自动做好了。

ApplicationContext实现

ApplicationContext有4个比较典型的实现类ClassPathXmlApplicationContextFileSystemXmlApplicationContextAnnotationConfigApplicationContext、‘AnnotationConfigServletWebServerApplicationContext’。

ClassPathXmlApplicationContext

ClassPathXmlApplicationContext用于从类路径(classpath)中加载 XML 配置文件,进而创建和初始化 Spring 应用上下文(ApplicationContext)。当使用 ClassPathXmlApplicationContext 时,Spring 会自动从类路径中查找指定的 XML 配置文件,并根据该文件的配置创建和管理 Spring Beans。

类图如下

在这里插入图片描述

示例:

首先我们创建一个测试类:

public class TestApplicationContext {public static void main(String[] args) {}static class Bean1 {}static class Bean2 {private Bean1 bean1;public Bean1 getBean1() {return bean1;}public void setBean1(Bean1 bean1) {this.bean1 =  bean1;}}}

里面定义了Bean1类和Bean2类,并且Bean2的属性有Bean1对象。

创建一个XML文件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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="bean1" class="com.cys.demo02.TestApplicationContext.Bean1"/><bean id="bean2" class="com.cys.demo02.TestApplicationContext.Bean2"><property name="bean1" ref="bean1"/></bean></beans>

测试方法:

public static void main(String[] args) {testClassPathXmlApplicationContext();
}private static void testClassPathXmlApplicationContext() {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml");for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println("beanDefinitionName:" + beanDefinitionName);}// 获取bean2Bean2 bean2 = context.getBean(Bean2.class);System.out.println(bean2.getBean1());}

结果如下:

beanDefinitionName:bean1
beanDefinitionName:bean2
com.cys.demo02.TestApplicationContext$Bean1@3e27aa33

需要注意的是,ClassPathXmlApplicationContext 在加载配置文件时会立即创建和初始化所有的单例 Bean。这意味着,如果你在配置文件中定义了一个单例 Bean,并且它有一个在初始化时执行的构造函数或 init-method,那么当 ClassPathXmlApplicationContext 加载配置文件时,这个 Bean 的构造函数或 init-method 会被调用。

FileSystemXmlApplicationContext

FileSystemXmlApplicationContext与ClassPathXmlApplicationContext类似,只不过他是读取系统中文件来,且文件位置不是相对于类路径了,而是绝对路径或者项目跟目录。这个用的很少。

示例:

public static void main(String[] args) {testFileSystemXmlApplicationContext();
}private static void testFileSystemXmlApplicationContext() {// 注意些绝对路径,且要是双斜杠FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("src//main//resources//b01.xml");for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println("beanDefinitionName:" + beanDefinitionName);}// 获取bean2Bean2 bean2 = context.getBean(Bean2.class);System.out.println(bean2.getBean1());
}

其结果一样。

AnnotationConfigApplicationContext

AnnotationConfigApplicationContext用于加载基于 Java 配置类的应用程序上下文。与 ClassPathXmlApplicationContext 不同,AnnotationConfigApplicationContext 依赖于 Java 类(使用 @Configuration 注解)而不是 XML 文件来定义和配置 Spring Beans。

使用 AnnotationConfigApplicationContext 的主要好处是它可以利用 Java 的编译时类型检查,从而提供更强大的配置验证。此外,Java 配置通常更加简洁和灵活,因为它可以直接利用 Java 的编程能力,如条件配置、环境特定的配置等。

示例:

首先创建一个配置类:

@Configuration
static class Config {@Beanpublic Bean1 bean1() {return new Bean1();}@Beanpublic Bean2 bean2(Bean1 bean1) {Bean2 bean2 = new Bean2();bean2.setBean1(bean1);return bean2;}
}

测试:

public static void main(String[] args) {
//        testClassPathXmlApplicationContext();testAnnotationConfigApplicationContext();}private static void testAnnotationConfigApplicationContext() {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println("beanDefinitionName:" + beanDefinitionName);}// 获取bean2Bean2 bean2 = context.getBean(Bean2.class);System.out.println(bean2.getBean1());}

结果如下:

beanDefinitionName:org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanDefinitionName:org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanDefinitionName:org.springframework.context.annotation.internalCommonAnnotationProcessor
beanDefinitionName:org.springframework.context.event.internalEventListenerProcessor
beanDefinitionName:org.springframework.context.event.internalEventListenerFactory
beanDefinitionName:testApplicationContext.Config
beanDefinitionName:bean1
beanDefinitionName:bean2
com.cys.demo02.TestApplicationContext$Bean1@6cb107fd

看到,成功创建了bean1和bean2,并把bean1注入了bean2。

另外上面五个后置处理器,也帮我们注册到了容器中。

AnnotationConfigServletWebServerApplicationContext

它是 AnnotationConfigApplicationContext 的一个子类,专门用于创建基于 Java 配置的 Servlet Web 应用程序上下文。这个类结合了 Spring Boot 的自动配置功能和 Spring MVC 的 Web 功能,使得开发者能够快速地创建和部署基于 Java 配置的 Web 应用程序。

AnnotationConfigServletWebServerApplicationContext 通常用于启动 Spring Boot 的 Web 应用程序。它会自动配置 Tomcat、Jetty 或 Undertow 等嵌入式 Web 服务器,并根据你在配置类中定义的 @Bean 和其他注解来创建和管理应用程序上下文。

示例:

使用时也需要指定配置类:

@Configuration
static class WebConfig {/*** tomcat服务器bean** @return*/@Beanpublic ServletWebServerFactory servletWebServerFactory() {return new TomcatServletWebServerFactory();}/*** dispatcherServlet的bean** @return*/@Beanpublic DispatcherServlet dispatcherServlet() {return new DispatcherServlet();}/*** 绑定tomcat服务器和dispatcherServlet** @param dispatcherServlet* @return*/@Beanpublic DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {return new DispatcherServletRegistrationBean(dispatcherServlet, "/");}/*** 控制器bean** @return*/@Bean("/hello")public Controller controller1() {return new Controller() {@Overridepublic ModelAndView handleRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws Exception {response.getWriter().println("hello");return null;}};}
}

测试:

public static void main(String[] args) {
//        testClassPathXmlApplicationContext();
//        testAnnotationConfigApplicationContext();testAnnotationConfigServletWebServerApplicationContext();}private static void testAnnotationConfigServletWebServerApplicationContext() {AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);}

启动后,访问http://127.0.0.1:8080/hello,正常就可以访问了。

如果启动出现这个错exception is java.lang.NoClassDefFoundError: org/apache/catalina/WebResourceRoot。这是因为缺少jar包,添加如下:

<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>

通过以上案例就可以大致了解一个web服务内部的核心组件,以及和tomcat的交互。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/270682.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot3.x 以上,官方不建议使用spring.factories

springboot2.7.x 以上,官方不建议使用spring.factories 最近公司项目升级.需要将springcloud/springboot版本升级到2.7.x以上,再升级的过程中遇到了太多的问题.总结在了如下文章中: springboot艰难版本升级之路!! springboot 2.3.x版本升级到2.7.x版本 这篇文章就重点是梳理一…

如何让多个视频同时转GIF 2024全新款 高清无损转换

大家是否经常会遇到这样的问题&#xff0c;看到一些有趣的短视频片段&#xff0c;但却不知道如何将它们转换成GIF动图&#xff1f;今天&#xff0c;小编就给大家分享一个简单教程&#xff0c;教你如何批量将喜欢的短视频转换成GIF动图&#xff0c;让我们一起来学习吧&#xff0…

linux安装ngnix

一、将nginx-1.20.1.tar.gz上传至linux服务器目录下 二、将nginx安装包解压到/usr/local目录下 tar -zxvf /home/local/nginx-1.20.1.tar.gz -C /usr/local/三、预先安装依赖 yum -y install pcre-devel yum -y install openssl openssl-devel yum -y install gcc gcc-c auto…

【自然语言处理】【大模型】BitNet:用1-bit Transformer训练LLM

BitNet&#xff1a;用1-bit Transformer训练LLM 《BitNet: Scaling 1-bit Transformers for Large Language Models》 论文地址&#xff1a;https://arxiv.org/pdf/2310.11453.pdf 相关博客 【自然语言处理】【大模型】BitNet&#xff1a;用1-bit Transformer训练LLM 【自然语言…

Vue3.2 + vue/cli-service 打包 chunk-vendors.js 文件过大导致页面加载缓慢解决方案

chunk-vendors.js 是/node_modules 目录下的所有模块打包成的包&#xff0c; 但是这包太大导致页面加载很慢&#xff08;我的都要3-4秒了&#xff09;&#xff0c; 这个时候就会出现白屏的情况 解决方案 1、compression-webpack-plugin 插件解决方案 1&#xff09;、安装 npm …

解决vue项目本地开发完成后部署到服务器后报404的问题

一、如何部署 前后端分离开发模式下&#xff0c;前后端是独立布署的&#xff0c;前端只需要将最后的构建物上传至目标服务器的web容器指定的静态目录下即可 我们知道vue项目在构建后&#xff0c;是生成一系列的静态文件 常规布署我们只需要将这个目录上传至目标服务器即可 /…

redis进阶(一)

文章目录 前言一、Redis中的对象的结构体如下&#xff1a;二、压缩链表三、跳跃表 前言 Redis是一种key/value型数据库&#xff0c;其中&#xff0c;每个key和value都是使用对象表示的。 一、Redis中的对象的结构体如下&#xff1a; /** Redis 对象*/ typedef struct redisO…

element多选框select下拉框数据回显的问题value.push is not a function

文章目录 问题描述 问题描述 今天在使用Element UI el-select组件遇到了一个问题&#xff0c;如下图&#xff1a; 下拉框里的值选中了&#xff0c;但是文本框里没有值 这是 el-select组件代码,我这里是用了一个多选框&#xff0c;options的值是在后端查询的&#xff0c;form.we…

提取b站字幕(视频字幕、AI字幕)

提取b站字幕&#xff08;视频字幕、AI字幕&#xff09; 1. 打开视频 2. 按 F12 进行开发者界面 视频自己的紫米输入的是 json&#xff0c;如果是AI字幕则需要输入 ai_subtitle 3. 进入这个网址&#xff1a;https://www.dreamlyn.cn/bsrt

关于springboot一个接口请求后,主动取消后,后端是否还在跑

1、最近在思考一个问题&#xff0c;如果一个springboot的请求的接口比较耗时&#xff0c;中途中断该请求后&#xff0c;则后端服务是否会终止该线程的处理&#xff0c;于是写了一个demo RequestMapping(value "/test", method RequestMethod.GET)public BasicResul…

云消息队列 Confluent 版正式上线!

作者&#xff1a;阿里云消息队列 前言 在 2023 年杭州云栖大会上&#xff0c;Confluent 成为阿里云技术合作伙伴&#xff0c;在此基础上&#xff0c;双方展开了深度合作&#xff0c;并在今天&#xff08;3月1日&#xff09;正式上线“云消息队列 Confluent 版”。 通过将 Co…

keycloak18.0.0==本地源码启动

github下载源码&#xff0c; 版本18.0.0 java和maven的版本如下 E:\keycloak-18.0.0>java -version java version "21.0.1" 2023-10-17 LTS Java(TM) SE Runtime Environment (build 21.0.112-LTS-29) Java HotSpot(TM) 64-Bit Server VM (build 21.0.112-LTS-…

电脑e盘文件没删除但不见了怎么办?了解原因与找回方法

电脑E盘文件没删除但不见了怎么办&#xff1f;在数字化时代&#xff0c;电脑已成为我们生活和工作中不可或缺的工具。其中&#xff0c;各个硬盘分区更是承载着我们宝贵的数据。但有时&#xff0c;你可能会发现&#xff0c;存放在E盘中的某些文件明明没有删除&#xff0c;却突然…

前端面试拼图-原理源码

摘要&#xff1a;最近&#xff0c;看了下慕课2周刷完n道面试题&#xff0c;记录下... 1. JS内存泄漏如何检测&#xff1f;场景有哪些? 1.1 垃圾回收 GC 垃圾回收是一种自动管理内存的机制&#xff0c;它负责在运行时跟踪内存的分配和使用情况&#xff0c;并在不再需要的对象…

2024年【道路运输企业安全生产管理人员】复审考试及道路运输企业安全生产管理人员模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年道路运输企业安全生产管理人员复审考试为正在备考道路运输企业安全生产管理人员操作证的学员准备的理论考试专题&#xff0c;每个月更新的道路运输企业安全生产管理人员模拟考试题祝您顺利通过道路运输企业安全…

Flink:Temporal Table 的两种实现方式 Temporal Table DDL 和 Temporal Table Function

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

多线程相关面试题(2024大厂高频面试题系列)

1、聊一下并行和并发有什么区别&#xff1f; 并发是同一时间应对多件事情的能力&#xff0c;多个线程轮流使用一个或多个CPU 并行是同一时间动手做多件事情的能力&#xff0c;4核CPU同时执行4个线程 2、说一下线程和进程的区别&#xff1f; 进程是正在运行程序的实例&#xff…

【MySQL】not in遇上null的坑

今天遇到一个问题&#xff1a; 1、当 in 内的字段包含 null 的时候&#xff0c;正常过滤&#xff1b; 2、当 not in 内的字段包含 null 的时候&#xff0c;不能正常过滤&#xff0c;即使满足条件&#xff0c;最终结果也为 空。 测试如下&#xff1a; select * from emp e;当…

网络爬虫部分应掌握的重要知识点

目录 一、预备知识1、Web基本工作原理2、网络爬虫的Robots协议 二、爬取网页1、请求服务器并获取网页2、查看服务器端响应的状态码3、输出网页内容 三、使用BeautifulSoup定位网页元素1、首先需要导入BeautifulSoup库2、使用find/find_all函数查找所需的标签元素 四、获取元素的…

vue+uniapp实现图形验证码功能-插件(附源码)

一、需求背景 vueuniapp实现图形验证码功能-插件&#xff08;附源码&#xff09; 在登录系统时&#xff0c;除了密码登录&#xff0c;还需要提供验证码登录。 涉及验证码&#xff0c;为了安全&#xff0c;一般会加入图形验证码&#xff0c;然后再发短信验证码。 如图&#xff1…