美文网首页
springboot starter启动流程

springboot starter启动流程

作者: 凌晨的咸鱼 | 来源:发表于2021-09-30 11:34 被阅读0次
    1. 初始化SpringApplication:确定webApplicatuonType类型,扫描所有jar包下META-INF/spring.factories文件,生成ApplicationContextInitializer和ApplicationListener接口的实现类的对象。
    - 确定webApplicationType类型:
      this.webApplicationType = WebApplicationType.deduceFromClasspath();
      源码解析:获取类加载器,去加载路径下的类,如果找不到就抛异常返回false,最后通过判 
      断返回类型为SERVLET
      参考:https://blog.csdn.net/z69183787/article/details/105834513/
    
    - 确定applicationContext的实现类:
      run方法中有这一段:
      ConfigurableApplicationContext context = null;
      context = this.createApplicationContext();
      打开createApplicationContext方法:
      switch(this.webApplicationType) {
          case SERVLET:
              contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
              break;
          case REACTIVE:
              contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
              break;
          default:
              contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
      * 最终得知applicationcontext的实现类为;AnnotationConfigServletWebServerApplicationContext
        然后通过return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass) 方法实例化AnnotationConfigServletWebServerApplicationContext类,生成对象
      
      * 所以,通过添加不同的jar包,就能得到不同的webApplicationType类型,通过不同的webApplicationType类型就能加载不同的applicationContext的实现类。
    
    - 假如我们想在springboot项目启动完成之后,做点什么,我们应该怎么办呢?
      实现ApplicationRunner,重写run方法。
      this.callRunners(context, applicationArguments); 这段是执行程序,大致流程如下:
      找到ApplicationRunner的实现类,反射生成对象,调用run()方法。前提是实现了ApplicationRunner的类标注@Component注解交给spring管理。
      思维发散:要用到spring某个接口的功能时,先要创建一个类实现接口并加上@Component注解交给Spring管理,然后实现这个接口的某个固定方法,
      spring会在执行过程中去找到接口的实现类,然后反射创建对象,调用这个固定方法执行其中的程序。
      参考:https://zhuanlan.zhihu.com/p/343357078
    
    - Runner,它是在项目加载完成之后执行的,有后就有前,有没有在项目加载之前执行的呢,ApplicationContextInitializer就是在spring的bean加载之前执行的
      ApplicationContextInitializer用于在spring容器刷新之前初始化Spring ConfigurableApplicationContext的回调接口。
      this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); - 就是这个方法中的this.applyInitializers(context)方法找到调用initialize()方法
      this.refreshContext(context);
      剪短说就是在容器刷新之前调用该类的 initialize 方法。并将 ConfigurableApplicationContext 类的实例传递给该方法
      通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活配置文件等。
      * 过程:在spring的bean加载之前,通过扫描所有jar包META-INF文件夹下的spring.factories文件,找到所有实现了ConfigurableApplicationContext接口的类,
        进行实例化,然后调用每个实例化对象的initialize()方法进行初始化等工作。这就是ApplicationContextInitializer接口的作用,请记住,他是用来初始化的。
      * 自己实现:创建一个类实现ApplicationContextInitializer接口,重写initialize()方法,在resource目录下新建META-INF文件夹,
        新建spring.factories文件,按照格式接口=配置类,即可以实现spring的bean加载前调用的初始化程序
      参考: https://zhuanlan.zhihu.com/p/343704665
    
    - ApplicationContextAware
      如果我们需要在工具类中获取bean怎么办,又不能用@Autowired引入
      此时,我们可以创建一个类交给Spring管理,然后实现ApplicationContextAware接口,重写其中的set方法。然后在这个类中写个方法去get全局变量ApplicationContext,获取写个方法获取某个bean就可以用了
      源码逻辑:启动过程中会去找到ApplicationContextAware接口的实现类,然后通过回调其中的set方法,把全局使用的ApplicationContext给set进去,所以我们就可以使用了。
    
    - springboot项目有很多自动配置注解,比如@EnableScheduling定时任务,@EnableAsync异步编程。
      其实实现很简单,点开这个注解,你会发现里面有一个@Import({SchedulingConfiguration.class})注解。
      进入导入的注解类,返回一个return new ScheduledAnnotationBeanPostProcessor();
      进入这个创建的类,有一个核心方法:
      public Object postProcessAfterInitialization(Object bean, String beanName) {
          if (!(bean instanceof AopInfrastructureBean) && !(bean instanceof TaskScheduler) && !(bean instanceof ScheduledExecutorService)) {
              Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
              if (!this.nonAnnotatedClasses.contains(targetClass)) {
                  Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, (method) -> {
                      Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);
                      return !scheduledMethods.isEmpty() ? scheduledMethods : null;
                  });
                  if (annotatedMethods.isEmpty()) {
                      this.nonAnnotatedClasses.add(targetClass);
                      if (this.logger.isTraceEnabled()) {
                          this.logger.trace("No @Scheduled annotations found on bean class: " + targetClass);
                      }
                  } else {
                      // 重点就是这里,因为我们是定时任务自动配置,所以这里面会找到加上@Scheduled注解的所有方法,然后去执行他们
                      annotatedMethods.forEach((method, scheduledMethods) -> {
                          scheduledMethods.forEach((scheduled) -> {
                              this.processScheduled(scheduled, method, bean);
                          });
                      });
                      if (this.logger.isTraceEnabled()) {
                          this.logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + "': " + annotatedMethods);
                      }
                  }
              }
    
              return bean;
          } else {
              return bean;
          }
      }
      所以其实很简单,我们在启动类上配置@EnableXXX注解,就会自动导入对应的实现类,实现类中有某个方法去执行对应的逻辑,
      比如找到添加了定时器@Scheduled的注解方法或者添加了异步的注解方法,去执行其中的逻辑。
    
    
    

    Spring如何把bean对象放入applicationContext的实现类AnnotationConfigServletWebServerApplicationContext中的源码在哪里研究下

    listeners研究下

    重要方法:
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
            // 获取类加载器
            ClassLoader classLoader = this.getClassLoader();
            // 获取所有实现类的名称集合
            Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            // 生成实现类的对象集合
            List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }
    
    1. 通过@EnableAutoConfiguration注解中的@Import({AutoConfigurationImportSelector.class})注解,同样扫描所有jar包下META-INF/spring.factories文件,生成EnableAutoConfiguration接口的实现类的对象,比如mybatis-spring-boot-starter就是这种方式加载的。
    2. 通过@ComponentScan注解,扫描启动类所在包及其子包中所有包含@Component的类并实例化,主要包括我们自定义的@Controller,@Service等注解。

    相关文章

      网友评论

          本文标题:springboot starter启动流程

          本文链接:https://www.haomeiwen.com/subject/exzsdltx.html