20.SpringBoot源码解读

SpringBoot源码解读

SringBoot源码解析

Application启动类

SpringbootDemoApplication这个启动类既可以通过main方法启动,也可以通过部署中间件,例如tomcat启动

//方式一:通过java -jar 启动
@SpringBootApplication
public class SpringbootDemoApplication{

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemoApplication.class, args);
    }
}
//方式二:部署中间件
@SpringBootApplication
public class SpringbootDemoApplication extends SpringBootServletInitializer{

     @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(SpringbootDemoApplication.class);
    }
}
//方式三:同时支持java -jar 和部署中间件
@SpringBootApplication
public class SpringbootDemoApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemoApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(SpringbootDemoApplication.class);
    }
}
@SpringBootApplication注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
//没有指定包名,默认是加这个注解的类所在包
public @interface SpringBootApplication {
    //......
}

@SpringBootApplication注解继承两部分

  • 一部分:元注解
  • 另一部分:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan
入口核心方法 SpringApplication.run()

image-20220704220428036

进入SpringApplication.run()方法,这是一个静态方法,返回值为ConfigurableApplicationContext,如下:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
   return run(new Class<?>[] { primarySource }, args);
}

这个run方法内部接着调用另一个run方法,其中class参数转换为class数组,如下:

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   return new SpringApplication(primarySources).run(args);
}

此时,内部是构建一个SpringApplication对象,接着调用SpringApplication对象的run方法

SpringApplication构造函数解读

public SpringApplication(Class<?>... primarySources) {
   this(null, primarySources);
}
/**
一:注释上的重要信息:
	1、primary sources:application context从primarySource加载beans 
	2、创建的SpringApplication实例在调用它的run(String...)方法前,可对其进行定制化设置 可以进行哪些设置?看类中提供的public方法。
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details). The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    //
    this.resourceLoader = resourceLoader;
    //1.一定指定primarySources
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //2.this.primarySources赋值
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //3.推断WebApplication的类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //4.通过SPI机制从META-INF/spring.factories中获取BootstrapRegistryInitializer
    this.bootstrapRegistryInitializers = new ArrayList<>(
        getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    //5.通过SPI机制从META-INF/spring.factories中获取ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //6.通过SPI机制从META-INF/spring.factories中获取ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //7.推断执行的main方法的定义类
    this.mainApplicationClass = deduceMainApplicationClass();
}

/*
*推断WebApplication的类型:NONE、SERVLET、REACTIVE 三种类型
*/
static WebApplicationType deduceFromClasspath() {
    //包含org.springframework.web.reactive.DispatcherHandler,并且不包含org.springframework.web.servlet.DispatcherServlet和org.glassfish.jersey.servlet.ServletContainer
    //则:REACTIVE
    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
        && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
  //SERVLET_INDICATOR_CLASSES
  //["javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext"]
 //循环都不匹配,返回NONE,也就是普通ApplicationContext
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    //最后返回SERVLET,也就是Servlet的容器
    return WebApplicationType.SERVLET;
}

/**
*推断主类
*/
private Class<?> deduceMainApplicationClass() {
    try {
        //获取运行到当前方法,之前的堆栈信息
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        //遍历堆栈信息中每一个堆栈信息
        for (StackTraceElement stackTraceElement : stackTrace) {
            //堆栈是main方法的,就获取当前方法的类,当做主类返回
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}
getSpringFactoriesInstances

获取spring.factories中实例

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
   return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    // 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;
}

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
   ClassLoader classLoaderToUse = classLoader;
   if (classLoaderToUse == null) {
      classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
   }
    //获取class类的全限定名
   String factoryTypeName = factoryType.getName();
    //加载spring.factories中信息,并返回指定类型的名称列表
   return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
loadSpringFactories
public final class SpringFactoriesLoader {

   /**
    * The location to look for factories.
    * <p>Can be present in multiple JAR files.
    * spring.factories的资源路径
    * 可以存在于多个jar包中
    */
   public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";


   private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);

   static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();


   private SpringFactoriesLoader() {
   }

   public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
      Assert.notNull(factoryType, "'factoryType' must not be null");
      ClassLoader classLoaderToUse = classLoader;
      if (classLoaderToUse == null) {
         classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
      }
      List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
      if (logger.isTraceEnabled()) {
         logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
      }
      List<T> result = new ArrayList<>(factoryImplementationNames.size());
      for (String factoryImplementationName : factoryImplementationNames) {
         result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
      }
      AnnotationAwareOrderComparator.sort(result);
      return result;
   }

   public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
      ClassLoader classLoaderToUse = classLoader;
      if (classLoaderToUse == null) {
         classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
      }
      String factoryTypeName = factoryType.getName();
      return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
   }

   private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
      Map<String, List<String>> result = cache.get(classLoader);
      if (result != null) {
         return result;
      }

      result = new HashMap<>();
      try {
          //从多个jar包中,加载META-INF/spring.factories,返回多个url
         Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
          //迭代
         while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
             //获取UrlResource
            UrlResource resource = new UrlResource(url);
             //将UrlResource转换为Properties(key-value)
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
             //遍历Properties中的key-value值
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                //获取key值,factory的类的全限定名称
               String factoryTypeName = ((String) entry.getKey()).trim();
                //获取factory类的实现类的类全限定名,并按照“,”分割,返回数组
               String[] factoryImplementationNames =
                     StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                //遍历factory类的实现类的类全限定名数组,
                //按照key(factory的类的全限定名称)-value(factory类的实现类的类全限定名)
               for (String factoryImplementationName : factoryImplementationNames) {
                  result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                        .add(factoryImplementationName.trim());
               }
            }
         }

         // Replace all lists with unmodifiable lists containing unique elements
          //去重
         result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
               .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
         cache.put(classLoader, result);
      }
      catch (IOException ex) {
         throw new IllegalArgumentException("Unable to load factories from location [" +
               FACTORIES_RESOURCE_LOCATION + "]", ex);
      }
      return result;
   }

   @SuppressWarnings("unchecked")
   private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
      try {
         Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
         if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
            throw new IllegalArgumentException(
                  "Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
         }
         return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
      }
      catch (Throwable ex) {
         throw new IllegalArgumentException(
            "Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
            ex);
      }
   }

}
BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationListener

image-20220704224837469

BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationListener这三个都是做什么的?

BootstrapRegistryInitializer

springboot从各jar包下META-INF/spring.factories中获取org.springframework.boot.BootstrapRegistryInitializer对应的实现类信息

BootstrapRegistryInitializer接口信息如下:

image-20220704225213471

ApplicationContextInitializer

springboot从各jar包下META-INF/spring.factories中获取org.springframework.context.ApplicationContextInitializer对应的实现类信息

ApplicationContextInitializer这个接口信息如下:

用于初始化spring 容器的回调接口,再被容器ConfigurableApplicationContext#refresh()刷新之前,通常在web应用程序中使用,需要一些程序初始化的应用程序上下文。例如:注册属性源或激活配置文件

@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

   /**
    * Initialize the given application context.
    * @param applicationContext the application to configure
    */
   void initialize(C applicationContext);

}
ApplicationListener

springboot从各jar包下META-INF/spring.factories中获取org.springframework.context.ApplicationListener对应的实现类信息

用于应用程序来实现的监听器的接口,基于标准的java.util.EventListener,用于Observer(观察者设计模式)

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

   /**
    * Handle an application event.
    * @param event the event to respond to
    */
   void onApplicationEvent(E event);
   /**
    * Create a new {@code ApplicationListener} for the given payload consumer.
    * @param consumer the event payload consumer
    * @param <T> the type of the event payload
    * @return a corresponding {@code ApplicationListener} instance
    * @since 5.3
    * @see PayloadApplicationEvent
    */
   static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
      return event -> consumer.accept(event.getPayload());
   }

}

SpringApplication.run()实例方法解读

SpringApplication.run()方法相当于ApplicatonContext#refresh()方法一样重要

/**
 *	运行Spring 容器,创建和刷新一个新的ApplicationContext
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
   //1.开始时间
   long startTime = System.nanoTime();
   //2.创建BootstrapContext,并回调BootstrapRegistryInitializer#initialize方法
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   ConfigurableApplicationContext context = null;
    //3.配置Headless的属性
   configureHeadlessProperty();
    //4.获取SpringApplicationRunListeners
   SpringApplicationRunListeners listeners = getRunListeners(args);
    //5.调用SpringApplicationRunListener的starting方法
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
       //6.把命令行参数包装为ApplicationArguments
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
       //7.准备Enviroment,此时JVM系统参数、系统环境变量、命令行参数、项目中配置文件参数都会被加载处理
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      configureIgnoreBeanInfo(environment);
       //8.打印SpringBoot  LOGO图标
      Banner printedBanner = printBanner(environment);
       //9.创建ApplicationContext
      context = createApplicationContext();
       
      context.setApplicationStartup(this.applicationStartup);
       //10.准备ApplicationContext
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
       //11.刷新ApplicationContext
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
      }
       //12.发布事件
      listeners.started(context, timeTakenToStartup);
       //13.执行所有的Runners
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }
   try {
      Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
       //14.发布事件
      listeners.ready(context, timeTakenToReady);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}
configureHeadlessProperty()

配置系统属性java.awt.headless的值为true

private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private boolean headless = true;
private void configureHeadlessProperty() {
   System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
         System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
getRunListeners(args)

返回SpringApplicationRunListener的包装类,SpringApplicationRunListener类用来监听SpringApplicaton的run方法的执行过程

private SpringApplicationRunListeners getRunListeners(String[] args) {
    //SpringApplication作为参数,传递给SpringApplicationRunListeners构造函数
   Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    //从jar包下META-INF/spring.factories中获取org.springframework.boot.SpringApplicationRunListener对应的实现类信息,并包装到SpringApplicationRunListeners类中的列表里
   return new SpringApplicationRunListeners(logger,
         getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
         this.applicationStartup);
}

image-20220706065325164

DefaultApplicationArguments

把命令行参数包装为ApplicationArguments

public DefaultApplicationArguments(String... args) {
   Assert.notNull(args, "Args must not be null");
   this.source = new Source(args);
   this.args = args;
}
prepareEnvironment环境准备
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
   // Create and configEnvironmenture the environment
    //获取或者创建Environment
   ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 配置环境:加入命令行参数PropertySource,配置启用的profiles
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   ConfigurationPropertySources.attach(environment);
    // 触发RunListener环境准备完成回调
   listeners.environmentPrepared(bootstrapContext, environment);
   DefaultPropertiesPropertySource.moveToEnd(environment);
   Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
         "Environment prefix cannot be set via properties.");
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
      environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
   }
   ConfigurationPropertySources.attach(environment);
   return environment;
}

getOrCreateEnvironment()

获取或者创建Environment

//获取或者创建Environment
private ConfigurableEnvironment getOrCreateEnvironment() {
    //存在,直接返回
    if (this.environment != null) {
        return this.environment;
    }
    //this.webApplicationType = SERVLET
    switch (this.webApplicationType) {
        case SERVLET:
            return new ApplicationServletEnvironment();
        case REACTIVE:
            return new ApplicationReactiveWebEnvironment();
        default:
            return new ApplicationEnvironment();
    }
}
new ApplicationServletEnvironment()

new ApplicationServletEnvironment(),调用过程如下

//第一步:调用StandardServletEnvironment构造函数
public StandardServletEnvironment() {
}
//第二步:调用StandardEnvironment构造函数
public StandardEnvironment() {
}
//第三步:调用AbstractEnvironment构造函数
public AbstractEnvironment() {
    this(new MutablePropertySources());
}
//第四步:调用AbstractEnvironment的带参MutablePropertySources构造函数
protected AbstractEnvironment(MutablePropertySources propertySources) {
    //属性源
    this.propertySources = propertySources;
    //创建属性解析器
    this.propertyResolver = createPropertyResolver(propertySources);
    //自定义属性源
    customizePropertySources(propertySources);
}
//第五步:调用AbstractEnvironment的customizePropertySources
protected void customizePropertySources(MutablePropertySources propertySources) {
}
//第六步:调用StandardServletEnvironment的customizePropertySources
protected void customizePropertySources(MutablePropertySources propertySources) {
    //在队尾添加servletConfigInitParams config初始化参数,占位
    propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
    //在队尾添加servletContextInitParams context初始化参数,占位
    propertySources.addLast(new StubPropertySource("servletContextInitParams"));
    //在队尾添加jndiProperties参数,占位
    if (jndiPresent && JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
        propertySources.addLast(new JndiPropertySource("jndiProperties"));
    }
	//调用父类customizePropertySources
    super.customizePropertySources(propertySources);
}
//第七步:调用StandardEnvironment的customizePropertySources
/** System environment property source name: {@value}. */
/**	系统环境变量 */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

/** JVM system properties property source name: {@value}. */
/**	JVM系统属性 */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

protected void customizePropertySources(MutablePropertySources propertySources) {
    //在队尾添加JVM系统属性,getSystemProperties()获取JVM系统属性
    propertySources.addLast(
        new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
     //在队尾添加系统环境变量,getSystemProperties()获取系统环境变量
    propertySources.addLast(
        new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
getSystemProperties()获取JVM系统属性

image-20220705224835269

getSystemProperties()获取系统环境变量

image-20220705224906528

ApplicationServletEnvironment继承体系

image-20220705225358273

listeners.environmentPrepared(bootstrapContext, environment)

调用环境准备事件,上文中从spring.factories中获取到org.springframework.boot.context.event.EventPublishingRunListener,执行它的environmentPrepared方法,发布一个事件ApplicationEnvironmentPreparedEvent事件

void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
   doWithListeners("spring.boot.application.environment-prepared",
         (listener) -> listener.environmentPrepared(bootstrapContext, environment));
}
/*EventPublishingRunListener#environmentPrepared*/
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
                                ConfigurableEnvironment environment) {
    this.initialMulticaster.multicastEvent(
        new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}
//SpringApplicationEvent事件
public class ApplicationContextInitializedEvent extends SpringApplicationEvent {
    //......
}

然后上文中获取到的ApplicationListener监听事件,如下,找到org.springframework.boot.env.EnvironmentPostProcessorApplicationListener处理环境的监听器

image-20220706072705305

EnvironmentPostProcessorApplicationListener

进入onApplicationEvent方法

image-20220706073001737

发布的是ApplicationEnvironmentPreparedEvent这个类型的事件,因此执行onApplicationEnvironmentPreparedEvent方法

image-20220706073052849

EnvironmentPostProcessor

获取EnvironmentPostProcessor类型的环境处理器,如下

image-20220706073342229

ConfigDataEnvironmentPostProcessor

SpringBoot2.4之后用的版本使用ConfigDataEnvironmentPostProcessor来处理配置信息

加载配置数据到Spring的Environment中

/**
 * {@link EnvironmentPostProcessor} that loads and applies {@link ConfigData} to Spring's
 * {@link Environment}.
 * 加载配置数据到Spring的Environment中
 * @author Phillip Webb
 * @author Madhura Bhave
 * @author Nguyen Bao Sach
 * @since 2.4.0
 */
public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
    //......
    
    ConfigDataEnvironment getConfigDataEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,Collection<String> additionalProfiles) {
       //构建ConfigDataEnvironment
        return new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, environment, resourceLoader,additionalProfiles, this.environmentUpdateListener);
    }
}
ConfigDataEnvironment

image-20220706074104075

ConfigFileApplicationListener

SpringBoot2.4之前用的版本使用ConfigFileApplicationListener来处理配置信息

public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {

    // 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/*/,file:./config/";

    private static final String DEFAULT_NAMES = "application";
    /**
     * The "active profiles" property name.
     */
    public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
    /**
     * The "includes profiles" property name.
     */
    public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
    /**
     * The "config name" property name.
     */
    public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
    /**
     * The "config location" property name.
     */
    public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
    /**
     * The "config additional location" property name.
     */
    public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
    
    //..........................
}
createApplicationContext创建容器

image-20220706074940668

根据webApplicationType类型创建AnnotationConfigServletWebServerApplicationContext容器,并返回没有初始化的

static class Factory implements ApplicationContextFactory {

   @Override
   public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
      return (webApplicationType != WebApplicationType.SERVLET) ? null
            : new AnnotationConfigServletWebServerApplicationContext();
   }

}
prepareContext准备容器

针对返回的AnnotationConfigServletWebServerApplicationContext容器处理,如下

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
    //1.设置容器的环境
   context.setEnvironment(environment);
    //2.设置ApplicationContext的beanNameGenerator、resourceLoader、 addConversionService(如果通过SpringApplication编程方式指定了它们)
   postProcessApplicationContext(context);
    //3.应用初始化器对ApplicationContext进行初始化处理(Initializers在构造 SpringApplication时就从spring.factories中加载到了)
   applyInitializers(context);
    //4.发布ApplicatonContext准备事件
   listeners.contextPrepared(context);
    //5.关闭事件
   bootstrapContext.close(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // Add boot specific singleton beans
    // 6.添加例 bean 到 beanFactory中
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
      ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
      if (beanFactory instanceof DefaultListableBeanFactory) {
         ((DefaultListableBeanFactory) beanFactory)
               .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
      }
   }
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
   // Load the sources
    //7.加载所有的资源,加载bean定义
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   load(context, sources.toArray(new Object[0]));
    //8.发布容器加载bean事件
   listeners.contextLoaded(context);
}
load加载bean定义

进入加载所有资源的方法,如下:

private void load(Object source) {
   Assert.notNull(source, "Source must not be null");
    //针对类,Annotation注解方式
   if (source instanceof Class<?>) {
      load((Class<?>) source);
      return;
   }
    //针对Resource,xml配置
   if (source instanceof Resource) {
      load((Resource) source);
      return;
   }
   //针对包,sanner扫描
   if (source instanceof Package) {
      load((Package) source);
      return;
   }
    //针对字符串,转换为类,resource,包
   if (source instanceof CharSequence) {
      load((CharSequence) source);
      return;
   }
   throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
refreshContext刷新容器
private void refreshContext(ConfigurableApplicationContext context) {
   if (this.registerShutdownHook) {
      shutdownHook.registerApplicationContext(context);
   }
   refresh(context);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
    applicationContext.refresh();
}

@Override
public final void refresh() throws BeansException, IllegalStateException {
    try {
        super.refresh();
    }
    catch (RuntimeException ex) {
        WebServer webServer = this.webServer;
        if (webServer != null) {
            webServer.stop();
        }
        throw ex;
    }
}

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            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();
            contextRefresh.end();
        }
    }
}
EnableAutoConfiguration自动配置bean的加载

问题

​ 1. META-INF/spring.factories中指定的auto configuration Bean定义在哪里完成的加载?

​ 2.整个过程的代码已看完,没看到这块代码,代码茫茫,如何找到加载它们的代码?

​ 3.一定会从META-INF/spring.factories中加载,加载的方法是哪个?

前面的代码中已见过从META-INF/spring.factories中加载其他的:

image-20220708073816691

SpringFactoriesLoader.loadFactoryNames(type, classLoader)

关键方法,进入此方法打断点,跟踪interface org.springframework.boot.autoconfigure.EnableAutoConfiguration这个值,此时查看下调用栈

image-20220708073911931

调用栈信息

image-20220708074230867

从上述的调用栈信息的得到,EnableAutoConfiguration自动配置的bean加载是ConfigurationClassPostProcessor中处理的

@EnableAutoConfiguration

其实,秘密在@SpringBootApplication复合的 @EnableAutoConfiguration 注解,这个注解中继承了@Import注解,这个关键点导入AutoConfigurationImportSelector

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

   /**
    * Environment property that can be used to override when auto-configuration is
    * enabled.
    */
   String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

   /**
    * Exclude specific auto-configuration classes such that they will never be applied.
    * @return the classes to exclude
    */
   Class<?>[] exclude() default {};

   /**
    * Exclude specific auto-configuration class names such that they will never be
    * applied.
    * @return the class names to exclude
    * @since 1.3.0
    */
   String[] excludeName() default {};

}
AutoConfigurationImportSelector
AutoConfigurationImportSelector类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
      ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
          //......
      }
AutoConfigurationImportSelector的继承体系

image-20220708075158133

重点说明:AutoConfigurationImportSelector 实现了 DeferredImportSelector,DeferredImportSelector 是延迟导入选择器。所谓延迟:在所有指定的、包扫描到的@Configuration类中的 bean定义注册后,才会来处理延迟导入的@Configuration类

为什么实现了DeferredImportSelector就延迟导入

此时就需要看下ConfigurationClassPostProcessor,如下看到,先解析主入口类@SpringApplication注解的类

image-20220708075619789

ConfigurationClassParser#parse(Set)parse方法

进入parse方法,先执行正常导入的方法,然后最后执行延迟导入的方法,

image-20220708075834676

看下正常导入的parse方法

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
   processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}

image-20220708080124482

doProcessConfigurationClass方法

/**
 * Apply processing and build a complete {@link ConfigurationClass} by reading the
 * annotations, members and methods from the source class. This method can be called
 * multiple times as relevant sources are discovered.
 * @param configClass the configuration class being build
 * @param sourceClass a source class
 * @return the superclass, or {@code null} if none found or previously processed
 */
@Nullable
protected final SourceClass doProcessConfigurationClass(
      ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
      throws IOException {

   if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
      // Recursively process any member (nested) classes first
      processMemberClasses(configClass, sourceClass, filter);
   }

   // Process any @PropertySource annotations
   for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), PropertySources.class,
         org.springframework.context.annotation.PropertySource.class)) {
      if (this.environment instanceof ConfigurableEnvironment) {
         processPropertySource(propertySource);
      }
      else {
         logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
               "]. Reason: Environment must implement ConfigurableEnvironment");
      }
   }

   // Process any @ComponentScan annotations
   Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
   if (!componentScans.isEmpty() &&
         !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
      for (AnnotationAttributes componentScan : componentScans) {
         // The config class is annotated with @ComponentScan -> perform the scan immediately
         Set<BeanDefinitionHolder> scannedBeanDefinitions =
               this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
         // Check the set of scanned definitions for any further config classes and parse recursively if needed
         for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
            if (bdCand == null) {
               bdCand = holder.getBeanDefinition();
            }
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
               parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
         }
      }
   }

   // Process any @Import annotations
   processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

   // Process any @ImportResource annotations
   AnnotationAttributes importResource =
         AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
   if (importResource != null) {
      String[] resources = importResource.getStringArray("locations");
      Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
      for (String resource : resources) {
         String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
         configClass.addImportedResource(resolvedResource, readerClass);
      }
   }

   // Process individual @Bean methods
   Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
   for (MethodMetadata methodMetadata : beanMethods) {
      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
   }

   // Process default methods on interfaces
   processInterfaces(configClass, sourceClass);

   // Process superclass, if any
   if (sourceClass.getMetadata().hasSuperClass()) {
      String superclass = sourceClass.getMetadata().getSuperClassName();
      if (superclass != null && !superclass.startsWith("java") &&
            !this.knownSuperclasses.containsKey(superclass)) {
         this.knownSuperclasses.put(superclass, configClass);
         // Superclass found, return its annotation metadata and recurse
         return sourceClass.getSuperClass();
      }
   }

   // No superclass -> processing is complete
   return null;
}

找到我们关注的处理注解@Import部分

image-20220708080227694

进入processImports方法

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
      boolean checkForCircularImports) {

   if (importCandidates.isEmpty()) {
      return;
   }

   if (checkForCircularImports && isChainedImportOnStack(configClass)) {
      this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
   }
   else {
      this.importStack.push(configClass);
      try {
         for (SourceClass candidate : importCandidates) {
             //1.第一种:处理导入的类是ImportSelector
            if (candidate.isAssignable(ImportSelector.class)) {
               // Candidate class is an ImportSelector -> delegate to it to determine imports
               Class<?> candidateClass = candidate.loadClass();
               ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                     this.environment, this.resourceLoader, this.registry);
               Predicate<String> selectorFilter = selector.getExclusionFilter();
               if (selectorFilter != null) {
                  exclusionFilter = exclusionFilter.or(selectorFilter);
               }
                //1-1.处理的导入类是DeferredImportSelector
               if (selector instanceof DeferredImportSelector) {
                   //只是简单缓存起来,在这不做处理
                  this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
               }
                //1-2.处理的导入类不是DeferredImportSelector
               else {
                  String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                  Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                  processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
               }
            }
              //2.第二种:处理导入的类是ImportBeanDefinitionRegistrar
            else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
               // Candidate class is an ImportBeanDefinitionRegistrar ->
               // delegate to it to register additional bean definitions
               Class<?> candidateClass = candidate.loadClass();
               ImportBeanDefinitionRegistrar registrar =
                     ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                           this.environment, this.resourceLoader, this.registry);
               configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
            }
             //3.第三种:处理导入的类是其他的
            else {
               // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
               // process it as an @Configuration class
               this.importStack.registerImport(
                     currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
               processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
            }
         }
      }
      catch (BeanDefinitionStoreException ex) {
         throw ex;
      }
      catch (Throwable ex) {
         throw new BeanDefinitionStoreException(
               "Failed to process import candidates for configuration class [" +
               configClass.getMetadata().getClassName() + "]", ex);
      }
      finally {
         this.importStack.pop();
      }
   }
}

AutoConfigurationImportSelector的继承体系中,继承自DeferredImportSelector因此,正常处理道理的流程中只是缓存起来,暂不处理,等到正常的导入bean 的流程处理完毕后,回到如下图的位置:

image-20220708080853039

进入process
public void process() {
   List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
   this.deferredImportSelectors = null;
   try {
      if (deferredImports != null) {
         DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
         deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
         deferredImports.forEach(handler::register);
         handler.processGroupImports();
      }
   }
   finally {
      this.deferredImportSelectors = new ArrayList<>();
   }
}
handler.processGroupImports
public void processGroupImports() {
   for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
      Predicate<String> exclusionFilter = grouping.getCandidateFilter();
       //获取要导入的类,遍历解析,关键看 grouping.getImports()
      grouping.getImports().forEach(entry -> {
         ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
         try {
            processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
                  Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
                  exclusionFilter, false);
         }
         catch (BeanDefinitionStoreException ex) {
            throw ex;
         }
         catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                  "Failed to process import candidates for configuration class [" +
                        configurationClass.getMetadata().getClassName() + "]", ex);
         }
      });
   }
}
grouping.getImports()
public Iterable<Group.Entry> getImports() {
   for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
       //延迟导入类,处理
      this.group.process(deferredImport.getConfigurationClass().getMetadata(),
            deferredImport.getImportSelector());
   }
   return this.group.selectImports();
}
this.group.process
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
   Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
         () -> String.format("Only %s implementations are supported, got %s",
               AutoConfigurationImportSelector.class.getSimpleName(),
               deferredImportSelector.getClass().getName()));
    //从META-INF/spring.factories中获取自动配置实体
   AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
         .getAutoConfigurationEntry(annotationMetadata);
   this.autoConfigurationEntries.add(autoConfigurationEntry);
   for (String importClassName : autoConfigurationEntry.getConfigurations()) {
      this.entries.putIfAbsent(importClassName, annotationMetadata);
   }
}
getAutoConfigurationEntry
/**
 * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
 * of the importing {@link Configuration @Configuration} class.
 * @param annotationMetadata the annotation metadata of the configuration class
 * @return the auto-configurations that should be imported
 */
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //从META-INF/spring.factories中获取自动配置的类
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   configurations = removeDuplicates(configurations);
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
   configurations = getConfigurationClassFilter().filter(configurations);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    //从META-INF/spring.factories中获取自动配置的类EnableAutoConfiguration对应的配置类
    List<String> configurations = new ArrayList<>(
        SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
    ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
    Assert.notEmpty(configurations,
                    "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
                    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}
//获取EnableAutoConfiguration
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}
Spring Boot的自动装配中的@ConditionalOnBean条件装配注解在Spring启动过程中,是如何保证处理顺序靠后的

https://i-blog.csdnimg.cn/blog_migrate/ea78018f17b5b9a95cf091109a38d7f7.png

Tomcat是如何启动的

Tomcat创建
ApplicatonContext#refresh

在ApplicationContext的refresh刷新的方法中,有个onRefresh方法,我们看到onRefresh()方法是调用其子类的实现,根据我们上文的分析,我们这里的子类是ServletWebServerApplicationContext

image-20220709143702456

ServletWebServerApplicationContext#onRefresh

AbstractApplicationContext的实现ServletWebServerApplicationContext#onRefresh中,

image-20220709143748177

createWebServer()

createWebServer()就是启动web服务,但是还没有真正启动Tomcat,既然webServer是通过ServletWebServerFactory来获取的,那就来看看这个工厂的真面目。

private void createWebServer() {
   WebServer webServer = this.webServer;
   ServletContext servletContext = getServletContext();
   if (webServer == null && servletContext == null) {
      StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
      ServletWebServerFactory factory = getWebServerFactory();
      createWebServer.tag("factory", factory.getClass().toString());
      this.webServer = factory.getWebServer(getSelfInitializer());
      createWebServer.end();
      getBeanFactory().registerSingleton("webServerGracefulShutdown",
            new WebServerGracefulShutdownLifecycle(this.webServer));
      getBeanFactory().registerSingleton("webServerStartStop",
            new WebServerStartStopLifecycle(this, this.webServer));
   }
   else if (servletContext != null) {
      try {
         getSelfInitializer().onStartup(servletContext);
      }
      catch (ServletException ex) {
         throw new ApplicationContextException("Cannot initialize servlet context", ex);
      }
   }
   initPropertySources();
}

根据上图,我们发现工厂类是一个接口,各个具体服务的实现是由各个子类来完成的,所以,就去看看TomcatServletWebServerFactory.getWebServer()的实现。

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
   if (this.disableMBeanRegistry) {
      Registry.disableRegistry();
   }
    //创建Tomcat实例
   Tomcat tomcat = new Tomcat();
   File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
   tomcat.setBaseDir(baseDir.getAbsolutePath());
   for (LifecycleListener listener : this.serverLifecycleListeners) {
      tomcat.getServer().addLifecycleListener(listener);
   }
   Connector connector = new Connector(this.protocol);
   connector.setThrowOnFailure(true);
   tomcat.getService().addConnector(connector);
   customizeConnector(connector);
   tomcat.setConnector(connector);
   tomcat.getHost().setAutoDeploy(false);
   configureEngine(tomcat.getEngine());
   for (Connector additionalConnector : this.additionalTomcatConnectors) {
      tomcat.getService().addConnector(additionalConnector);
   }
   prepareContext(tomcat.getHost(), initializers);
    //关键返回TomcatWebServer对象
   return getTomcatWebServer(tomcat);
}
Tomcat启动
getTomcatWebServer(tomcat)
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
   return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
TomcatWebServer
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
   Assert.notNull(tomcat, "Tomcat Server must not be null");
   this.tomcat = tomcat;
   this.autoStart = autoStart;
   this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
    //初始化
   initialize();
}
初始化并启动Tomcat
private void initialize() throws WebServerException {
   logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
   synchronized (this.monitor) {
      try {
         addInstanceIdToEngineName();

         Context context = findContext();
         context.addLifecycleListener((event) -> {
            if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
               // Remove service connectors so that protocol binding doesn't
               // happen when the service is started.
               removeServiceConnectors();
            }
         });

         // Start the server to trigger initialization listeners
          //启动Tomcat
         this.tomcat.start();

         // We can re-throw failure exception directly in the main thread
         rethrowDeferredStartupExceptions();

         try {
            ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
         }
         catch (NamingException ex) {
            // Naming is not enabled. Continue
         }

         // Unlike Jetty, all Tomcat threads are daemon threads. We create a
         // blocking non-daemon to stop immediate shutdown
         startDaemonAwaitThread();
      }
      catch (Exception ex) {
         stopSilently();
         destroySilently();
         throw new WebServerException("Unable to start embedded Tomcat", ex);
      }
   }
}

Spring boot web 中集成 Servlet api

使用 Servlet Filter

有两种方式

第一种servelt3.0注解 + @ServletComponentScan
@WebServlet(urlPatterns = "/myservlet") 
public class MyServlet extends HttpServlet { 

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws 
    ServletException, IOException { 
        String name = req.getParameter("name"); 
        resp.getWriter().print("hello MyServlet " + name); 
    }

}
@SpringBootApplication 
@ServletComponentScan 
public class SpringBootStudySourceApplication { 
    public static void main(String[] args) { 		        					SpringApplication.run(SpringBootStudySourceApplication.class, args); 
    } 
}
第二种ServletRegistrationBean和FilterRegistrationBean注册方式
//@WebFilter("/*") 
public class MyFilter implements Filter { 
    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
        System.out.println("*************** MyFilter doFilter *****"); chain.doFilter(request, response); 
    } 
}

@Bean 
public FilterRegistrationBean<MyFilter> getFilterRegistrationBean() { 					    FilterRegistrationBean<MyFilter> frb = new FilterRegistrationBean<>(); 
   frb.setFilter(new MyFilter()); 
   frb.addUrlPatterns("/*"); 
   return frb; 
}

jsp做视图技术

spring boot 不推荐使用jsp。

集成jsp的步骤:

  1. 引入对应的jar

    <dependency> 
        <groupId>javax.servlet</groupId> 
        <artifactId>jstl</artifactId> 
    </dependency> 
    <dependency> 
        <groupId>org.apache.tomcat.embed</groupId> 
        <artifactId>tomcat-embed-jasper</artifactId> 
    </dependency>
    
  2. 创建jsp文件放置目录 src/main下创建 webapp/WEB-INF/jsp/

  3. 在application.yml中配置spring.mvc.view

  4. 编写Controller jsp

    @Controller 
    public class MyController { 
    	@RequestMapping("/index") 
        public String toIndex() { 
            return "index"; 
        } 
    }
    

SpringBoot问题

Invalid byte tag in constant pool: 19

org.apache.tomcat.util.bcel.classfile.ClassFormatException: Invalid byte tag in constant pool: 19

原因:SpringBoot使用外部容器Tomcat部署时,版本问题导致的,Tomcat版本需要9.0+

解决:更换9.0+以上的Tomcat高版本

控制台乱码

原因:编码格式问题

解决:修改apache-tomcat-9.0.45\conf下的logging.properties中 java.util.logging.ConsoleHandler.encoding设置为GBK

​ 默认配置:java.util.logging.ConsoleHandler.encoding = UTF-8

​ 修改后: java.util.logging.ConsoleHandler.encoding = GBK

image-20220702184038229

附录

Spring Boot 应用程序启动器

姓名描述
spring-boot-starter核心启动器,包括自动配置支持、日志记录和 YAML
spring-boot-starter-activemq使用 Apache ActiveMQ 进行 JMS 消息传递的启动器
spring-boot-starter-amqp使用 Spring AMQP 和 Rabbit MQ 的入门
spring-boot-starter-aop使用 Spring AOP 和 AspectJ 进行面向方面编程的入门者
spring-boot-starter-artemis使用 Apache Artemis 的 JMS 消息传递启动器
spring-boot-starter-batch使用 Spring Batch 的入门者
spring-boot-starter-cache使用 Spring Framework 的缓存支持的 Starter
spring-boot-starter-data-cassandra使用 Cassandra 分布式数据库和 Spring Data Cassandra 的入门者
spring-boot-starter-data-cassandra-reactive使用 Cassandra 分布式数据库和 Spring Data Cassandra Reactive 的入门者
spring-boot-starter-data-couchbase使用 Couchbase 面向文档的数据库和 Spring Data Couchbase 的入门
spring-boot-starter-data-couchbase-reactive使用 Couchbase 面向文档的数据库和 Spring Data Couchbase Reactive 的初学者
spring-boot-starter-data-elasticsearch使用 Elasticsearch 搜索和分析引擎和 Spring Data Elasticsearch 的入门者
spring-boot-starter-data-jdbc使用 Spring Data JDBC 的入门者
spring-boot-starter-data-jpa将 Spring Data JPA 与 Hibernate 一起使用的入门
spring-boot-starter-data-ldap使用 Spring Data LDAP 的入门者
spring-boot-starter-data-mongodb使用 MongoDB 面向文档的数据库和 Spring Data MongoDB 的入门
spring-boot-starter-data-mongodb-reactive使用 MongoDB 面向文档的数据库和 Spring Data MongoDB Reactive 的入门
spring-boot-starter-data-neo4j使用 Neo4j 图形数据库和 Spring Data Neo4j 的入门
spring-boot-starter-data-r2dbc使用 Spring Data R2DBC 的入门者
spring-boot-starter-data-redis将 Redis 键值数据存储与 Spring Data Redis 和 Lettuce 客户端一起使用的入门
spring-boot-starter-data-redis-reactive使用带有 Spring Data Redis 反应式和 Lettuce 客户端的 Redis 键值数据存储的启动器
spring-boot-starter-data-rest使用 Spring Data REST 通过 REST 公开 Spring Data 存储库的启动器
spring-boot-starter-freemarker使用 FreeMarker 视图构建 MVC Web 应用程序的 Starter
spring-boot-starter-graphql使用 Spring GraphQL 构建 GraphQL 应用程序的 Starter
spring-boot-starter-groovy-templates使用 Groovy 模板视图构建 MVC Web 应用程序的 Starter
spring-boot-starter-hateoas使用 Spring MVC 和 Spring HATEOAS 构建基于超媒体的 RESTful Web 应用程序的启动器
spring-boot-starter-integration使用 Spring Integration 的入门者
spring-boot-starter-jdbc使用 JDBC 和 HikariCP 连接池的 Starter
spring-boot-starter-jersey使用 JAX-RS 和 Jersey 构建 RESTful Web 应用程序的初学者。一个替代方案spring-boot-starter-web
spring-boot-starter-jooq使用 jOOQ 通过 JDBC 访问 SQL 数据库的入门程序。替代spring-boot-starter-data-jpaspring-boot-starter-jdbc
spring-boot-starter-json读写json的starter
spring-boot-starter-jta-atomikos使用 Atomikos 进行 JTA 事务的启动器
spring-boot-starter-mail使用 Java Mail 和 Spring Framework 的电子邮件发送支持的 Starter
spring-boot-starter-mustache使用 Mustache 视图构建 Web 应用程序的入门程序
spring-boot-starter-oauth2-client使用 Spring Security 的 OAuth2/OpenID Connect 客户端功能的入门者
spring-boot-starter-oauth2-resource-server使用 Spring Security 的 OAuth2 资源服务器特性的入门者
spring-boot-starter-quartz使用 Quartz 调度器的启动器
spring-boot-starter-rsocket用于构建 RSocket 客户端和服务器的启动器
spring-boot-starter-security使用 Spring Security 的入门者
spring-boot-starter-test使用包括 JUnit Jupiter、Hamcrest 和 Mockito 在内的库来测试 Spring Boot 应用程序的 Starter
spring-boot-starter-thymeleaf使用 Thymeleaf 视图构建 MVC Web 应用程序的启动器
spring-boot-starter-validation使用带有 Hibernate Validator 的 Java Bean Validation 的 Starter
spring-boot-starter-web使用 Spring MVC 构建 Web 应用程序的入门程序,包括 RESTful 应用程序。使用 Tomcat 作为默认的嵌入式容器
spring-boot-starter-web-services使用 Spring Web 服务的入门者
spring-boot-starter-webflux使用 Spring Framework 的响应式 Web 支持构建 WebFlux 应用程序的启动器
spring-boot-starter-websocket使用 Spring Framework 的 WebSocket 支持构建 WebSocket 应用程序的 Starter

Spring Boot 生产启动器

姓名描述
spring-boot-starter-actuator使用 Spring Boot 的 Actuator 的 Starter,它提供了生产就绪的特性来帮助你监控和管理你的应用程序

Spring Boot 技术入门

姓名描述
spring-boot-starter-jetty使用 Jetty 作为嵌入式 servlet 容器的启动器。一个替代方案spring-boot-starter-tomcat
spring-boot-starter-log4j2使用 Log4j2 进行日志记录的启动器。一个替代方案spring-boot-starter-logging
spring-boot-starter-logging使用 Logback 进行日志记录的启动器。默认日志记录启动器
spring-boot-starter-reactor-netty使用 Reactor Netty 作为嵌入式响应式 HTTP 服务器的启动器。
spring-boot-starter-tomcat使用 Tomcat 作为嵌入式 servlet 容器的启动器。使用的默认 servlet 容器启动器spring-boot-starter-web
spring-boot-starter-undertow使用 Undertow 作为嵌入式 servlet 容器的启动器。一个替代方案spring-boot-starter-tomcat
    | 使用 Spring Framework 的 WebSocket 支持构建 WebSocket 应用程序的 Starter |

Spring Boot 生产启动器

姓名描述
spring-boot-starter-actuator使用 Spring Boot 的 Actuator 的 Starter,它提供了生产就绪的特性来帮助你监控和管理你的应用程序

Spring Boot 技术入门

姓名描述
spring-boot-starter-jetty使用 Jetty 作为嵌入式 servlet 容器的启动器。一个替代方案spring-boot-starter-tomcat
spring-boot-starter-log4j2使用 Log4j2 进行日志记录的启动器。一个替代方案spring-boot-starter-logging
spring-boot-starter-logging使用 Logback 进行日志记录的启动器。默认日志记录启动器
spring-boot-starter-reactor-netty使用 Reactor Netty 作为嵌入式响应式 HTTP 服务器的启动器。
spring-boot-starter-tomcat使用 Tomcat 作为嵌入式 servlet 容器的启动器。使用的默认 servlet 容器启动器spring-boot-starter-web
spring-boot-starter-undertow使用 Undertow 作为嵌入式 servlet 容器的启动器。一个替代方案spring-boot-starter-tomcat
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值