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

news2024/10/25 6:25:05

目录

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

    @Configuration
    static class Config {
        @Bean
        public 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的BeanDefinition
    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
    // 把beanDefinition注册到beanFactory
    beanFactory.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的BeanDefinition
    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
    // 把beanDefinition注册到beanFactory
    beanFactory.registerBeanDefinition("config", beanDefinition);

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

    // 拿到beanFactory的后置处理器,这里有很多,其中一个就是internalConfigurationAnnotationProcessor
    Map<String, BeanFactoryPostProcessor> processorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
    // 执行后置处理器,当internalConfigurationAnnotationProcessor运行,就会把Bean1和Bean2的beanDefinition注册到beanFactory
    processorMap.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的BeanDefinition
    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
    // 把beanDefinition注册到beanFactory
    beanFactory.registerBeanDefinition("config", beanDefinition);

    // 给beanFactory添加一些内置的后处理器,注意只是把后处理器添加到了beanFactory而已,后处理器还不会起作用
    AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

    // 拿到beanFactory的后置处理器,这里有很多,其中一个就是internalConfigurationAnnotationProcessor
    Map<String, BeanFactoryPostProcessor> processorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
    // 执行后置处理器,当internalConfigurationAnnotationProcessor运行,就会把Bean1和Bean2的beanDefinition注册到beanFactory
    processorMap.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("---------- 分割线 ----------");
    // 获取Bean
    Bean1 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();
   
  	// 省略代码
  	...

    // 主动创建单例bean
    beanFactory.preInstantiateSingletons();

    System.out.println("---------- 分割线 ----------");
    // 获取Bean
    Bean1 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的BeanDefinition
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        // 把beanDefinition注册到beanFactory
        beanFactory.registerBeanDefinition("config", beanDefinition);

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

        // 拿到beanFactory的后置处理器,这里有很多,其中一个就是internalConfigurationAnnotationProcessor
        Map<String, BeanFactoryPostProcessor> processorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
        // 执行后置处理器,当internalConfigurationAnnotationProcessor运行,就会把Bean1和Bean2的beanDefinition注册到beanFactory
        processorMap.values().stream().forEach(processor -> {
            processor.postProcessBeanFactory(beanFactory);
        });

        // Bean 的后处理器,针对Bean的生命周期的各个阶段提供扩展,例如@Autowired
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().forEach(beanFactory::addBeanPostProcessor);

        // 查看beanFactory中beanDefinition的名字
//        for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
//            System.out.println(beanDefinitionName);
//        }

        // 主动创建单例bean
        beanFactory.preInstantiateSingletons();

        System.out.println("---------- 分割线 ----------");
        // 获取Bean
        Bean1 bean1 = beanFactory.getBean(Bean1.class);
        System.out.println(bean1.getInter());

    }

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

    }

    interface Inter {};

    static class Bean3 implements Inter{};

    static class Bean4 implements Inter{};

    static class Bean1 {
        public Bean1() {
            System.out.println("Bean1 构造方法");
        }

        @Autowired
        private Bean2 bean2;

        public Bean2 getBean2() {
            return bean2;
        }

        @Autowired
        private 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 构造方法");
    }

    @Autowired
    private Bean2 bean2;

    public Bean2 getBean2() {
        return bean2;
    }

    @Autowired
    private Inter bean3;

    public Inter getInter() {
        return bean3;
    }

}

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

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

static class Bean1 {
    public Bean1() {
        System.out.println("Bean1 构造方法");
    }

    @Autowired
    private 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 构造方法");
    }

    @Autowired
    private 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的BeanDefinition
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        // 把beanDefinition注册到beanFactory
        beanFactory.registerBeanDefinition("config", beanDefinition);

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

        // 拿到beanFactory的后置处理器,这里有很多,其中一个就是internalConfigurationAnnotationProcessor
        Map<String, BeanFactoryPostProcessor> processorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
        // 执行后置处理器,当internalConfigurationAnnotationProcessor运行,就会把Bean1和Bean2的beanDefinition注册到beanFactory
        processorMap.values().stream().forEach(processor -> {
            processor.postProcessBeanFactory(beanFactory);
        });

        // Bean 的后处理器,针对Bean的生命周期的各个阶段提供扩展,例如@Autowired
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().forEach(processor -> {
            System.out.println("processor:" + processor);
            beanFactory.addBeanPostProcessor(processor);
        });

        // 主动创建单例bean
        beanFactory.preInstantiateSingletons();

        System.out.println("---------- 分割线 ----------");
        // 获取Bean
        Bean1 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
   @Nullable
   protected Integer findOrder(Object obj) {
      Integer order = super.findOrder(obj);
      if (order != null) {
         return order;
      }
      return findOrderFromAnnotation(obj);
   }

   @Nullable
   private 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);
    }

    // 获取bean2
    Bean2 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);
    }

    // 获取bean2
    Bean2 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 {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public 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);
        }

        // 获取bean2
        Bean2 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
     */
    @Bean
    public ServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }

    /**
     * dispatcherServlet的bean
     *
     * @return
     */
    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    /**
     * 绑定tomcat服务器和dispatcherServlet
     *
     * @param dispatcherServlet
     * @return
     */
    @Bean
    public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
        return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
    }

    /**
     * 控制器bean
     *
     * @return
     */
    @Bean("/hello")
    public Controller controller1() {
        return new Controller() {
            @Override
            public 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.coloradmin.cn/o/1497650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Lazada本土店与跨境店区别,附店铺防关联攻略

许多新手商家在初入跨境电商时&#xff0c;对于平台账号类别并不清楚。Lazada是最大的东南亚在线购物平台之一&#xff0c;如果你的跨境目标正指向东南亚&#xff0c;那么Lazada一定是是你的首选平台。那么接下来让小编带大家认识Lazada本土店与跨境店的区别&#xff01; 一、本…

DNS——域名系统

TCP/IP提供了通过IP地址来连接到设备的功能&#xff0c;但对用户来讲&#xff0c;记住某台设备的IP地址是相当困难的&#xff0c;因此专门设计了一种字符串形式的主机命名机制&#xff0c;这些主机名与IP地址相对应。在IP地址与主机名之间需要有一种转换和查询机制&#xff0c;…

基于springboot的某大学外卖系统的实现(源码+论文)

文章目录 目录 文章目录 前言 一、功能设计 二、功能实现 1 后台登录 2管理员界面 3员工信息管理 4客户信息管理 三、库表设计 四、论文 前言 如今&#xff0c;信息化不断的高速发展&#xff0c;社会也跟着不断进步&#xff0c;现今的社会&#xff0c;各种工作都离不开信息化技…

python 截取字符串string.split

目录 作用语法只要第一个值获得第3个值遍历 作用 根据某个符号对数据进行截取 从而获得自己想要的内容 语法 使用’string.split’ 方法 对字符串’123/abc/BPYC’ 以 ‘/’ 进行截取 string "123/abc/BPYC" substring string.split("/") print(subs…

亚马逊运营要使用什么海外代理IP?

代理IP作为网络活动的有力工具&#xff0c;同时也是跨境电商的必备神器。亚马逊作为跨境电商的头部平台&#xff0c;吸引了大量的跨境电商玩家入驻&#xff0c;想要做好亚马逊&#xff0c;养号、测评都需要代理IP的帮助。那么应该使用什么代理IP呢&#xff1f;如何使用&#xf…

【投稿优惠|火热征稿】2024年计算机技术与自动化发展国际会议 (ICCTAD 2024)

2024年计算机技术与自动化发展国际会议 (ICCTAD 2024) 2024 International Conference on Computer Technology and Automation Development (ICCTAD 2024) 【会议简介】 2024年计算机技术与自动化发展国际会议( ICCTAD 2024)将在中国武汉盛大开幕&#xff01;这是一场在自动化…

【AI视野·今日Sound 声学论文速览 第五十二期】Tue, 5 Mar 2024

AI视野今日CS.Sound 声学论文速览 Tue, 5 Mar 2024 Totally 18 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers SA-SOT: Speaker-Aware Serialized Output Training for Multi-Talker ASR Authors Zhiyun Fan, Linhao Dong, Jun Zhang, Lu Lu, Zejun M…

【海贼王的数据航海:利用数据结构成为数据海洋的霸主】栈和队列

目录 1 -> 栈 1.1 -> 栈的概念及结构 1.2 -> 栈的实现 1.2.1 -> Stack.h 1.2.2 -> Stack.c 1.2.3 -> Test.c 2 -> 队列 2.1 -> 队列的概念及结构 2.2 -> 队列的实现 2.2.1 -> Queue.h 2.2.2 -> Queue.c 1 -> 栈 1.1 -> 栈的…

如何在Linux系统Docker部署Dashy并远程访问内网服务界面

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Python算法题集_N 皇后

Python算法题集_N 皇后 题51&#xff1a;N 皇后1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【规则遍历合理性回溯】2) 改进版一【线状态检测合理性回溯】3) 改进版二【单行矩阵回溯】 4. 最优算法5. 相关资源 本文为Python算法题集之一的代码…

威步安全技术保护铁路免受网络威胁

IoW的TrainCAS列车碰撞预警系统保护铁路列车免受网络攻击。TrainCAS系统内置的高端技术及其被非法利用的风险&#xff0c;让安全和知识产权保护成为公司的首要任务。TrainCAS软件及其轨道图通过CodeMeter AxProtector和Core API工具的结合得到保护&#xff0c;有效防止未授权使…

任务调度新境界:探秘ScheduledExecutorService的异步魔力

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 任务调度新境界&#xff1a;探秘ScheduledExecutorService的异步魔力 前言ScheduledExecutorService的基本概念基本概念&#xff1a;为何它是 Java 中任务调度的首选工具&#xff1a;基本用法&#xf…

Milvus 向量数据库实践 - 1

假定你已经安装了docker、docker-compose 环境 参考的文档如下&#xff1a; Milvus技术探究 - 知乎 MilvusClient() - Pymilvus v2.3.x for Milvus 一文带你入门向量数据库milvus 一、在docker上安装单机模式milvus数据库 1、 进入milvus官网&#xff1a; Install Milvus Stand…

【C++】string类的基础操作

&#x1f497;个人主页&#x1f497; ⭐个人专栏——C学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读 1. 基本概述 2. string类对象的常见构造 3. string类对象的容量操作 4. string类对象的访问及遍历操作 5. 迭代器 6.…

noetic ros配置因时机械夹爪的驱动

noetic ros配置因时机械夹爪的驱动文件 配置编译教程解决方案 配置编译教程 1.inspire_robot 包支持因时机器人公司的机械夹爪在ROS平台上的使用&#xff0c;我们在ros noetic环境下进行了测试。 2.为了使程序能够正常运行&#xff0c;需要执行以下环境配置操作&#xff1a;&a…

从一个问题开始聊聊clickhouse的物化视图

【问题】 今天有A问我一个问题&#xff0c;我明明创建了一个物化视图&#xff0c;源表是有数据的&#xff0c;为什么查询物化视图就没有数据&#xff1f; 创建物化视图的SQL示意如下&#xff1a; CREATE MATERIALIZED VIEW schema1.test_mvon cluster clusterNameTO schema1…

玩转安卓之配置gradle-8.2.1

概述&#xff1a;看了一下&#xff0c;由于gradle是国外的&#xff0c;所以下载速度很慢&#xff0c;这个老师又是很菜的类型&#xff0c;同学又不会&#xff0c;于是曹某就写这一篇文章&#xff0c;教大家学会简单的为安卓配置gradle-8.2.1。 第一步&#xff1a;下载gradle-8…

【问题解决】| 关于vscode调试python文件 报错 且直接运行正常的诡异情况记录

关于python的debug报错&#xff0c;其实很奇怪 首先&#xff0c;对于工作区代码&#xff0c;我们可以通过CtrlShiftP 来切换Python解释器 这样的话&#xff0c;工作区的代码就不会报import error 而且这样的话是可以运行跑通的&#xff0c;但最抽象的一集来了&#xff0c;这…

JavaScript 作用域详解:如何影响变量生命周期

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

总结Redis的原理

一、为什么要使用Redis 缓解数据库访问压力mysql读请求进行磁盘I/O速度慢&#xff0c;给数据库加Redis缓存&#xff08;参考CPU缓存&#xff09;&#xff0c;将数据缓存在内存中&#xff0c;省略了I/O操作 二、Redis数据管理 2.1 redis数据的删除 定时删除惰性删除内存淘汰…