SpringBoot配置外部Tomcat项目启动流程源码分析(长文)

简介: SpringBoot配置外部Tomcat项目启动流程源码分析(长文)

【3】SpringBoot 使用外部Tomcat启动原理

① 首先看Servlet3.0中的规范

  • javax.servlet.ServletContainerInitializer(其是一个接口) 类是通过JAR服务API查找的。对于每个应用程序,ServletContainerInitializer的一个实例是由容器在应用程序启动时创建。
  • 提供servletcontainerinitializer实现的框架必须将名为javax.servlet的文件捆绑到jar文件的META-INF/services目录中。根据JAR服务API,找到指向ServletContainerInitializer的实现类。
  • 除了ServletContainerInitializer 之外,还有一个注解–@HandlesTypes。ServletContainerInitializer 实现上的handlesTypes注解用于寻找感兴趣的类–要么是@HandlesTypes注解指定的类,要么是其子类。
  • 不管元数据完成的设置如何,都将应用handlesTypes注解。
  • ServletContainerInitializer实例的onStartup 方法将在应用程序启动时且任何servlet侦听器事件被激发之前被调用。

ServletContainerInitializer 的onStartup 方法调用是伴随着一组类的(Set<Class<?>> webAppInitializerClasses),这些类要么是initializer的扩展类,要么是添加了@HandlesTypes注解的类。将会依次调用webAppInitializerClasses实例的onStartup方法。

总结以下几点:

1)服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例;

2)jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名;

如下图所示:

3)还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类;

4)容器启动过程中首先调用ServletContainerInitializer 实例的onStartup方法。

ServletContainerInitializer 接口如下:

public interface ServletContainerInitializer {
    void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}

② 步骤分析如下

第一步,Tomcat启动

第二步,根据Servlet3.0规范,找到ServletContainerInitializer ,进行实例化

jar包路径:

org\springframework\spring-web\4.3.14.RELEASE\
spring-web-4.3.14.RELEASE.jar!\METAINF\services\
javax.servlet.ServletContainerInitializer:

Spring的web模块里面有这个文件:

org.springframework.web.SpringServletContainerInitializer



第三步,创建实例

SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set集合,为这些WebApplicationInitializer类型的类创建实例并遍历调用其onStartup方法。

SpringServletContainerInitializer 源码如下(调用其onStartup方法):

//感兴趣的类为WebApplicationInitializer及其子类
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
  //先调用onStartup方法,会传入一系列webAppInitializerClasses
  @Override
  public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
      throws ServletException {
    List<WebApplicationInitializer> initializers = new LinkedList<>();
    if (webAppInitializerClasses != null) {
      //遍历感兴趣的类
      for (Class<?> waiClass : webAppInitializerClasses) {
        // Be defensive: Some servlet containers provide us with invalid classes,
        // no matter what @HandlesTypes says...
        //判断是不是接口,是不是抽象类,是不是该类型
        if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
            WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
          try {
            //实例化每个initializer并添加到initializers中
            initializers.add((WebApplicationInitializer)
                ReflectionUtils.accessibleConstructor(waiClass).newInstance());
          }
          catch (Throwable ex) {
            throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
          }
        }
      }
    }
    if (initializers.isEmpty()) {
      servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
      return;
    }
    servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
    AnnotationAwareOrderComparator.sort(initializers);
    //依次调用initializer的onStartup方法。
    for (WebApplicationInitializer initializer : initializers) {
      initializer.onStartup(servletContext);
    }
  }

20190206155530105.png

如上所示,在SpringServletContainerInitializer方法中又调用每一个initializer的onStartup方法。即先调用SpringServletContainerInitializer实例的onStartup方法,在onStartup()方法内部又遍历每一个WebApplicationInitializer类型的实例,调用其onStartup()方法。

WebApplicationInitializer(Web应用初始化器)是什么?

在Servlet 3.0+环境中提供的一个接口,以便编程式配置ServletContext而非传统的xml配置。该接口的实例被SpringServletContainerInitializer自动检测(@HandlesTypes(WebApplicationInitializer.class)这种方式)。而SpringServletContainerInitializer是Servlet 3.0+容器自动引导的。通过WebApplicationInitializer,以往在xml中配置的DispatcherServlet、Filter等都可以通过代码注入。你可以不用直接实现WebApplicationInitializer,而选择继承AbstractDispatcherServletInitializer。

WebApplicationInitializer类型的类如下图:

可以看到,将会创建我们的com.web.ServletInitializer(继承自SpringBootServletInitializer)实例,并调用onStartup方法。

第四步:我们的SpringBootServletInitializer的实例(com.web.ServletInitializer)会被创建对象,并执行onStartup方法(com.web.ServletInitializer继承自SpringBootServletInitializer,故而会调用SpringBootServletInitializer的onStartup方法)

SpringBootServletInitializer源码如下:

  @Override
  public void onStartup(ServletContext servletContext) throws ServletException {
    // Logger initialization is deferred in case an ordered
    // LogServletContextInitializer is being used
    this.logger = LogFactory.getLog(getClass());
    //创建WebApplicationContext 
    WebApplicationContext rootAppContext = createRootApplicationContext(
        servletContext);
    if (rootAppContext != null) {
//如果根容器不为null,则添加监听--注意这里的ContextLoaderListener,
//contextInitialized方法为空,因为默认application context已经被初始化
      servletContext.addListener(new ContextLoaderListener(rootAppContext) {
        @Override
        public void contextInitialized(ServletContextEvent event) {
          // no-op because the application context is already initialized
        }
      });
    }
    else {
      this.logger.debug("No ContextLoaderListener registered, as "
          + "createRootApplicationContext() did not "
          + "return an application context");
    }
  }

可以看到做了两件事:创建RootAppContext 和为容器添加监听。


创建WebApplicationContext 源码如下:

protected WebApplicationContext createRootApplicationContext(
      ServletContext servletContext) {
      //创建SpringApplicationBuilder --这一步很关键
    SpringApplicationBuilder builder = createSpringApplicationBuilder();
    //设置应用主启动类--本文这里为com.web.ServletInitializer
    builder.main(getClass());
*/从servletContext中获取servletContext.getAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)作为parent。第一次获取肯定为null
*/
    ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
    if (parent != null) {
      this.logger.info("Root context already created (using as parent).");
      servletContext.setAttribute(
      //以将ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE重置为null
    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
    //注册一个新的ParentContextApplicationContextInitializer--包含parent
      builder.initializers(new ParentContextApplicationContextInitializer(parent));
    }
    //注册ServletContextApplicationContextInitializer--包含servletContext
    builder.initializers(
        new ServletContextApplicationContextInitializer(servletContext));
//设置applicationContextClass为AnnotationConfigServletWebServerApplicationContext
    builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
    builder = configure(builder);
    //添加监听器
    builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
    //返回一个准备好的SpringApplication ,准备run-很关键
    SpringApplication application = builder.build();
    if (application.getAllSources().isEmpty() && AnnotationUtils
        .findAnnotation(getClass(), Configuration.class) != null) {
      application.addPrimarySources(Collections.singleton(getClass()));
    }
    Assert.state(!application.getAllSources().isEmpty(),
        "No SpringApplication sources have been defined. Either override the "
            + "configure method or add an @Configuration annotation");
    // Ensure error pages are registered
    if (this.registerErrorPageFilter) {
      application.addPrimarySources(
          Collections.singleton(ErrorPageFilterConfiguration.class));
    }
    //启动应用
    return run(application);
  }

【4】createRootApplicationContext详细流程源码分析

① createRootApplicationContext().createSpringApplicationBuilder()

跟踪代码到:

public SpringApplicationBuilder(Class<?>... sources) {
    this.application = createSpringApplication(sources);
}

此时的Sources为空,继续跟踪代码:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //web应用类型--Servlet
    this.webApplicationType = deduceWebApplicationType();
获取ApplicationContextInitializer,也是在这里开始首次加载spring.factories文件
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
这里是第二次加载spring.factories文件
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
  }

20190124115400935.png

ApplicationContextInitializer是spring组件spring-context组件中的一个接口,主要是spring ioc容器刷新之前的一个回调接口,用于处于自定义逻辑。

ApplicationContextInitializer(应用上下文初始化器)是什么?

在ConfigurableApplicationContext-Spring IOC容器称为“已经被刷新”状态前的一个回调接口去初始化ConfigurableApplicationContext。通常用于需要对应用程序上下文进行某些编程初始化的Web应用程序中。例如,与ConfigurableApplicationContext#getEnvironment() 对比,注册property sources或激活配置文件。另外ApplicationContextInitializer(和子类)相关处理器实例被鼓励使用去检测org.springframework.core.Ordered接口是否被实现或是否存在org.springframework.core.annotation.Order注解,如果存在,则在调用之前对实例进行相应排序。


spring.factories文件中的实现类:

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

设置WebApplicationType

private WebApplicationType deduceWebApplicationType() {
    if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
        && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
      return WebApplicationType.REACTIVE;
    }
    for (String className : WEB_ENVIRONMENT_CLASSES) {
      if (!ClassUtils.isPresent(className, null)) {
        return WebApplicationType.NONE;
      }
    }
    return WebApplicationType.SERVLET;
  }

这里主要是通过判断REACTIVE相关的字节码是否存在,如果不存在,则web环境即为SERVLET类型。这里设置好web环境类型,在后面会根据类型初始化对应环境。

设置Initializer–ApplicationContextInitializer类型

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, Object... args) {
      //线程上下文类加载器
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set<String> names = new LinkedHashSet<>(
        SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
        classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
  }

这里ClassLoader 获取的是线程上下文类加载器,这里使用的是Tomcat启动:

Set<String> names如下:


获取了6个instance:

设置监听–ApplicationListener类型

此时的type为ApplicationListener,Set<String> names如下:

至此SpringApplicationBuilder创建完毕。

② 添加ServletContextApplicationContextInitializer

builder.initializers(
        new ServletContextApplicationContextInitializer(servletContext));

此时SpringApplication Initializers和Listener如下:


③ 设置application.setApplicationContextClass


④ builder = configure(builder);

此时调用我们的ServletInitializer的configure方法:


⑤ SpringApplication application = builder.build()创建应用

把我们的主类添加到application 中:


⑥ 将ErrorPageFilterConfiguration添加到Set<Class<?>> primarySources

接下来该run(application)了注意直到此时,我们让没有创建我们想要的容器,容器将会在run(application)中创建。

SpringApplication.run源码如下所示:

/**
 run Spring application,创建并刷新一个新的ApplicationContext
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
  public ConfigurableApplicationContext run(String... args) {
//简单的秒表,允许对许多任务计时,显示每个指定任务的总运行时间和运行时间。非线程安全
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    //异常报告集合
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很
//多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
    configureHeadlessProperty();
 //第一步:获取并启动监听器 SpringApplicationRunListener只有一个实现类EventPublishingRunListener,
 //EventPublishingRunListener有一个SimpleApplicationEventMulticaster
 //SimpleApplicationEventMulticaster有一个defaultRetriver
 //defaultRetriver有个属性为applicationListeners
 //每一次listeners.XXX()方法调用,都将会广播对应事件给applicationListeners监听器处理
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();//run方法第一次被调用时,调用listeners.starting();
    try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
          args);
    //第二步:构造容器环境
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
          applicationArguments);
          //设置需要忽略的bean
      configureIgnoreBeanInfo(environment);
      //打印banner
      Banner printedBanner = printBanner(environment);
      //第三步:创建容器
      context = createApplicationContext();
//第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
      exceptionReporters = getSpringFactoriesInstances(
          SpringBootExceptionReporter.class,
          new Class[] { ConfigurableApplicationContext.class }, context);
           //第五步:准备容器
      prepareContext(context, environment, listeners, applicationArguments,
          printedBanner);
           //第六步:刷新容器
      refreshContext(context);
       //第七步:刷新容器后的扩展接口
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass)
            .logStarted(getApplicationLog(), stopWatch);
      }
//容器已经被刷新,但是CommandLineRunners和ApplicationRunners还没有被调用
      listeners.started(context);
      //调用CommandLineRunner和ApplicationRunner的run方法
      callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
    }
    try {
//在run结束前,且调用CommandLineRunner和ApplicationRunner的run方法后,调用
      listeners.running(context);
    }
    catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
    }
    return context;
  }

【5】SpringApplication.run方法详细分析-获取并启动监听器

① 获取监听器getRunListeners

SpringApplicationRunListeners listeners = getRunListeners(args);

跟进SpringApplication.getRunListeners方法(返回SpringApplicationRunListeners):

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
        SpringApplicationRunListener.class, types, this, args));
}

上面可以看到,args本身默认为空,但是在获取监听器的方法中,getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)将当前对象作为参数,该方法用来获取spring.factories对应的监听器:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, Object... args) {
      //获取类加载器 WebappClassLoader
    ClassLoader classLoader = getClassLoader();
    // Use names and ensure unique to protect against duplicates
    //根据类加载器,获取SpringApplicationRunListener(type)相关的监听器 
    Set<String> names = new LinkedHashSet<>(
        SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        //创建factories
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
        classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
  }

Set<String> names如下:

整个 springBoot 框架中获取factories的方式统一如下:

@SuppressWarnings("unchecked")
  private <T> List<T> createSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
      Set<String> names) {
    List<T> instances = new ArrayList<>(names.size());
    for (String name : names) {
      try {
      // //装载class文件到内存
        Class<?> instanceClass = ClassUtils.forName(name, classLoader);
        Assert.isAssignable(type, instanceClass);
        Constructor<?> constructor = instanceClass
            .getDeclaredConstructor(parameterTypes);
            //主要通过反射创建实例
        T instance = (T) BeanUtils.instantiateClass(constructor, args);
        instances.add(instance);
      }
      catch (Throwable ex) {
        throw new IllegalArgumentException(
            "Cannot instantiate " + type + " : " + name, ex);
      }
    }
    return instances;
  }

上面通过反射获取实例时会触发EventPublishingRunListener的构造函数。如下图所示将会把application的listener添加到SimpleApplicationEventMulticaster initialMulticaster的ListenerRetriever defaultRetriever的Set<ApplicationListener<?>> applicationListeners中:

重点来看一下addApplicationListener方法:

public void addApplicationListener(ApplicationListener<?> listener) {
    synchronized (this.retrievalMutex) {
      // Explicitly remove target for a proxy, if registered already,
      // in order to avoid double invocations of the same listener.
      Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
      if (singletonTarget instanceof ApplicationListener) {
        this.defaultRetriever.applicationListeners.remove(singletonTarget);
      }
      this.defaultRetriever.applicationListeners.add(listener);
      this.retrieverCache.clear();
    }
  }

上述方法定义在SimpleApplicationEventMulticaster父类AbstractApplicationEventMulticaster中。关键代码为this.defaultRetriever.applicationListeners.add(listener);,这是一个内部类,用来保存所有的监听器。也就是在这一步,将spring.factories中的监听器传递到SimpleApplicationEventMulticaster中。

继承关系如下:


② listeners.starting()–SpringApplicationRunListener启动–监听器第一次处理事件


listeners.starting();,获取的监听器为EventPublishingRunListener,从名字可以看出是启动事件发布监听器,主要用来发布启动事件。

SpringApplicationRunListener是run()方法的监听器,其只有一个实现类EventPublishingRunListener。SpringApplicationRunListeners是SpringApplicationRunListener的集合类。


也就是说将会调用EventPublishingRunListener的starting()方法。

public void starting() {
//关键代码,这里是创建application启动事件`ApplicationStartingEvent`
    this.initialMulticaster.multicastEvent(
        new ApplicationStartingEvent(this.application, this.args));
  }

EventPublishingRunListener这个是springBoot框架中最早执行的监听器,在该监听器执行started()方法时,会继续发布事件,也就是事件传递。这种实现主要还是基于spring的事件机制。


继续跟进SimpleApplicationEventMulticaster,有个核心方法:

  @Override
  public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
    // //获取线程池,如果为空则同步处理。这里线程池为空,还未没初始化。
      Executor executor = getTaskExecutor();
      if (executor != null) {
      异步发送事件
        executor.execute(() -> invokeListener(listener, event));
      }
      else {
      // //同步发送事件
        invokeListener(listener, event);
      }
    }
  }

ApplicationListener<E extends ApplicationEvent>接口有个抽象方法onApplicationEvent(E event)子类必须实现。该方法用来处理对应事件。

其中getApplicationListeners(event, type)主要有四种listener:

  • LoggingApplicationListener(处理日志)
  • BackgroundPreinitializer
  • DelegatingApplicationListener
  • LiquibaseServiceLocatorApplicationListener

这是springBoot启动过程中,第一处根据类型,执行监听器的地方。根据发布的事件类型从上述10种监听器中选择对应的监听器进行事件发布,当然如果继承了 springCloud或者别的框架,就不止10个了。这里选了一个 springBoot 的日志监听器来进行讲解,核心代码如下:

@Override
public void onApplicationEvent(ApplicationEvent event) {
     //在springboot启动的时候
     if (event instanceof ApplicationStartedEvent) {
         onApplicationStartedEvent((ApplicationStartedEvent) event);
     }
     //springboot的Environment环境准备完成的时候
     else if (event instanceof ApplicationEnvironmentPreparedEvent) {
         onApplicationEnvironmentPreparedEvent(
                 (ApplicationEnvironmentPreparedEvent) event);
     }
     //在springboot容器的环境设置完成以后
     else if (event instanceof ApplicationPreparedEvent) {
         onApplicationPreparedEvent((ApplicationPreparedEvent) event);
     }
     //容器关闭的时候
     else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)
             .getApplicationContext().getParent() == null) {
         onContextClosedEvent();
     }
     //容器启动失败的时候
     else if (event instanceof ApplicationFailedEvent) {
         onApplicationFailedEvent();
     }
 }

因为我们的事件类型为ApplicationEvent,所以会执行onApplicationStartedEvent((ApplicationStartedEvent) event);。springBoot会在运行过程中的不同阶段,发送各种事件,来执行对应监听器的对应方法。


【6】SpringApplication.run方法详细分析-准备环境

③ prepareEnvironment–环境构建

ConfigurableEnvironment environment = prepareEnvironment(listeners,
          applicationArguments);

跟进去该方法:

  private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // Create and configure the environment
        //获取对应的ConfigurableEnvironment
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        //配置
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        //发布环境已准备事件,这是第二次发布事件
        listeners.environmentPrepared(environment);
        bindToSpringApplication(environment);
        if (this.webApplicationType == WebApplicationType.NONE) {
            environment = new EnvironmentConverter(getClassLoader())
                    .convertToStandardEnvironmentIfNecessary(environment);
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

来看一下getOrCreateEnvironment()方法,前面已经提到,environment已经被设置了servlet类型,所以这里创建的是环境对象是StandardServletEnvironment。

 private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        if (this.webApplicationType == WebApplicationType.SERVLET) {
            return new StandardServletEnvironment();
        }
        return new StandardEnvironment();
    }

枚举类WebApplicationType是SpringBoot2新增的特性,主要针对spring5引入的reactive特性。枚举类型如下

public enum WebApplicationType {
    //不需要再web容器的环境下运行,普通项目
    NONE,
    //基于servlet的web项目
    SERVLET,
    //这个是spring5版本开始的新特性
    REACTIVE
}

Environment接口提供了4种实现方式,StandardEnvironment、StandardServletEnvironment和MockEnvironment、StandardReactiveWebEnvironment,分别代表普通程序、Web程序、测试程序的环境、响应式web环境,


配置环境代码如下:

protected void configureEnvironment(ConfigurableEnvironment environment,
      String[] args) {
    configurePropertySources(environment, args);
    configureProfiles(environment, args);
  }

在返回return new StandardServletEnvironment();对象的时候,会完成一系列初始化动作,主要就是将运行机器的系统变量和环境变量,加入到其父类AbstractEnvironment定义的对象MutablePropertySources中,MutablePropertySources对象中定义了一个属性集合:

 private final List<PropertySource<?>> propertySourceList;
    public MutablePropertySources() {
        this.propertySourceList = new CopyOnWriteArrayList();
        this.logger = LogFactory.getLog(this.getClass());
    }

执行到这里,系统变量和环境变量已经被载入到配置文件的集合中,接下来就行解析项目中的配置文件。

④ listeners.environmentPrepared(environment);–第二次发布事件

来看一下listeners.environmentPrepared(environment);,上面已经提到了,这里是第二次发布事件。什么事件呢?顾名思义,系统环境初始化完成的事件。

跟进方法:


继续跟:

@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
  this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
      this.application, this.args, environment));
}

这里将要广播ApplicationEnvironmentPreparedEvent事件了

@Override
  public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      Executor executor = getTaskExecutor();
      if (executor != null) {
        executor.execute(() -> invokeListener(listener, event));
      }
      else {
        invokeListener(listener, event);
      }
    }
  }

发布事件的流程上面已经讲过了,这里不在赘述。来看一下根据事件类型获取到的监听器:

遍历监听器,调用不同监听器对该事件的处理。

可以看到获取到的监听器和第一次发布启动事件获取的监听器有几个是重复的,这也验证了监听器是可以多次获取,根据事件类型来区分具体处理逻辑。上面介绍日志监听器的时候已经提到。


主要来看一下ConfigFileApplicationListener,该监听器非常核心,主要用来处理项目配置。项目中的 properties 和yml文件都是其内部类所加载。

首先方法执行入口:

调用onApplicationEnvironmentPreparedEvent方法:

private void onApplicationEnvironmentPreparedEvent(
      ApplicationEnvironmentPreparedEvent event) {
    List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
    postProcessors.add(this);
    AnnotationAwareOrderComparator.sort(postProcessors);
    for (EnvironmentPostProcessor postProcessor : postProcessors) {
      postProcessor.postProcessEnvironment(event.getEnvironment(),
          event.getSpringApplication());
    }

首先还是会去读spring.factories 文件,List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();获取的处理类有以下四种:

SystemEnvironmentPropertySourceEnvironmentPostProcessor(加载系统环境变量):


SpringApplicationJsonEnvironmentPostProcessor:

@Override
  public void postProcessEnvironment(ConfigurableEnvironment environment,
      SpringApplication application) {
    MutablePropertySources propertySources = environment.getPropertySources();
    StreamSupport.stream(propertySources.spliterator(), false)
        .map(JsonPropertyValue::get).filter(Objects::nonNull).findFirst()
        .ifPresent((v) -> processJson(environment, v));
  }

在执行完上述三个监听器流程后,ConfigFileApplicationListener会执行该类本身的逻辑。由其内部类Loader加载项目制定路径下的配置文件:

// Note the order is from least to most specific (last one wins)
private static final String DEFAULT_SEARCH_LOCATIONS = 
"classpath:/,classpath:/config/,file:./,file:./config/";

ConfigFileApplicationListener

@Override
  public void postProcessEnvironment(ConfigurableEnvironment environment,
      SpringApplication application) {
    addPropertySources(environment, application.getResourceLoader());
  }

内部类Loader构造函数:

Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
      this.environment = environment;
      this.resourceLoader = (resourceLoader != null ? resourceLoader
          : new DefaultResourceLoader());
      this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
          PropertySourceLoader.class, getClass().getClassLoader());
    }

其获取的propertySourceLoaders 如下:

至此使用ConfigFileApplicationListener将应用配置文件加载进来,接下来该其它六个监听器依次处理。

监听器处理完毕返回到ConfigurableEnvironment

prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments)处,此时environment对象如下:

接下来将environment绑定到SpringApplication上。

返回到SpringApplication类中ConfigurableApplicationContext run(String... args)方法处



【7】SpringApplication.run方法详细分析-创建容器

⑤ createApplicationContext();创建容器

SpringBootServletInitializer.createRootApplicationContext(ServletContext servletContext)中设置过contextClass:

builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);

看创建容器的代码:

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    //如果contextClass 为null,就根据webApplicationType加载对应class;
    //这里不为null,是AnnotationConfigServletWebServerApplicationContext
    if (contextClass == null) {
      try {
        switch (this.webApplicationType) {
        case SERVLET:
          contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
          break;
        case REACTIVE:
          contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
          break;
        default:
          contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
        }
      }
      catch (ClassNotFoundException ex) {
        throw new IllegalStateException(
            "Unable create a default ApplicationContext, "
                + "please specify an ApplicationContextClass",
            ex);
      }
    }
    //这里根据反射实例化容器
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
  }

创建的context如下:


⑥ 报告错误信息

这里还是以同样的方式获取 spring.factories文件中的指定类:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set<String> names = new LinkedHashSet<>(
        SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
        classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
  }

20190124152915171.png

如果应用启动失败,则会打印失败信息:

@Override
  public void report(FailureAnalysis failureAnalysis) {
    if (logger.isDebugEnabled()) {
      logger.debug("Application failed to start due to an exception",
          failureAnalysis.getCause());
    }
    if (logger.isErrorEnabled()) {
      logger.error(buildMessage(failureAnalysis));
    }
  }

【8】SpringApplication.run方法详细分析-容器刷新之前准备

⑦ prepareContext–准备容器

这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。

private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
      //设置容器环境,包括各种变量
    context.setEnvironment(environment);
    //执行容器后置处理
    postProcessApplicationContext(context);
     //执行容器中的ApplicationContextInitializer(包括 spring.factories和自定义的实例)
    applyInitializers(context);
    //发送容器已经准备好的事件,通知各监听器
    listeners.contextPrepared(context);
    //打印log
    if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
     //注册启动参数bean,这里将容器指定的参数封装成bean,注入容器
    context.getBeanFactory().registerSingleton("springApplicationArguments",
        applicationArguments);
        //设置banner
    if (printedBanner != null) {
      context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }
    // Load the sources
     //获取我们的启动类指定的参数,可以是多个
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    //加载我们的启动类,将启动类注入容器
    load(context, sources.toArray(new Object[0]));
    //发布容器已加载事件。
    listeners.contextLoaded(context);
  }

容器的后置处理:

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    if (this.beanNameGenerator != null) {
      context.getBeanFactory().registerSingleton(
          AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
          this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
      if (context instanceof GenericApplicationContext) {
        ((GenericApplicationContext) context)
            .setResourceLoader(this.resourceLoader);
      }
      if (context instanceof DefaultResourceLoader) {
        ((DefaultResourceLoader) context)
            .setClassLoader(this.resourceLoader.getClassLoader());
      }
    }
  }

这里默认不执行任何逻辑,因为beanNameGenerator和resourceLoader默认为空。之所以这样做,是springBoot留给我们的扩展处理方式,类似于这样的扩展,spring中也有很多。

初始化器初始化方法调用

protected void applyInitializers(ConfigurableApplicationContext context) {
    for (ApplicationContextInitializer initializer : getInitializers()) {
      Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
          initializer.getClass(), ApplicationContextInitializer.class);
      Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
      initializer.initialize(context);
    }
  }

ServletContextApplicationContextInitializer.initialize:

public void initialize(ConfigurableWebApplicationContext applicationContext) {
//给创建的容器设置servletContext引用
    applicationContext.setServletContext(this.servletContext);
    //判断true or false ,如果为true,将applicationContext放到servletContext
    //这里为false。
    if (this.addApplicationContextAttribute) {
      this.servletContext.setAttribute(
          WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
          applicationContext);
    }
  }

ContextIdApplicationContextInitializer.initialize:

public void initialize(ConfigurableApplicationContext applicationContext) {
    ContextId contextId = getContextId(applicationContext);
    applicationContext.setId(contextId.getId());
    applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(),
        contextId);
  }

为applicationContext设置ID。

listeners.contextPrepared(context)发布事件–第三次发布事件了

public void contextPrepared(ConfigurableApplicationContext context) {
    for (SpringApplicationRunListener listener : this.listeners) {
      listener.contextPrepared(context);
    }
  }

这里listener为EventPublishingRunListener,其contextPrepared方法为空方法:

@Override
public void contextPrepared(ConfigurableApplicationContext context) {
}

获取primarySources放到allSources

可以看到我们的主启动类在里面。


加载启动指定类(重点)

这里会将我们的启动类加载spring容器beanDefinitionMap中,为后续springBoot 自动化配置奠定基础,springBoot为我们提供的各种注解配置也与此有关。

这里参数即为我们项目启动时传递的参数:SpringApplication.run(SpringbootwebprojectApplication.class, args);

protected void load(ApplicationContext context, Object[] sources) {
    if (logger.isDebugEnabled()) {
      logger.debug(
          "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    }
    //根据BeanDefinitionRegistry创建BeanDefinitionLoader
    BeanDefinitionLoader loader = createBeanDefinitionLoader(
        getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
      loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
      loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
    //设置环境
      loader.setEnvironment(this.environment);
    }
    loader.load();
  }

因为这里的applicationContext实现了BeanDefinitionRegistry接口,所以获取BeanDefinitionRegistry时将applicationContext强转返回。故而这里直接拿着applicationContext和sources获取BeanDefinitionLoader



20190124171221945.png



20190124171409798.png

需要注意的是,springBoot2会优先选择groovy加载方式,找不到再选用java方式。或许groovy动态加载class文件的性能更胜一筹。

private int load(Class<?> source) {
    if (isGroovyPresent()
        && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
      // Any GroovyLoaders added in beans{} DSL can contribute beans here
      GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
          GroovyBeanDefinitionSource.class);
      load(loader);
    }
    if (isComponent(source)) {
    //以注解的方式,将启动类bean信息存入beanDefinitionMap
      this.annotatedReader.register(source);
      return 1;
    }
    return 0;
  }

listeners.contextLoaded(context);广播事件容器已准备就绪–监听器第四次处理事件

public void contextLoaded(ConfigurableApplicationContext context) {
    for (SpringApplicationRunListener listener : this.listeners) {
      listener.contextLoaded(context);
    }
  }
public void contextLoaded(ConfigurableApplicationContext context) {
    for (ApplicationListener<?> listener : this.application.getListeners()) {
      if (listener instanceof ApplicationContextAware) {
        ((ApplicationContextAware) listener).setApplicationContext(context);
      }
      //将application.getListeners()添加到context
      context.addApplicationListener(listener);
    }
    //广播事件给监听器
    this.initialMulticaster.multicastEvent(
        new ApplicationPreparedEvent(this.application, this.args, context));
  }

20190124174329737.png

此时是有四个监听器处理ApplicationPreparedEvent事件:

ConfigFileApplicationListener.onApplicationPreparedEvent:

private void onApplicationPreparedEvent(ApplicationEvent event) {
    this.logger.replayTo(ConfigFileApplicationListener.class);
    addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
  }

addPostProcessors源码如下:

protected void addPostProcessors(ConfigurableApplicationContext context) {
//给context中添加BeanFactoryPostProcessor
    context.addBeanFactoryPostProcessor(
        new PropertySourceOrderingPostProcessor(context));
  }

【9】SpringApplication.run方法详细分析-刷新容器

refreshContext(context);刷新容器,准备IOC容器的核心步骤就在这里。

AbstractApplicationContext.refresh方法源码如下:

  @Override
  public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
       /**
             * 刷新上下文环境
             * 初始化上下文环境,对系统的环境变量或者系统属性进行准备和校验
             * 如环境变量中必须设置某个值才能运行,否则不能运行,这个时候可以在这里加这个校验,
             * 重写initPropertySources方法就好了
             */
      prepareRefresh();
      // Tell the subclass to refresh the internal bean factory.
      /**
             * 初始化BeanFactory,解析XML,相当于之前的XmlBeanFactory的操作,
             */
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // Prepare the bean factory for use in this context.
/**
* 为上下文准备BeanFactory,即对BeanFactory的各种功能进行填充,如常用的注解@Autowired @Qualifier等
* 设置SPEL表达式#{key}的解析器
* 设置资源编辑注册器,如PerpertyEditorSupper的支持
* 添加ApplicationContextAwareProcessor处理器
* 在依赖注入忽略实现*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
* 注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory的实例注入进去
*/
      prepareBeanFactory(beanFactory);
      try {
        // Allows post-processing of the bean factory in context subclasses.
/**
* 提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
*/
        postProcessBeanFactory(beanFactory);
        // Invoke factory processors registered as beans in the context.
/**
* 激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
* 执行对应的postProcessBeanDefinitionRegistry方法 和  postProcessBeanFactory方法
*/
        invokeBeanFactoryPostProcessors(beanFactory);
/**
* 注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是BeanFactoryPostProcessor,注意两者的区别
* 注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法
*/
        // Register bean processors that intercept bean creation.
        registerBeanPostProcessors(beanFactory);
        // Initialize message source for this context.
        /**
                 * 初始化上下文中的资源文件,如国际化文件的处理等
                 */
        initMessageSource();
        // Initialize event multicaster for this context.
         /**
                 * 初始化上下文事件广播器,并放入applicatioEventMulticaster,如ApplicationEventPublisher
                 */
        initApplicationEventMulticaster();
        // Initialize other special beans in specific context subclasses.
/**
* 给子类扩展初始化其他Bean
* 在没有使用外部Tomcat项目中,还会在这里创建内置Tomcat WebServer 并启动
*/
        onRefresh();
        // Check for listener beans and register them.
        /**
                 * 在所有bean中查找listener bean,然后注册到广播器中
                 */
        registerListeners();
        // Instantiate all remaining (non-lazy-init) singletons.
         /**
                 * 设置转换器
                 * 注册一个默认的属性值解析器
                 * 冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理
                 * 初始化剩余的非惰性的bean,即初始化非延迟加载的bean
                 */
        finishBeanFactoryInitialization(beanFactory);
        // Last step: publish corresponding event.
/**
* 初始化生命周期处理器DefaultLifecycleProcessor,DefaultLifecycleProcessor含有start方法和stop方法,spring启动的时候调用start方法开始生命周期,
* spring关闭的时候调用stop方法来结束生命周期,通常用来配置后台程序,启动有一直运行,如一直轮询kafka
* 启动所有实现了Lifecycle接口的类
* 通过spring的事件发布机制发布ContextRefreshedEvent事件,以保证对应的监听器做进一步的处理,即对那种在spring启动后需要处理的一些类,这些类实现了
* ApplicationListener<ContextRefreshedEvent> ,这里就是要触发这些类的执行(执行onApplicationEvent方法)另外,spring的内置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
* 完成初始化,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出ContextRefreshEvent通知其他人
*/
        finishRefresh();
      }
      catch (BeansException ex) {
        if (logger.isWarnEnabled()) {
          logger.warn("Exception encountered during context initialization - " +
              "cancelling refresh attempt: " + ex);
        }
        // Destroy already created singletons to avoid dangling resources.
        destroyBeans();
        // Reset 'active' flag.
        cancelRefresh(ex);
        // Propagate exception to caller.
        throw ex;
      }
      finally {
        // Reset common introspection caches in Spring's core, since we
        // might not ever need metadata for singleton beans anymore...
        resetCommonCaches();
      }
    }
  }

① obtainFreshBeanFactory();

默认获取的BeanFactory为DefaultListableBeanFactory,源码如下:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
  }

这里获取后的beanFactory如下所示:

②prepareBeanFactory-准备BeanFactory

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // Tell the internal bean factory to use the context's class loader etc.
    //设置BeanClassLoader-这里是WebappClassLoader
    beanFactory.setBeanClassLoader(getClassLoader());
    //设置SPEL表达式#{key}的解析器 --StandardBeanExpressionResolver
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    //设置资源编辑注册器,如PerpertyEditorSupper的支持-- ResourceEditorRegistrar
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    // Configure the bean factory with context callbacks.
    //添加ApplicationContextAwareProcessor处理器
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
//在依赖注入忽略实现*Aware的接口,
//如EnvironmentAware、ApplicationEventPublisherAware等
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    // BeanFactory interface not registered as resolvable type in a plain factory.
    // MessageSource registered (and found for autowiring) as a bean.
    注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),
    //则会将beanFactory的实例注入进去
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    // Register early post-processor for detecting inner beans as ApplicationListeners.
    将早期后处理器注册为应用程序监听器,以检测内部bean。
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    // Detect a LoadTimeWeaver and prepare for weaving, if found.
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      // Set a temporary ClassLoader for type matching.
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
    // Register default environment beans.
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
      beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
      beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
      beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
  }

其父类ServletWebServerApplicationContext.postProcessBeanFactory如下:

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//添加WebApplicationContextServletContextAwareProcessor后置处理器
    beanFactory.addBeanPostProcessor(
        new WebApplicationContextServletContextAwareProcessor(this));
        //忽略依赖接口
    beanFactory.ignoreDependencyInterface(ServletContextAware.class);
  }

③ 后置处理BeanFactorypostProcessBeanFactory(beanFactory);

@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  super.postProcessBeanFactory(beanFactory);
  if (this.basePackages != null && this.basePackages.length > 0) {
    this.scanner.scan(this.basePackages);
  }
  if (!this.annotatedClasses.isEmpty()) {
    this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
  }
}

注册完了,该调用BeanFactoryPostProcessors了。

④ invokeBeanFactoryPostProcessors(beanFactory)

在单例实例化前调用所有注册的BeanFactoryPostProcessor:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
    // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
  }

20190124185809500.png

这里有三个BeanFactoryPostProcessor:

  • ConfigurationWarningsApplicationContextInitializer.ConfigurationWarningsPostProcessor(后者是前者static final内部类);
  • SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor(后者是前者静态内部类);
  • ConfigFileApplicationListener.PropertySourceOrderingPostProcessor(后者是前者内部类);

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors:

class PostProcessorRegistrationDelegate {
  public static void invokeBeanFactoryPostProcessors(
      ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    // Invoke BeanDefinitionRegistryPostProcessors first, if any.
    Set<String> processedBeans = new HashSet<>();
    if (beanFactory instanceof BeanDefinitionRegistry) {
    //如果beanFactory是BeanDefinitionRegistry类型,就强转一下
      BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
      List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<>();
      List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<>();
//遍历beanFactoryPostProcessors
      for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
        if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
//如果postProcessor 还是BeanDefinitionRegistryPostProcessor类型,就强转一下
          BeanDefinitionRegistryPostProcessor registryProcessor =
              (BeanDefinitionRegistryPostProcessor) postProcessor;
              //调用registryProcessor 的postProcessBeanDefinitionRegistry方法
          registryProcessor.postProcessBeanDefinitionRegistry(registry);
          //添加到registryProcessors 
          registryProcessors.add(registryProcessor);
        }else {
        //否则添加到List<BeanFactoryPostProcessor> regularPostProcessors
        //也就是说只是BeanFactoryPostProcessor
          regularPostProcessors.add(postProcessor);
        }
      }
      // Do not initialize FactoryBeans here: We need to leave all regular beans
      // uninitialized to let the bean factory post-processors apply to them!
      // Separate between BeanDefinitionRegistryPostProcessors that implement
      // PriorityOrdered, Ordered, and the rest.
      List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
      // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
      String[] postProcessorNames =
          beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
      for (String ppName : postProcessorNames) {
        if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
          currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
          processedBeans.add(ppName);
        }
      }
      sortPostProcessors(currentRegistryProcessors, beanFactory);
      registryProcessors.addAll(currentRegistryProcessors);
      invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
      currentRegistryProcessors.clear();
      // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
      postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
      for (String ppName : postProcessorNames) {
        if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
          currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
          processedBeans.add(ppName);
        }
      }
      sortPostProcessors(currentRegistryProcessors, beanFactory);
      registryProcessors.addAll(currentRegistryProcessors);
      invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
      currentRegistryProcessors.clear();
      // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
      boolean reiterate = true;
      while (reiterate) {
        reiterate = false;
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
          if (!processedBeans.contains(ppName)) {
            currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
            processedBeans.add(ppName);
            reiterate = true;
          }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();
      }
      // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
      invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
      invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    }
    else {
      // Invoke factory processors registered with the context instance.
      invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let the bean factory post-processors apply to them!
    String[] postProcessorNames =
        beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
    // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
    // Ordered, and the rest.
    List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    List<String> orderedPostProcessorNames = new ArrayList<>();
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    for (String ppName : postProcessorNames) {
      if (processedBeans.contains(ppName)) {
        // skip - already processed in first phase above
      }
      else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
      }
      else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
        orderedPostProcessorNames.add(ppName);
      }
      else {
        nonOrderedPostProcessorNames.add(ppName);
      }
    }
    // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
    // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
    List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
    for (String postProcessorName : orderedPostProcessorNames) {
      orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    sortPostProcessors(orderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
    // Finally, invoke all other BeanFactoryPostProcessors.
    List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
    for (String postProcessorName : nonOrderedPostProcessorNames) {
      nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
    // Clear cached merged bean definitions since the post-processors might have
    // modified the original metadata, e.g. replacing placeholders in values...
    beanFactory.clearMetadataCache();
  }

遍历BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors后List<BeanFactoryPostProcessor> regularPostProcessors和List<BeanDefinitionRegistryPostProcessor> registryProcessors如下所示:

⑤ registerBeanPostProcessors(beanFactory);

实例化并调用所有已经注册的BeanPostProcessor:

/**
   * Instantiate and invoke all registered BeanPostProcessor beans,
   * respecting explicit order if given.
   * <p>Must be called before any instantiation of application beans.
   */
  protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
  }

⑥ initMessageSource();

初始化上下文中的资源文件,如国际化文件的处理等:

protected void initMessageSource() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
      this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
      // Make MessageSource aware of parent MessageSource.
      if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
        HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
        if (hms.getParentMessageSource() == null) {
          // Only set parent context as parent MessageSource if no parent MessageSource
          // registered already.
          hms.setParentMessageSource(getInternalParentMessageSource());
        }
      }
      if (logger.isTraceEnabled()) {
        logger.trace("Using MessageSource [" + this.messageSource + "]");
      }
    }
    else {
      // Use empty MessageSource to be able to accept getMessage calls.
      DelegatingMessageSource dms = new DelegatingMessageSource();
      dms.setParentMessageSource(getInternalParentMessageSource());
      this.messageSource = dms;
      beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
      if (logger.isTraceEnabled()) {
        logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
      }
    }
  }

⑦ initApplicationEventMulticaster

初始化上下文事件广播器,如果不存在ApplicationEventMulticaster 则使用默认SimpleApplicationEventMulticaster。

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
      this.applicationEventMulticaster =
          beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
      if (logger.isTraceEnabled()) {
        logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
      }
    }
    else {
      this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
      beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
      if (logger.isTraceEnabled()) {
        logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
            "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
      }
    }
  }

注意,在这之前,事情发布机制都是通过SpringApplicationRunListener的实现类EventPublishingRunListener来完成的,后者内部有一个ApplicationEventMulticaster进行事件发布给ApplicationListener。在这个方法中,直接使用this.applicationEventMulticaster=赋值了一个应用事件广播器。

⑧ onRefresh()

ServletWebServerApplicationContext.onRefresh()方法源码如下:

protected void onRefresh() {
//调用父类onRefresh()方法
    super.onRefresh();
    try {
    //创建WebServer
      createWebServer();
    }
    catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
    }
  }
/**
   * Initialize the theme capability.
   */
  @Override
  protected void onRefresh() {
    this.themeSource = UiApplicationContextUtils.initThemeSource(this);
  }
/**
   * Initialize the ThemeSource for the given application context,
   * autodetecting a bean with the name "themeSource". If no such
   * bean is found, a default (empty) ThemeSource will be used.
   * @param context current application context
   * @return the initialized theme source (will never be {@code null})
   * @see #THEME_SOURCE_BEAN_NAME
   */
  public static ThemeSource initThemeSource(ApplicationContext context) {
    if (context.containsLocalBean(THEME_SOURCE_BEAN_NAME)) {
      ThemeSource themeSource = context.getBean(THEME_SOURCE_BEAN_NAME, ThemeSource.class);
      // Make ThemeSource aware of parent ThemeSource.
      if (context.getParent() instanceof ThemeSource && themeSource instanceof HierarchicalThemeSource) {
        HierarchicalThemeSource hts = (HierarchicalThemeSource) themeSource;
        if (hts.getParentThemeSource() == null) {
          // Only set parent context as parent ThemeSource if no parent ThemeSource
          // registered already.
          hts.setParentThemeSource((ThemeSource) context.getParent());
        }
      }
      if (logger.isDebugEnabled()) {
        logger.debug("Using ThemeSource [" + themeSource + "]");
      }
      return themeSource;
    }
    else {
      // Use default ThemeSource to be able to accept getTheme calls, either
      // delegating to parent context's default or to local ResourceBundleThemeSource.
      HierarchicalThemeSource themeSource = null;
      if (context.getParent() instanceof ThemeSource) {
        themeSource = new DelegatingThemeSource();
        themeSource.setParentThemeSource((ThemeSource) context.getParent());
      }
      else {
        themeSource = new ResourceBundleThemeSource();
      }
      if (logger.isDebugEnabled()) {
        logger.debug("Unable to locate ThemeSource with name '" + THEME_SOURCE_BEAN_NAME +
            "': using default [" + themeSource + "]");
      }
      return themeSource;
    }
  }

ServletWebServerApplicationContext.createWebServer()方法如下:

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
      ServletWebServerFactory factory = getWebServerFactory();
      this.webServer = factory.getWebServer(getSelfInitializer());
    }
    else if (servletContext != null) {
      try {
        getSelfInitializer().onStartup(servletContext);
      }
      catch (ServletException ex) {
        throw new ApplicationContextException("Cannot initialize servlet context",
            ex);
      }
    }
    initPropertySources();
  }

判断ServletContext不为null,就会调用getSelfInitializer().onStartup(servletContext);

private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    return this::selfInitialize;
  }
private void selfInitialize(ServletContext servletContext) throws ServletException {
    prepareWebApplicationContext(servletContext);
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
        beanFactory);
    WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
        getServletContext());
    existingScopes.restore();
    WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
        getServletContext());
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
      beans.onStartup(servletContext);
    }
  }

如下图所示,在末尾循环遍历ServletContextInitializerBean,这里有6个,分别注册了Servlet和Filter。

⑨ registerListeners();

添加实现了ApplicationListener 接口的bean作为监听器,并不影响其他监听器。

/**
   * Add beans that implement ApplicationListener as listeners.
   * Doesn't affect other listeners, which can be added without being beans.
   */
  protected void registerListeners() {
    // Register statically specified listeners first.
    for (ApplicationListener<?> listener : getApplicationListeners()) {
      getApplicationEventMulticaster().addApplicationListener(listener);
    }
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let post-processors apply to them!
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
      getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }
    // Publish early application events now that we finally have a multicaster...
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (earlyEventsToProcess != null) {
      for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
        getApplicationEventMulticaster().multicastEvent(earlyEvent);
      }
    }
  }

⑩ finishBeanFactoryInitialization

  • 设置转换器
  • 注册一个默认的属性值解析器
  • 冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理
  • 初始化剩余的非惰性的bean,即初始化非延迟加载的bean
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // Initialize conversion service for this context.
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
        beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
      beanFactory.setConversionService(
          beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    }
    // Register a default embedded value resolver if no bean post-processor
    // (such as a PropertyPlaceholderConfigurer bean) registered any before:
    // at this point, primarily for resolution in annotation attribute values.
    if (!beanFactory.hasEmbeddedValueResolver()) {
      beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    }
    // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    for (String weaverAwareName : weaverAwareNames) {
      getBean(weaverAwareName);
    }
    // Stop using the temporary ClassLoader for type matching.
    beanFactory.setTempClassLoader(null);
    // Allow for caching all bean definition metadata, not expecting further changes.
    beanFactory.freezeConfiguration();
    // Instantiate all remaining (non-lazy-init) singletons.
    beanFactory.preInstantiateSingletons();
  }

完成上下文刷新,调用LifecycleProcessor的onRefresh()方法并发布ContextRefreshedEvent事件。

/**
   * Finish the refresh of this context, invoking the LifecycleProcessor's
   * onRefresh() method and publishing the
   * {@link org.springframework.context.event.ContextRefreshedEvent}.
   */
  protected void finishRefresh() {
    // Clear context-level resource caches (such as ASM metadata from scanning).
    clearResourceCaches();
    // Initialize lifecycle processor for this context.
    initLifecycleProcessor();
    // Propagate refresh to lifecycle processor first.
    getLifecycleProcessor().onRefresh();
    // Publish the final event.
    publishEvent(new ContextRefreshedEvent(this));
    // Participate in LiveBeansView MBean, if active.
    LiveBeansView.registerApplicationContext(this);
  }

【10】SpringApplication.run方法详细分析-afterRefresh


目录
相关文章
|
4天前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
19 0
|
8天前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
19 2
 SpringBoot入门(7)- 配置热部署devtools工具
|
7天前
|
Java 数据库连接
SpringBoot配置多数据源实战
第四届光学与机器视觉国际学术会议(ICOMV 2025) 2025 4th International Conference on Optics and Machine Vision
33 8
|
4天前
|
Java 数据库连接 数据库
springboot启动配置文件-bootstrap.yml常用基本配置
以上是一些常用的基本配置项,在实际应用中可能会根据需求有所变化。通过合理配置 `bootstrap.yml`文件,可以确保应用程序在启动阶段加载正确的配置,并顺利启动运行。
11 2
|
16天前
|
Java Spring 容器
SpringBoot读取配置文件的6种方式,包括:通过Environment、@PropertySource、@ConfigurationProperties、@Value读取配置信息
SpringBoot读取配置文件的6种方式,包括:通过Environment、@PropertySource、@ConfigurationProperties、@Value读取配置信息
43 3
|
Java 应用服务中间件 Apache
springboot+maven+tomcat问题
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_26654727/article/details/78456345 问题背景 版本介绍 jdk1.7 、springboot:1.3.1.RELEASE、tomcat8 、maven3 解决过程 1. 搭建Springboot+jsp项目。
2356 0
|
1月前
|
安全 应用服务中间件 网络安全
Tomcat如何配置PFX证书?
【10月更文挑战第2天】Tomcat如何配置PFX证书?
182 7
|
1月前
|
存储 算法 应用服务中间件
Tomcat如何配置JKS证书?
【10月更文挑战第2天】Tomcat如何配置JKS证书?
257 4
|
3月前
|
网络协议 Java 应用服务中间件
tomcat配置域名及HTTPS
tomcat配置域名及HTTPS
|
3月前
|
Java 应用服务中间件 Windows
【应用服务 App Service】App Service 中部署Java项目,查看Tomcat配置及上传自定义版本
【应用服务 App Service】App Service 中部署Java项目,查看Tomcat配置及上传自定义版本