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()
进入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
BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationListener这三个都是做什么的?
BootstrapRegistryInitializer
springboot从各jar包下META-INF/spring.factories中获取org.springframework.boot.BootstrapRegistryInitializer对应的实现类信息
BootstrapRegistryInitializer接口信息如下:
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);
}
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系统属性
getSystemProperties()获取系统环境变量
ApplicationServletEnvironment继承体系
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处理环境的监听器
EnvironmentPostProcessorApplicationListener
进入onApplicationEvent方法
发布的是ApplicationEnvironmentPreparedEvent这个类型的事件,因此执行onApplicationEnvironmentPreparedEvent方法
EnvironmentPostProcessor
获取EnvironmentPostProcessor类型的环境处理器,如下
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
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创建容器
根据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中加载其他的:
SpringFactoriesLoader.loadFactoryNames(type, classLoader)
关键方法,进入此方法打断点,跟踪interface org.springframework.boot.autoconfigure.EnableAutoConfiguration这个值,此时查看下调用栈
调用栈信息
从上述的调用栈信息的得到,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的继承体系
重点说明:AutoConfigurationImportSelector 实现了 DeferredImportSelector,DeferredImportSelector 是延迟导入选择器。所谓延迟:在所有指定的、包扫描到的@Configuration类中的 bean定义注册后,才会来处理延迟导入的@Configuration类
为什么实现了DeferredImportSelector就延迟导入
此时就需要看下ConfigurationClassPostProcessor,如下看到,先解析主入口类@SpringApplication注解的类
ConfigurationClassParser#parse(Set)parse方法
进入parse方法,先执行正常导入的方法,然后最后执行延迟导入的方法,
看下正常导入的parse方法
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
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部分
进入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 的流程处理完毕后,回到如下图的位置:
进入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启动过程中,是如何保证处理顺序靠后的
Tomcat是如何启动的
Tomcat创建
ApplicatonContext#refresh
在ApplicationContext的refresh刷新的方法中,有个onRefresh方法,我们看到onRefresh()
方法是调用其子类的实现,根据我们上文的分析,我们这里的子类是ServletWebServerApplicationContext
ServletWebServerApplicationContext#onRefresh
AbstractApplicationContext的实现ServletWebServerApplicationContext#onRefresh中,
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的步骤:
-
引入对应的jar
<dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency>
-
创建jsp文件放置目录 src/main下创建 webapp/WEB-INF/jsp/
-
在application.yml中配置spring.mvc.view
-
编写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
附录
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-jpa 或spring-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 |