栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

spring原理-springmvc原理-springboot原理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

spring原理-springmvc原理-springboot原理

Spring高级

一、容器与bean a01 、容器接口(BeanFactory与ApplicationContext的区别与联系) ①、到底什么是 BeanFactory?

BeanFactory接口:

首先我们先了解一下BeanFactory接口:

我们写一段springboot项目的启动类如下:

@SpringBootApplication
public class A01Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
    }
}
  • 上述代码中 SpringApplication.run(A01Application.class, args)方法返回一个容器对象,我们使用idea查看这个容器类的继承关系(快捷键:Ctrl + alt + u)如下:

  • 我们发现ConfigurableApplicationContext继承与ApplicationContext

  • ApplicationContext继承与BeanFactory

到底什么是BeanFactory?

  • 它是ApplicationContext的父接口

  • 它才是 Spring 的核心容器,主要的 ApplicationContext 实现都 [组合]了它的功能

    在web环境下,我们打印ConfigurableApplicationContext的class对象发现它其实是AnnotationConfigServletWebServerApplicationContext类型

我们接着查看AnnotationConfigServletWebServerApplicationContext类的继承图,
按图所示:AnnotationConfigServletWebServerApplicationContext又间接继承了GenericApplicationContext

我们跟进GenericApplicationContext源码:发现beanFactory是该类的成员变量。

②、BeanFactory的功能

BeanFactory接口中的方法:

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

我们可以查看springboot默认的ConfigurableApplicationContext类中的BeanFactory的实际类型

   public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 查看实际类型
        // class org.springframework.beans.factory.support.DefaultListableBeanFactory
        System.out.println(beanFactory.getClass());
    }

从打印结果可以了解到实际类型为DefaultListableBeanFactory,所以这里以BeanFactory的一个实现类DefaultListableBeanFactory作为出发点,进行分析。

这里我们暂且不细看DefaultListableBeanFactory,先看DefaultListableBeanFactory的父类DefaultSingletonBeanRegistry,先选中它,然后双击,可以跳转到对应的源码,可以看到有个私有的成员变量singletonObjects

知道了这些关系,我们可以利用反射获取singletonObjects进行查看代码如下:

  • 首先我们人为往spring容器中添加两个bean
//bean1
package com.colin.a01;
import org.springframework.stereotype.Component;
@Component
public class Component1 {

}
//bean2
package com.colin.a01;
import org.springframework.stereotype.Component;
@Component
public class Component2 {

}
  • 利用反射查看我们定义的bean是否在singletonObjects中
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
            ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
            singletonObjects.setAccessible(true);
            Map map = (Map)  singletonObjects.get(beanFactory);
            System.out.println(beanFactory.getClass());
            map.entrySet().stream().filter(entry -> entry.getKey().startsWith("component")).forEach(System.out::println);
        }

结果如下:说明单例池在BeanFactory。

③、ApplicationContext 比 BeanFactory 多点啥?
  • 多实现了四个接口:

    • MessageSource: 国际化功能,支持多种语言
    • ResourcePatternResolver: 通配符匹配资源路径
    • EnvironmentCapable: 环境信息,系统环境变量,.properties、.application.yml等配置文件中的值
    • ApplicationEventPublisher: 发布事件对象

    蓝色框标识:

⑴、演示MessageSource: 国际化功能
  • 我们在resource下建立三个语言翻译的properties

    其实还应该有一个默认语言环境的properties名字为messages.properties当所有语言环境都不匹配时它就生效

    图省事没有创建,真实开发一定要创建


    内容如下:

  • 我们编写java代码从测试: 其中GenericApplicationContext是干净的ApplicationContext 什么功能都没有

    我们编程注入一个bean ,bean的类型为MessageSource

    调用GenericApplicationContext的refresh方法加载或刷新配置

    使用Locale指定语言环境

    说明: 真实开发,语言环境由前台浏览器携带请求头传递,这里只是演示,真实开发是使用@Bean注入messageSource只是演示

 public static void main(String[] args) {
        //干净的ApplicationContext 什么功能都没有
        GenericApplicationContext context = new GenericApplicationContext();

        //编程注入一个bean bean的类型为MessageSource
        context.registerBean("messageSource", MessageSource.class, () -> {
            ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
            ms.setDefaultEncoding("utf-8");
            ms.setBasename("messages");
            return ms;
        });

        //加载容器
        context.refresh();

        //测试
        System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
        System.out.println(context.getMessage("hi", null, Locale.CHINESE));
        System.out.println(context.getMessage("hi", null, Locale.JAPANESE));
    }
  • 结果如下: 我们发现由于语言环境的不同spring帮我们返回来不同的键值。实现了国际化的功能

注意:

  • ApplicationContext 中 MessageSource bean 的名字固定为 messageSource
  • 使用 SpringBoot 时,国际化文件名固定为 messages
  • 空的 messages.properties 也必须存在
⑵、演示ResourcePatternResolver: 通配符匹配资源路径
  • 获取类路径下的messages开头的配置文件
   //测试ResourcePatternResolver
    Resource[] resources = context.getResources("classpath:messages*.properties");
    for (Resource resource : resources) {
        System.out.println(resource);
    }

运行结果:

  • 获取spring相关jar包中的spring.factories配置文件
 	//测试获取spring相关jar包中的spring.factories配置文件
    Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
    for (Resource resource : resources) {
        System.out.println(resource);
    }

运行结果:

⑶、演示EnvironmentCapable: 环境信息

获取系统环境变量中的java_home和项目的application.yml中的server.port属性:

	//测试获取系统环境变量中的`java_home`和项目的`application.yml`中的`server.port`属性
    System.out.println(context.getEnvironment().getProperty("java_home"));
    System.out.println(context.getEnvironment().getProperty("server.port"));

结果如下:

⑷、演示ApplicationEventPublisher: 发布事件对象(重要)

我们模拟用户注册发布事件:

  • 定义一个用户注册事件类,继承自ApplicationEvent类
import org.springframework.context.ApplicationEvent;

public class UserRegisteredEvent extends ApplicationEvent {
    public UserRegisteredEvent(Object source) {
        super(source);
    }
}

  • 定义一个监听器类,主要用来监听事件,处理事件。类头上需要加@Component注解,将该类交给spring管理,定义一个处理事件的方法,参数类型为用户注册事件类的对象,方法头上需要加上@EventListener注解
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class UserRegisteredListener {
    @EventListener
    public void userRegist(UserRegisteredEvent event) {
        System.out.println("UserRegisteredEvent...");
        log.debug("{}", event);
    }
}
  • 定义一个service 用来模拟用户注册业务,注册完毕发送用户注册事件
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class UserService {
    @Autowired
    private ApplicationEventPublisher context;

    public void register(String username, String password) {
        log.debug("新用户注册,账号:" + username + ",密码:" + password);
        context.publishEvent(new UserRegisteredEvent(this));
    }
}
  • 在启动类模拟调用
 	//模拟调用事件
    UserService userService = context.getBean(UserService.class);
    userService.register("colin","123456");

结果如下:

④、a01总结:

学到了什么?

a. BeanFactory 与 ApplicationContext 并不仅仅是简单接口继承的关系, ApplicationContext 组合并扩展了 BeanFactory 的功能

b. 又新学一种代码之间解耦途径(发布事件)

a02 、容器的实现

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 的功能,并非继承而来

①、DefaultListableBeanFactory

接着第一讲中的内容,执行以下代码,可以了解到

ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
// org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 查看实际类型
// class org.springframework.beans.factory.support.DefaultListableBeanFactory
System.out.println(beanFactory.getClass());

ConfigurableApplicationContext类内部组合的BeanFactory实际类型为DefaultListableBeanFactory

spring底层创建实体类就是依赖于这个类,所以它是BeanFactory接口最重要的一个实现类。

  • 模拟一下spring使用DefaultListableBeanFactory类创建其他实体类对象的过程。

    • 准备工作首先我们在类中定义一个配置类,并增加几个bean的声明

      package com.colin.a02;
      
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.beans.factory.annotation.Autowired;
      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.Bean;
      import org.springframework.context.annotation.Configuration;
      
      import javax.annotation.Resource;
      
      public class TestBeanFactory {
          public static void main(String[] args) {
             
          }
      
          @Configuration
          static class Config {
              @Bean
              public Bean1 bean1() {
                  return new Bean1();
              }
      
              @Bean
              public Bean2 bean2() {
                  return new Bean2();
              }
          }
      
          static class Bean1 {
              public Bean1() {
                  log.debug("构造 Bean1()");
              }
          }
      
          static class Bean2 {
              
          }
      }
      
    • 紧接着我们在main方法中利用DefaultListableBeanFactory和bean的定义信息BeanDefinition把配置类注册到spring容器。打印所有的bean的定义信息。

      public static void main(String[] args) {
              //创建BeanFactory重要的实现类DefaultListableBeanFactory
              DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
      
              
              AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                      .genericBeanDefinition(Config.class)
                      .setScope("singleton")
                      .getBeanDefinition();
      
              //将bean定义注册到beanfactory
              beanFactory.registerBeanDefinition("config",beanDefinition);
      
              //查看所有bean的定义信息
              for (String name : beanFactory.getBeanDefinitionNames()) {
                  System.out.println(name);
              }
          }
      

      结果如下:


      我们通过结果发现:容器中只有config类,但在配置类中的@Bean标注的Bean1、Bean2都不在容器中。

      这是因为beanFactory没有能力处理@Configuration和@Bean注解,我们需要为beanFactory添加一些Bean工厂的后处理器,让后处理器处理这些事情。

    • 我们为beanFactory注入常用的处理器,并再次打印容器中bean的所有定义

              // 给 BeanFactory 添加一些常用的后处理器
              AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
      
              //查看所有bean的定义信息
              for (String name : beanFactory.getBeanDefinitionNames()) {
                  System.out.println(name);
              }
      

      结果如下:


      我们发现除了config,又多了很多bean的定义信息,这些多的bean就是AnnotationConfigUtils为我们添加的。但还是没有我们自己定义的bean信息,这是因为,我们只是将后处理器放入到了容器中,并没有调用它们。

    • 我们接着手动调用这些后处理器(Bean工厂的后处理器都实现了BeanFactoryPostProcessor接口)

          //调用 BeanFactory 的后处理器
          // BeanFactory 后处理器主要功能,补充了一些 bean 定义
          System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
          beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
              //查看类型
              System.out.println(beanFactoryPostProcessor.getClass());
              //手动调用后处理器
              beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
          });
          System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
          //再次查看所有bean的定义信息
          for (String name : beanFactory.getBeanDefinitionNames()) {
              System.out.println(name);
          }
      
      

      输出结果:

      可以看到:我们手动调用了以下后处理器。

      • org.springframework.context.annotation.ConfigurationClassPostProcessor
      • org.springframework.context.event.EventListenerMethodProcessor

      再次查看所有bean的定义信息我们看到了bean1、bean2

    • 现在我们继续研究,我们已经看到了bean1的定义,那下面我们试一下这些bean能不能正常使用

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

      结果:

      这里有一个细节(打印了 构造 Bean1()):说明只有我们getBean的时候 BeanFactory才会真正的根据bean的定义信息创建这个bean,BeanFactory并不会主动创建bean

    • 可以看到可以使用,那我们改造一下代码,改变一下代码把依赖注入加上

      //改造bean1 
      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;
              }
          }
      

      调用bean1的getBean2方法

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

      结果:


      我们发现打印为null,这是因为BeanFactory没有处理@Autowired的能力,那是不是缺少相应的后处理器呢?

      这里注意的是BeanFactory 后处理器主要功能,补充了一些 bean 定义。 而依赖注入是创建bean的时期要处理的。并不是用来补充 bean的 定义。

      我们是要添加后处理器,但这个后处理器不是针对BeanFactory的,而是针对bean,我们把针对 bean 的生命周期的各个阶段提供扩展的后处理器称为Bean 后处理器

      • AnnotationConfigUtilsy已经为我们添加了Bean 后处理器的定义信息,我们要将Bean 后处理器设置给BeanFactory让其发挥作用

        Bean 后处理器都实现了BeanPostProcessor接口

        //        System.out.println(beanFactory.getBean(Bean1.class));
        //        System.out.println(beanFactory.getBean(Bean1.class).getBean2());
        
                //给beanFactory设置bean后处理器
                beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().forEach(
                        beanPostProcessor -> {
                            System.out.println(">>>>>" + beanPostProcessor);
                            beanFactory.addBeanPostProcessor(beanPostProcessor);
                        }
                );
                //再次查看bean1
                System.out.println(beanFactory.getBean(Bean1.class));
                System.out.println(beanFactory.getBean(Bean1.class).getBean2());
        

        结果:

        我们可以看到:

        ​ 给beanFactory设置bean后处理器:

        org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
        org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

        ​ bean1也已经有了bean2:

        com.colin.a02.TestBeanFactory B e a n 1 @ 33 d 512 c 1 < b r / > c o m . c o l i n . a 02. T e s t B e a n F a c t o r y Bean1@33d512c1
        com.colin.a02.TestBeanFactory Bean1@33d512c1
        com.colin.a02.TestBeanFactoryBean2@515c6049`

    • 探究Bean后处理器有排序的逻辑

      • 准备工作定义一个接口Inter,再定义两个Bean,名称分别为Bean3和Bean4,都继承Inter,接着在Config中通过@Bean注解将Bean3和Bean4都加进Bean工厂中,然后在Bean1中定义一个Inter对象,通过@Autowired注解将实现类注入进来。

        @Configuration
            static class Config {
                @Bean
                public Bean1 bean1() {
                    return new Bean1();
                }
        
                @Bean
                public Bean2 bean2() {
                    return new Bean2();
                }
        
                //将bean3加进Bean工厂
                @Bean
                public Bean3 bean3() {
                    return new Bean3();
                }
         		//将bean4加进Bean工厂
                @Bean
                public Bean4 bean4() {
                    return new Bean4();
                }
            }
        
            //定义接口
            interface Inter {
        
            }
        
            //bean3
            static class Bean3 implements Inter {
        
            }
            //bean4
            static class Bean4 implements Inter {
        
            }
        
            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;
                }
        
                //注入Inter
                @Autowired
                private Inter bean3;
        
                public Inter getInter() {
                    return bean3;
                }
            }
        
            static class Bean2 {
               
            }
        
      • 由于BeanFactory不会主动创建bean,所以我们调用BeanFactory类中重要的方法preInstantiateSingletons()

        preInstantiateSingletons()方法会准备好了所有的单例bean

        我们从容器中取出bean1 然后调用getInter() 看成员变量bean3注入的类型到底是什么类型?

        public static void main(String[] args) {
               //.....以前代码
        
               //调用BeanFactory类中重要的方法preInstantiateSingletons准备好了所有的单例
                beanFactory.preInstantiateSingletons(); // 准备好所有单例
                //取出bean1
                Bean1 bean1 = beanFactory.getBean(Bean1.class);
                //调用getInter()
                System.out.println(bean1.getInter());
            }
        

        结果如下:

        • 我们发现注入的是bean3。
        • 我们分析一下原因:@Autowired是根据类型注入,但容器中是Inter类型的有两个,bean3、bean4,所以@Autowired会根据变量的名称去配置bean的名称,所以注入的是bean3
      • 我们接着实验,我们除了使用@Autowired注解还可以使用@Resource注解实现依赖注入。所以我们去除@Autowired使用@Resource并指明bean的名字,查看结果。

        //改造bean1
        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;
                }
                @Resource(name = "bean4")
                private Inter bean3;
        
                public Inter getInter() {
                    return bean3;
                }
            }
        

        确实注入对象变成了bean4

      • 我们接着如果@Autowired和@Resource同时存在(正常开发,不会怎么干,这只是实验),会注入什么对象呢?

        //改造bean1
        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;
                }
            
                @Autowired
                @Resource(name = "bean4")
                private Inter bean3;
        
                public Inter getInter() {
                    return bean3;
                }
            }
        

        结果又变成了bean3,这是为什么呢? 我们研究一下。

        • 首先我们知道,BeanFactory并部具备处理这些依赖注入相关注解的能力,处理@Autowired和@Resource都是bean后处理器工作。而这些后处理器是我们调用AnnotationConfigUtils工具类内的方法添加的。

        • 以往学习我们知道AnnotationConfigUtils为BeanFactory添加的bean后处理器主要有两个org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor(处理@Autowired)
          org.springframework.context.annotation.CommonAnnotationBeanPostProcessor(处理@Resource)

        • 我们点进去AnnotationConfigUtils的registerAnnotationConfigProcessors()方法看一下源码

          • 我们发现AutowiredAnnotationBeanPostProcessor确实比CommonAnnotationBeanPostProcessor顺序靠前
        • AnnotationConfigUtils不仅为BeanFcatory添加了一些常用后处理器,同时也为BeanFcatory添加了默认的后处理排序规则, 补充后处理器内有order属性就是排序的依据,数字越小,排名越往前,反之越靠后。

        • 我们通过代码试着改变一下后处理顺序,看一下效果

          //改变bean后处理顺序前
                  System.out.println("改变顺序前");
                  beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().forEach(
                          beanPostProcessor -> {
                              System.out.println(">>>>>" + beanPostProcessor);
                          }
                  );
                  //改变顺序后
                  System.out.println("改变顺序后");
                  beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
                          .sorted(beanFactory.getDependencyComparator())
                          .forEach(beanPostProcessor -> {
                              System.out.println(">>>>" + beanPostProcessor);
                              beanFactory.addBeanPostProcessor(beanPostProcessor);
                  });
                  //调用BeanFactory类中重要的方法preInstantiateSingletons准备好了所有的单例
                  beanFactory.preInstantiateSingletons(); // 准备好所有单例
                  //取出bean1
                  Bean1 bean1 = beanFactory.getBean(Bean1.class);
                  //调用getInter()
                  System.out.println(bean1.getInter());
          

          结果:如下图所示,改变顺序后CommonAnnotationBeanPostProcessor排在了AutowiredAnnotationBeanPostProcessor之前,依赖注入只会进行一次,所以在依赖注入时先是@Resource被解析完成了依赖注入,@Autowired后被解析,但依赖注入已经完成,所以不起效果,所以注入的对象是bean4

⑴、DefaultListableBeanFactory总结:

学到了什么:

beanFactory 不会做的事
a.不会主动调用 BeanFactory 后处理器

b.不会主动添加 Bean 后处理器
c.不会主动初始化单例

d.不会解析: beanFactory 还不会解析 ${ } 与 #{ }

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

②、常见 ApplicationContext 实现
  • 四个重要的ApplicationContext接口的实现类

    • ClassPathXmlApplicationContext
    • FileSystemXmlApplicationContext
    • AnnotationConfigApplicationContext
    • AnnotationConfigServletWebServerApplication

⑴、测试ClassPathXmlApplicationContext
  • 在resources下新建a02.xml

    
    
    
      
      
    
      
      
          
          
      
    
    
  • main方法测试

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
    import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
    import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
    import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.context.support.FileSystemXmlApplicationContext;
    import org.springframework.web.servlet.DispatcherServlet;
    import org.springframework.web.servlet.mvc.Controller;
    
    
    public class A02 {
        private static final Logger log = LoggerFactory.getLogger(A02.class);
    
        public static void main(String[] args) {
            testClassPathXmlApplicationContext();
        }
    
        // ⬇️较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
        private static void testClassPathXmlApplicationContext() {
            ClassPathXmlApplicationContext context =
                    new ClassPathXmlApplicationContext("a02.xml");
    
            for (String name : context.getBeanDefinitionNames()) {
                System.out.println(name);
            }
    
            System.out.println(context.getBean(Bean2.class).getBean1());
        }
    
    

    测试结果:

⑵、测试FileSystemXmlApplicationContext
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;


public class A02 {
    private static final Logger log = LoggerFactory.getLogger(A02.class);

    public static void main(String[] args) {
        testFileSystemXmlApplicationContext();
    }
    
// ⬇️基于磁盘路径下 xml 格式的配置文件来创建
    private static void testFileSystemXmlApplicationContext() {
        FileSystemXmlApplicationContext context =
                new FileSystemXmlApplicationContext(
                        "src\main\resources\a02.xml");
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        System.out.println(context.getBean(Bean2.class).getBean1());
    }
}

测试结果:

⑶、测试AnnotationConfigApplicationContext
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;


public class A02 {
    private static final Logger log = LoggerFactory.getLogger(A02.class);

    public static void main(String[] args) {
        testAnnotationConfigApplicationContext();
    }


    // ⬇️较为经典的容器, 基于 java 配置类来创建
    private static void testAnnotationConfigApplicationContext() {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(Config.class);

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        System.out.println(context.getBean(Bean2.class).getBean1());
    }

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

        @Bean
        public Bean2 bean2(Bean1 bean1) {
            Bean2 bean2 = new Bean2();
            bean2.setBean1(bean1);
            return bean2;
        }
    }

    static class Bean1 {
    }

    static class Bean2 {

        private Bean1 bean1;

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

        public Bean1 getBean1() {
            return bean1;
        }
    }
}

测试结果:

⑷、测试AnnotationConfigServletWebServerApplication
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;


public class A02 {
    private static final Logger log = LoggerFactory.getLogger(A02.class);

    public static void main(String[] args) {
        testAnnotationConfigServletWebServerApplicationContext();
    }


    // ⬇️较为经典的容器, 基于 java 配置类来创建, 用于 web 环境
    private static void testAnnotationConfigServletWebServerApplicationContext() {
        AnnotationConfigServletWebServerApplicationContext context =
                new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    @Configuration
    static class WebConfig {
        
       
        @Bean
        public ServletWebServerFactory servletWebServerFactory(){
            return new TomcatServletWebServerFactory();
        }
        @Bean
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }
        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }
        //最简单的控制器 bean的名字就是访问路径 路径是:/hello
        @Bean("/hello")
        public Controller controller1() {
            return (request, response) -> {
                response.getWriter().print("hello");
                return null;
            };
        }
    }
}

测试结果:

访问浏览器http://localhost:8080/hello

⑸、模拟ApplicationContext 实现都干了什么?
  • 以ClassPathXmlApplicationContext示例

     public static void main(String[] args) {
            DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
            System.out.println("读取之前...");
            for (String name : beanFactory.getBeanDefinitionNames()) {
                System.out.println(name);
            }
            System.out.println("读取之后...");
            XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
            reader.loadBeanDefinitions(new ClassPathResource("a02.xml"));
            for (String name : beanFactory.getBeanDefinitionNames()) {
                System.out.println(name);
            }
     }
    

    测试结果:

⑹、总结

学到了什么

a. 常见的 ApplicationContext 容器实现

b. 内嵌容器、DispatcherServlet 的创建方法、作用

a03 、Bean 的生命周期
  • 一个受 Spring 管理的 bean,生命周期主要阶段有
    • 创建:根据 bean 的构造方法或者工厂方法来创建 bean 实例对象
    • 依赖注入:根据 @Autowired,@Value 或其它一些手段,为 bean 的成员变量填充值、建立关系
    • 初始化:回调各种 Aware 接口,调用对象的各种初始化方法
    • 销毁:在容器关闭时,会销毁所有单例对象(即调用它们的销毁方法)
      • prototype 对象也能够销毁,不过需要容器这边主动调用

一些资料会提到,生命周期中还有一类 bean 后处理器:BeanPostProcessor,会在 bean 的初始化的前后,提供一些扩展逻辑。但这种说法是不完整的因为bean 后处理器可能在bean的各个生命周期里发挥作用。

①、演示1 - bean 生命周期
  • springboot项目启动类

    @SpringBootApplication
    public class A03 {
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(A03.class, args);
            context.close();
        }
    }
    
  • 定义一个LifeCycleBean,加上@Component注解,再编写一些方法,给这些方法加上Bean的生命周期过程中的注解

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
    
    @Slf4j
    @Component
    public class LifeCycleBean {
    
        public LifeCycleBean() {
            log.info("构造方法");
        }
    
        @Autowired
        public void autowire(@Value("${java_home}") String home) {
            log.info("依赖注入:{}", home);
        }
    
        @PostConstruct
        public void init() {
            log.info("初始化方法");
        }
    
        @PreDestroy
        public void destory() {
            log.info("销毁方法");
        }
    }
    

    结果打印:

    执行顺序是:

    构造方法 》》 依赖注入 》》 初始化方法 》》 销毁方法

  • 下面我们编写自定义Bean的后处理器,对lifeCycleBean的生命周期过程进行扩展,编写自定义Bean的后处理器需要实现InstantiationAwareBeanPostProcessor和DestructionAwareBeanPostProcessor接口,并加上@Component注解。

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.PropertyValues;
    import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
    import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
    import org.springframework.stereotype.Component;
    
    
    @Slf4j
    @Component
    public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {
    
        
        @Override
        public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
            if (beanName.equals("lifeCycleBean")) {
                log.info("<<<<<< 销毁之前执行 (postProcessBeforeDestruction), 如 @PreDestroy");
            }
        }
    
        
        @Override
        public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
            if (beanName.equals("lifeCycleBean")) {
                log.info("<<<<<< 实例化之前(未调用构造方法)执行(postProcessBeforeInstantiation), 这里返回的对象会替换掉原本的 bean");
            }
            return null;
        }
    
        
        @Override
        public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
            if (beanName.equals("lifeCycleBean")) {
                log.info("<<<<<< 实例化之后(调用构造方法后)执行(postProcessAfterInstantiation), 这里如果返回 false 会跳过依赖注入阶段");
    //            return false;
            }
            return true;
        }
    
        
        @Override
        public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
            if (beanName.equals("lifeCycleBean")) {
                log.info("<<<<<< 依赖注入阶段执行(postProcessProperties), 如 @Autowired、@Value、@Resource");
            }
            return pvs;
        }
    
        
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals("lifeCycleBean")) {
                log.info("<<<<<< 初始化之前执行(postProcessBeforeInitialization), 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
            }
            return bean;
        }
    
        
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals("lifeCycleBean")) {
                log.info("<<<<<< 初始化之后执行(postProcessAfterInitialization), 这里返回的对象会替换掉原本的 bean, 如代理增强");
            }
            return bean;
        }
    }
    
    

    结果打印:

    把postProcessAfterInstantiation返回值改为false,让bean跳过依赖注入阶段结果:依赖注入语句无打印

②、演示2 - 模板方法设计模式
  • 提高代码的扩展性。
import java.util.ArrayList;
import java.util.List;

public class TestMethodTemplate {

    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Autowired"));
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));
        beanFactory.getBean();
    }

    // 模板方法  Template Method Pattern
    static class MyBeanFactory {
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            for (BeanPostProcessor processor : processors) {
                processor.inject(bean);
            }
            System.out.println("初始化 " + bean);
            return bean;
        }

        private List processors = new ArrayList<>();

        public void addBeanPostProcessor(BeanPostProcessor processor) {
            processors.add(processor);
        }
    }

    static interface BeanPostProcessor {
        public void inject(Object bean); // 对依赖注入阶段的扩展
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/921395.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号