• Spring原理学习(一)容器接口与容器实现


    感谢黑马满老师的讲解,通俗易懂,很适合去学习。

    视频地址:Spring高级49讲-导学_哔哩哔哩_bilibili

    一、容器接口 

    学习目标:

    1. BeanFactory能做哪些事
    2. ApplicationContext有哪些扩展功能
    3. 事件解耦

    1、BeanFactory 与 ApplicationContext 的区别

    通过这个示例结合 debug 查看 ApplicationContext 对象的内部结构

    1. @SpringBootApplication
    2. public class A01 {
    3. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
    4. ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
    5. context.getBean("aaa");
    6. System.out.println(context);
    7. }
    8. }

    BeanFactory 接口典型功能:getBean方法

    1. //AbstractApplicationContext类 
    2. public Object getBean(String name) throws BeansException {
    3. this.assertBeanFactoryActive();
    4. //内部调用了beanFactory的getBean方法
    5. return this.getBeanFactory().getBean(name);
    6. }

    debug查看context内部结构

     ConfigurableApplicationContext继承关系: 

    总结:到底什么是 BeanFactory

    • 它是 ApplicationContext 的父接口

    • 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory

    2、BeanFactory 能干啥

    BeanFactory接口:

    1. public interface BeanFactory {
    2. String FACTORY_BEAN_PREFIX = "&";
    3. Object getBean(String var1) throws BeansException;
    4. T getBean(String var1, Class var2) throws BeansException;
    5. Object getBean(String var1, Object... var2) throws BeansException;
    6. T getBean(Class var1) throws BeansException;
    7. T getBean(Class var1, Object... var2) throws BeansException;
    8. ObjectProvider getBeanProvider(Class var1);
    9. ObjectProvider getBeanProvider(ResolvableType var1);
    10. boolean containsBean(String var1);
    11. boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
    12. boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
    13. boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
    14. boolean isTypeMatch(String var1, Class var2) throws NoSuchBeanDefinitionException;
    15. @Nullable
    16. Class getType(String var1) throws NoSuchBeanDefinitionException;
    17. @Nullable
    18. Class getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;
    19. String[] getAliases(String var1);
    20. }

    表面上只有 getBean,实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供。 

    查看它的实现类

     我们知道BeanFactory能管理所有的bean,最为熟悉的就是单例bean,其中DefaultSingletonBeanRegistry就管理了所有的单例对象。

    通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean

    1. @SpringBootApplication
    2. public class A01 {
    3. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
    4. ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
    5. Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
    6. singletonObjects.setAccessible(true);
    7. //获取beanFactory
    8. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    9. //获取beanFactory对象的singletonObjects属性
    10. Map map = (Map) singletonObjects.get(beanFactory);
    11. //由于对象太多,我们只打印名称以component开头的对象
    12. map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
    13. .forEach( e -> {
    14. System.out.println(e.getKey() + "=" + e.getValue());
    15. });
    16. }
    17. }
    1. @Component
    2. public class Component1 {
    3. }
    4. @Component
    5. public class Component2 {
    6. }

    结果:

    1. component1=com.itheima.a01.Component1@16eedaa6
    2. component2=com.itheima.a01.Component2@28501a4b 

    3、ApplicationContext 比 BeanFactory 多点啥

    查看类图,可以发现ApplicationContext多实现了四个接口,也就可以回答下面的问题了。

    ApplicationContext 比 BeanFactory 多点啥:

    • ApplicationContext 组合并扩展了 BeanFactory 的功能

    • 国际化、通配符方式获取一组 Resource 资源、整合 Environment 环境、事件发布与监听

    1)MessageSource接口:国际化

    1. @SpringBootApplication
    2. public class A01 {
    3. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
    4. ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
    5. System.out.println(context.getMessage("hi", null, Locale.CHINA));
    6. System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
    7. System.out.println(context.getMessage("hi", null, Locale.JAPANESE));
    8. }

    国际化文件均在 src/resources 目录下

    messages.properties(空),空的 messages.properties 也必须存在。

    messages_en.properties

    hi=Hello

    messages_ja.properties

    hi=こんにちは

    messages_zh.propertieshi=你好

    hi=你好

    结果:

    1. 你好
    2. Hello
    3. こんにちは

    2)ResourcePatternResolver接口:通配符方式获取一组 Resource 资源

    1. @SpringBootApplication
    2. public class A01 {
    3. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
    4. ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
    5. //Resource[] resources = context.getResources("classpath:application.properties");
    6. //classpath:META-INF/spring.factories:在类路径找,如果在jar包中是找不到的
    7. //classpath*:META-INF/spring.factories:这样就可以
    8. Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
    9. for (Resource resource : resources) {
    10. System.out.println(resource);
    11. }
    12. }

    结果:第一个是自己模块的,剩下的是导入的jar中的

    1. URL [file:/E:/spring%e6%ba%90%e7%a0%81/%e4%bb%a3%e7%a0%81/show/target/classes/META-INF/spring.factories]
    2. URL [jar:file:/D:/developer_tools/maven/repository/org/springframework/boot/spring-boot/2.5.5/spring-boot-2.5.5.jar!/META-INF/spring.factories]
    3. URL [jar:file:/D:/developer_tools/maven/repository/org/springframework/boot/spring-boot-autoconfigure/2.5.5/spring-boot-autoconfigure-2.5.5.jar!/META-INF/spring.factories]
    4. URL [jar:file:/D:/developer_tools/maven/repository/org/springframework/spring-beans/5.3.10/spring-beans-5.3.10.jar!/META-INF/spring.factories]
    5. URL [jar:file:/D:/developer_tools/maven/repository/org/mybatis/spring/boot/mybatis-spring-boot-autoconfigure/2.2.0/mybatis-spring-boot-autoconfigure-2.2.0.jar!/META-INF/spring.factories]
    6. URL [jar:file:/D:/developer_tools/maven/repository/com/alibaba/druid-spring-boot-starter/1.2.8/druid-spring-boot-starter-1.2.8.jar!/META-INF/spring.factories]
    7. URL [jar:file:/D:/developer_tools/maven/repository/org/springframework/spring-test/5.3.10/spring-test-5.3.10.jar!/META-INF/spring.factories]

     3)EnvironmentCapable接口:整合 Environment 环境

    1. @SpringBootApplication
    2. public class A01 {
    3. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
    4. ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
    5. //不区分大小写
    6. System.out.println(context.getEnvironment().getProperty("java_home"));
    7. System.out.println(context.getEnvironment().getProperty("server.port"));
    8. }

    结果:

    1. D:\developer_tools\jdk\1.8.0_131
    2. 8082

    4)ApplicationEventPublisher接口:事件发布与监听

    Spring中任何一个组件都可以作为监听器,这里用Component2

    事件类 

    1. public class UserRegisteredEvent extends ApplicationEvent {
    2. public UserRegisteredEvent(Object source) {
    3. super(source);
    4. }
    5. }

    发送事件 

    1. @SpringBootApplication
    2. public class A01 {
    3. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
    4. ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
    5. //发送事件
    6. context.publishEvent(new UserRegisteredEvent(context));
    7. }

    监听器 

    1. @Component
    2. public class Component2 {
    3. private static final Logger log = LoggerFactory.getLogger(Component2.class);
    4. //监听器,方法名任意,参数要求:与发送的事件类型一致
    5. @EventListener
    6. public void aaa(UserRegisteredEvent event) {
    7. log.debug("{}", event);
    8. }
    9. }

    结果

    1. [DEBUG] 10:00:42.236 [main] com.itheima.a01.Component2
    2. -com.itheima.a01.UserRegisteredEvent
    3. [source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@747edf66,
    4. started on Tue Nov 01 10:00:39 CST 2022]

     4、事件解耦

    完成用户注册与发送短信之间的解耦

    发送事件

    1. @Component
    2. public class Component1 {
    3. private static final Logger log = LoggerFactory.getLogger(Component1.class);
    4. //具备发送事件的功能
    5. @Autowired
    6. private ApplicationEventPublisher context;
    7. public void register() {
    8. log.debug("用户注册");
    9. //发布事件
    10. context.publishEvent(new UserRegisteredEvent(this));
    11. }
    12. }

    监听事件 

    1. @Component
    2. public class Component2 {
    3. private static final Logger log = LoggerFactory.getLogger(Component2.class);
    4. //监听器,方法名任意,参数要求:与发送的事件类型一致
    5. @EventListener
    6. public void aaa(UserRegisteredEvent event) {
    7. log.debug("{}", event);
    8. log.debug("发送短信");
    9. }
    10. }

     从容器获取Component1调用其方法

    1. @SpringBootApplication
    2. public class A01 {
    3. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
    4. ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
    5. context.getBean(Component1.class).register();
    6. }

    二、容器实现

    学习目标:

    1. BeanFactory实现的特点
    2. ApplicationContext的常见实现和用法
    3. 内嵌容器,注册DispatcherServlet

    Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,这里出于怀旧的原因,把它们都列出来,供大家参考

    • DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转依赖注入功能,都是它来实现

    • ClassPathXmlApplicationContext,从类路径查找 XML 配置文件,创建容器(旧)

    • FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)

    • XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)

    • AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)

    • AnnotationConfigApplicationContext,Spring boot 中非 web 环境容器(新)

    • AnnotationConfigServletWebServerApplicationContext,Spring boot 中 servlet web 环境容器(新)

    • AnnotationConfigReactiveWebServerApplicationContext,Spring boot 中 reactive web 环境容器(新)

    另外要注意的是,后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现,但它们是组合了 DefaultListableBeanFactory 的功能,并非继承而来

    1、DefaultListableBeanFactory演示

    原始的DefaultListableBeanFactory

    1. public class TestBeanFactory {
    2. public static void main(String[] args) {
    3. DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    4. //bean的定义(class,scope,初始化,销毁)
    5. AbstractBeanDefinition beanDefinition
    6. = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
    7. //注册beandefinition对象
    8. //参数一:名字 ,参数二:具体bean
    9. beanFactory.registerBeanDefinition("config", beanDefinition);
    10. //输出容器中bean的名字
    11. for (String name : beanFactory.getBeanDefinitionNames()) {
    12. System.out.println(name);
    13. }
    14. }
    15. @Configuration
    16. static class Config {
    17. @Bean
    18. public Bean1 bean1(){
    19. return new Bean1();
    20. }
    21. @Bean
    22. public Bean2 bean2(){
    23. return new Bean2();
    24. }
    25. }
    26. static class Bean1 {
    27. private static final Logger log = LoggerFactory.getLogger(Bean1.class);
    28. public Bean1() {
    29. log.debug("构造 Bean1()");
    30. }
    31. @Autowired
    32. private Bean2 bean2;
    33. public Bean2 getBean2() {
    34. return bean2;
    35. }
    36. }
    37. static class Bean2 {
    38. private static final Logger log = LoggerFactory.getLogger(Bean2.class);
    39. public Bean2() {
    40. log.debug("构造 Bean2()");
    41. }
    42. }
    43. }

    结果:容器中只有一个config对象,原始的beanFactory并没有解析注解的能力

    config
    

    1)给BeanFactory添加一些常用的后处理器

    1. public class TestBeanFactory {
    2. public static void main(String[] args) {
    3. DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    4. //bean的定义(class,scope,初始化,销毁)
    5. AbstractBeanDefinition beanDefinition
    6. = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
    7. //注册beandefinition对象
    8. //参数一:名字 ,参数二:具体bean
    9. beanFactory.registerBeanDefinition("config", beanDefinition);
    10. //给BeanFactory添加一些常用的后处理器,仅仅是注册后处理器
    11. AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
    12. //输出容器中bean的名字
    13. for (String name : beanFactory.getBeanDefinitionNames()) {
    14. System.out.println(name);
    15. }
    16. }
    17. }

    结果:仅仅是注册了后处理器,还没有执行,此时并不具备解析注解的能力

    1. config
    2. org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    3. org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    4. org.springframework.context.annotation.internalCommonAnnotationProcessor
    5. org.springframework.context.event.internalEventListenerProcessor
    6. org.springframework.context.event.internalEventListenerFactory

    2)执行BeanFactory后置处理器

    1. public class TestBeanFactory {
    2. public static void main(String[] args) {
    3. DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    4. //bean的定义(class,scope,初始化,销毁)
    5. AbstractBeanDefinition beanDefinition
    6. = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
    7. //注册beandefinition对象
    8. //参数一:名字 ,参数二:具体bean
    9. beanFactory.registerBeanDefinition("config", beanDefinition);
    10. //给BeanFactory添加一些常用的后处理器,仅仅是注册后处理器
    11. AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
    12. //执行BeanFactory后置处理器
    13. beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
    14. .stream().forEach(beanFactoryPostProcessor -> {
    15. beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
    16. });
    17. for (String name : beanFactory.getBeanDefinitionNames()) {
    18. System.out.println(name);
    19. }
    20. System.out.println(beanFactory.getBean(Bean1.class).getBean2());
    21. }
    22. }

    结果: bean1,bean2成功注册进容器,但是bean1获取不到bean2对象,结果为null。

    并且可以发现是懒加载,第一次用到的时候才创建对象实例

    1. config
    2. org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    3. org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    4. org.springframework.context.annotation.internalCommonAnnotationProcessor
    5. org.springframework.context.event.internalEventListenerProcessor
    6. org.springframework.context.event.internalEventListenerFactory
    7. bean1
    8. bean2
    9. [DEBUG] 15:34:11.550 [main] c.itheima.a02.TestBeanFactory$Bean1 - 构造 Bean1()
    10. null

     原因:输出刚才执行的后处理器,发现没有执行@Autowire后处理器

    1. System.out.println(beanFactory.getBean(Bean1.class).getBean2());
    2. [org.springframework.context.annotation.ConfigurationClassPostProcessor@24313fcc,
    3. org.springframework.context.event.EventListenerMethodProcessor@7d20d0b]

     3)执行Bean后处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowire,@Resource

    1. public class TestBeanFactory {
    2. public static void main(String[] args) {
    3. DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    4. //bean的定义(class,scope,初始化,销毁)
    5. AbstractBeanDefinition beanDefinition
    6. = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
    7. //注册beandefinition对象
    8. //参数一:名字 ,参数二:具体bean
    9. beanFactory.registerBeanDefinition("config", beanDefinition);
    10. //给BeanFactory添加一些常用的后处理器,仅仅是注册后处理器
    11. AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
    12. //执行BeanFactory后置处理器
    13. beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
    14. .stream().forEach(beanFactoryPostProcessor -> {
    15. beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
    16. });
    17. //执行Bean后处理器
    18. beanFactory.getBeansOfType(BeanPostProcessor.class).values()
    19. .forEach(beanPostProcessor -> {
    20. //查看Bean后处理器顺序
    21. System.out.println(">>>>" + beanPostProcessor);
    22. //添加Bean后处理器
    23. beanFactory.addBeanPostProcessor(beanPostProcessor);
    24. });
    25. System.out.println(beanFactory.getBean(Bean1.class).getBean2());
    26. }
    27. }

    结果:

    1. >>>>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@76908cc0
    2. >>>>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@2473d930
    3. [DEBUG] 15:50:59.132 [main] c.itheima.a02.TestBeanFactory$Bean1 - 构造 Bean1()
    4. [DEBUG] 15:50:59.149 [main] c.itheima.a02.TestBeanFactory$Bean2 - 构造 Bean2()
    5. com.itheima.a02.TestBeanFactory$Bean2@188715b5

    我们可以在获取bean前调用下面的方法准备好单例,就不用到使用时才去创建实例。 

    1. beanFactory.preInstantiateSingletons(); //准备好所有单例
    2. System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");//为了区分是在获取前创建的

    结果:

    1. >>>>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@76908cc0
    2. >>>>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@2473d930
    3. [DEBUG] 15:56:20.124 [main] c.itheima.a02.TestBeanFactory$Bean1 - 构造 Bean1()
    4. [DEBUG] 15:56:20.138 [main] c.itheima.a02.TestBeanFactory$Bean2 - 构造 Bean2()
    5. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    6. com.itheima.a02.TestBeanFactory$Bean2@43195e57

    此时增加bean3,bean4

    1. @Configuration
    2. static class Config {
    3. @Bean
    4. public Bean1 bean1(){
    5. return new Bean1();
    6. }
    7. @Bean
    8. public Bean2 bean2(){
    9. return new Bean2();
    10. }
    11. @Bean
    12. public Bean3 bean3() {
    13. return new Bean3();
    14. }
    15. @Bean
    16. public Bean4 bean4() {
    17. return new Bean4();
    18. }
    19. }
    20. static class Bean1 {
    21. private static final Logger log = LoggerFactory.getLogger(Bean1.class);
    22. public Bean1() {
    23. log.debug("构造 Bean1()");
    24. }
    25. @Autowired
    26. private Bean2 bean2;
    27. public Bean2 getBean2() {
    28. return bean2;
    29. }
    30. }
    31. static class Bean2 {
    32. private static final Logger log = LoggerFactory.getLogger(Bean2.class);
    33. public Bean2() {
    34. log.debug("构造 Bean2()");
    35. }
    36. }
    37. interface Inter {
    38. }
    39. static class Bean3 implements Inter {
    40. }
    41. static class Bean4 implements Inter {
    42. }

     关于@Autowire的一些点,这里说明一下

    我要在bean1里面这样注入,能成功吗?注入失败,容器中bean3,bean4都匹配

    1. @Autowired
    2. private Inter inter;

     但是这样就可以,当有多个bean匹配时,通过名字进一步匹配

    1. @Autowired
    2. private Inter bean3

    可以发现我刚刚打印了Bean后处理器的顺序,这个有什么用?看这个例子,请问注入的是bean3还是bean4呢

    注入bean3,与后处理器顺序有关,先解析Autowire

    1. @Autowired
    2. @Resource(name = "bean4")
    3. private Inter bean3;

    设置Bean后处理器顺序,通过优先级排序。

    1. //Bean后处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowire,@Resource
    2. beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
    3. .sorted(beanFactory.getDependencyComparator()).
    4. forEach(beanPostProcessor -> {
    5. //添加Bean后处理器
    6. beanFactory.addBeanPostProcessor(beanPostProcessor);
    7. });

    查看源码,它们都实现了 Ordered 接口,设置Ordered大小进行排序,ordered小的优先级高

    1. //AutowiredAnnotationBeanPostProcessor类
    2. private int order = 2147483645;
    3. //CommonAnnotationBeanPostProcessor类
    4. public CommonAnnotationBeanPostProcessor() {
    5. this.setOrder(2147483644);
    6. this.setInitAnnotationType(PostConstruct.class);
    7. this.setDestroyAnnotationType(PreDestroy.class);
    8. this.ignoreResourceType("javax.xml.ws.WebServiceContext");
    9. }
    10. public interface Ordered {
    11. int HIGHEST_PRECEDENCE = -2147483648;
    12. int LOWEST_PRECEDENCE = 2147483647;
    13. int getOrder();
    14. }

    beanFactory.getDependencyComparator()是什么?

    查看源码:可以发现

    1. 实现了 PriorityOrdered 接口的优先级最高
    2. 实现了 Ordered 接口与加了 @Order 注解的平级, 按数字升序
    3. 其它的排在最后
    1. public class AnnotationAwareOrderComparator extends OrderComparator {
    2. }
    3. public class OrderComparator implements Comparator {
    4. public int compare(@Nullable Object o1, @Nullable Object o2) {
    5. return this.doCompare(o1, o2, (OrderComparator.OrderSourceProvider)null);
    6. }
    7. private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderComparator.OrderSourceProvider sourceProvider) {
    8. boolean p1 = o1 instanceof PriorityOrdered;
    9. boolean p2 = o2 instanceof PriorityOrdered;
    10. if (p1 && !p2) {
    11. return -1;
    12. } else if (p2 && !p1) {
    13. return 1;
    14. } else {
    15. int i1 = this.getOrder(o1, sourceProvider);
    16. int i2 = this.getOrder(o2, sourceProvider);
    17. return Integer.compare(i1, i2);
    18. }
    19. }
    20. }
    21.  总结

      beanFactory 不会做的事:

      1. 不会主动调用 BeanFactory 后处理器
      2. 不会主动添加 Bean 后处理器
      3. 不会主动初始化单例
      4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }

      bean 后处理器会有排序的逻辑

      2、常见 ApplicationContext 实现

      1)ClassPathXmlApplicationContext

      从类路径查找 XML 配置文件,创建容器

      1. public class A02Application {
      2. public static void main(String[] args) {
      3. testClassPathXmlApplicationContext();
      4. }
      5. // 较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
      6. private static void testClassPathXmlApplicationContext() {
      7. ClassPathXmlApplicationContext context =
      8. new ClassPathXmlApplicationContext("b01.xml");
      9. for (String name : context.getBeanDefinitionNames()) {
      10. System.out.println(name);
      11. }
      12. System.out.println(context.getBean(Bean2.class).getBean1());
      13. }
      14. static class Bean1 {
      15. }
      16. static class Bean2 {
      17. private Bean1 bean1;
      18. public void setBean1(Bean1 bean1) {
      19. this.bean1 = bean1;
      20. }
      21. public Bean1 getBean1() {
      22. return bean1;
      23. }
      24. }
      25. }

      b01.xml 

      1. "1.0" encoding="UTF-8"?>
      2. <beans xmlns="http://www.springframework.org/schema/beans"
      3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      4. xmlns:context="http://www.springframework.org/schema/context"
      5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
      6. <bean id="bean1" class="com.itheima.a02.A02Application.Bean1"/>
      7. <bean id="bean2" class="com.itheima.a02.A02Application.Bean2">
      8. <property name="bean1" ref="bean1"/>
      9. bean>
      10. beans>

      结果:

      1. bean1
      2. bean2
      3. com.itheima.a02.A02Application$Bean1@4b168fa9

       我们在Spring基础中都学过这个标签,作用是让系统能够识别相应的注解

      <context:annotation-config/>

      现在我们看看到底是为什么呢?

      加上这个标签,打印容器中的bean,发现多了几个后处理器,用来解析@Configuration,@Autowire,@Resourse等注解

      1. bean1
      2. bean2
      3. org.springframework.context.annotation.internalConfigurationAnnotationProcessor
      4. org.springframework.context.annotation.internalAutowiredAnnotationProcessor
      5. org.springframework.context.annotation.internalCommonAnnotationProcessor
      6. org.springframework.context.event.internalEventListenerProcessor
      7. org.springframework.context.event.internalEventListenerFactory

      2)FileSystemXmlApplicationContext

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

      1. public class A02Application {
      2. public static void main(String[] args) {
      3. testFileSystemXmlApplicationContext();
      4. }
      5. private static void testFileSystemXmlApplicationContext() {
      6. FileSystemXmlApplicationContext context =
      7. new FileSystemXmlApplicationContext("src\\main\\resources\\b01.xml");
      8. //相对路径(模块开始):src\main\resources\b01.xml
      9. //绝对路径:E:\spring源码\代码\show\src\main\resources\b01.xml
      10. for (String name : context.getBeanDefinitionNames()) {
      11. System.out.println(name);
      12. }
      13. System.out.println(context.getBean(Bean2.class).getBean1());
      14. }
      15. }

      结果:

      1. bean1
      2. bean2
      3. com.itheima.a02.A02Application$Bean1@4b168fa9

       测试的时候遇到了一个报错,如下

      1. Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException:
      2. IOException parsing XML document from file
      3. [D:\workspace_idea2\spring_yuanli\src\main\resources\b01.xml];
      4. nested exception is java.io.FileNotFoundException: src\main\resources\b01.xml

      原因:这个项目是导进去的,与默认的工作目录不一样导致的。

      处理方法:Edit Configurations -> 修改工作目录

      原理:前面说过,ApplicationContext内部还是需要beanFactory的支持

      比DefaultListableBeanFactory多的功能,加载bean的定义信息

      1. public class A02Application {
      2. public static void main(String[] args) {
      3. DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
      4. System.out.println("读取之前...");
      5. for (String name : beanFactory.getBeanDefinitionNames()) {
      6. System.out.println(name);
      7. }
      8. System.out.println("读取之后...");
      9. XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
      10. reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
      11. //reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\b01.xml"));
      12. for (String name : beanFactory.getBeanDefinitionNames()) {
      13. System.out.println(name);
      14. }
      15. }

      结果:

      1. 读取之前...
      2. 读取之后...
      3. bean1
      4. bean2
      5. org.springframework.context.annotation.internalConfigurationAnnotationProcessor
      6. org.springframework.context.annotation.internalAutowiredAnnotationProcessor
      7. org.springframework.context.annotation.internalCommonAnnotationProcessor
      8. org.springframework.context.event.internalEventListenerProcessor
      9. org.springframework.context.event.internalEventListenerFactory

      3)AnnotationConfigApplicationContext

      Spring boot 中非 web 环境容器,较为经典的容器, 基于 java 配置类来创建

      1. private static void testAnnotationConfigApplicationContext() {
      2. AnnotationConfigApplicationContext context =
      3. new AnnotationConfigApplicationContext(Config.class);
      4. for (String name : context.getBeanDefinitionNames()) {
      5. System.out.println(name);
      6. }
      7. System.out.println(context.getBean(Bean2.class).getBean1());
      8. }
      9. @Configuration
      10. static class Config {
      11. @Bean
      12. public Bean1 bean1() {
      13. return new Bean1();
      14. }
      15. @Bean
      16. public Bean2 bean2(Bean1 bean1) {
      17. Bean2 bean2 = new Bean2();
      18. bean2.setBean1(bean1);
      19. return bean2;
      20. }
      21. }

      结果:发现该容器已经配置了解析@Configuration,@Autowire,@Resourse等注解的后处理器

      1. org.springframework.context.annotation.internalConfigurationAnnotationProcessor
      2. org.springframework.context.annotation.internalAutowiredAnnotationProcessor
      3. org.springframework.context.annotation.internalCommonAnnotationProcessor
      4. org.springframework.context.event.internalEventListenerProcessor
      5. org.springframework.context.event.internalEventListenerFactory
      6. a02Application.Config
      7. bean1
      8. bean2
      9. com.itheima.a02.A02Application$Bean1@6025e1b6

      4)AnnotationConfigServletWebServerApplicationContext

      Spring boot 中 servlet web 环境容器,基于 java 配置类来创建, 用于 web 环境

      1. private static void testAnnotationConfigServletWebServerApplicationContext() {
      2. AnnotationConfigServletWebServerApplicationContext context =
      3. new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
      4. for (String name : context.getBeanDefinitionNames()) {
      5. System.out.println(name);
      6. }
      7. }
      8. @Configuration
      9. static class WebConfig {
      10. //内嵌一个基于servlet的Web容器,即提供web服务器的组件
      11. @Bean
      12. public ServletWebServerFactory servletWebServerFactory(){
      13. return new TomcatServletWebServerFactory();
      14. }
      15. //前端控制器
      16. @Bean
      17. public DispatcherServlet dispatcherServlet() {
      18. return new DispatcherServlet();
      19. }
      20. //Servlet是运行在Tomcat服务器上的,需要将他们两个关联
      21. //将DispatcherServlet注册到Tomcat服务器
      22. @Bean
      23. public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
      24. //让除了jsp的所有请求都先经过DispatcherServlet
      25. return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
      26. }
      27. //以 /开头可以将它的名称作为访问路径
      28. @Bean("/hello")
      29. //注意:是org.springframework.web.servlet.mvc.Controller接口,而不是注解
      30. public Controller controller1() {
      31. return new Controller() {
      32. @Override
      33. public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
      34. response.getWriter().print("hello");
      35. return null;
      36. }
      37. };
      38. }
      39. }

       结果:发现该容器已经配置了解析@Configuration,@Autowire,@Resourse注解的后处理器,并且我们可以通过 localhost:8080/hello进行访问

      1. org.springframework.context.annotation.internalConfigurationAnnotationProcessor
      2. org.springframework.context.annotation.internalAutowiredAnnotationProcessor
      3. org.springframework.context.annotation.internalCommonAnnotationProcessor
      4. org.springframework.context.event.internalEventListenerProcessor
      5. org.springframework.context.event.internalEventListenerFactory
      6. a02Application.WebConfig
      7. servletWebServerFactory
      8. dispatcherServlet
      9. registrationBean
      10. /hello

      总结

      学到了什么?

      • 常见的 ApplicationContext 容器实现
      • 内嵌容器、DispatcherServlet 的创建方法、作用
    22. 相关阅读:
      选择题汇总8-9(括号里填的答案都是对的,不用管下面那个答案正确与错误,因为作者懒得删了)
      休闲卤味的商业江湖里,周黑鸭的巨变与出路
      一道小学一年级数学题,国产大模型全军覆没
      微信小程序 25 npm模块和PubSubJS实现页面通讯
      RK356x U-Boot研究所(开发篇)5.1 启动SATA硬盘中的固件
      计算机考研408高分复习规划-如何复习408才能得高分
      Python中RotatingFileHandler、TimedRotatingFileHandler函数用法
      对自动化测试的一些见解
      Win11安装最新Android studio时闪退
      Android OpenGLES3.0 开发 :光照基础
    23. 原文地址:https://blog.csdn.net/qq_51409098/article/details/127627060