文章目录
- [SpringBoot 源码分析](#SpringBoot 源码分析)
- [一、源码分析 - 自动装配](#一、源码分析 - 自动装配)
- [二、源码分析 - 启动加载](#二、源码分析 - 启动加载)
-
- [1、SpringApplication - 静态run](#1、SpringApplication - 静态run)
- [2、SpringApplication - 构造方法](#2、SpringApplication - 构造方法)
- [3、SpringApplication - 实例run](#3、SpringApplication - 实例run)
- 4、IoC容器的初始化
- 5、内置Tomcat原理
- [三、自动装配 - 相关注解](#三、自动装配 - 相关注解)
-
- [1、自动装配 - 条件注解](#1、自动装配 - 条件注解)
- [2、自动装配 - 顺序注解](#2、自动装配 - 顺序注解)
- [四、自定义 starter](#四、自定义 starter)
SpringBoot 源码分析
一、源码分析 - 自动装配
1、@SpringBootApplication
java
// 自动装配的开始
@SpringBootApplication
public class BasicProjectApplication {
public static void main(String[] args) {
SpringApplication.run(BasicProjectApplication.class, args);
}
}
java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// @Configuration的子注解
@SpringBootConfiguration
// 开启自动装配
@EnableAutoConfiguration
// 开启扫描机制,扫描启动类所在包(但是不包含类型排除和自动配置的Filter)
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication {.....}
2、@EnableAutoConfiguration
java
package org.springframework.boot.autoconfigure;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
// 通过 @Import 引入 AutoConfigurationImportSelector
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
3、AutoConfigurationImportSelector
ImportSelector
接口用于实现动态地选择需要被导入到容器中的配置类的逻辑。
java
public interface ImportSelector {
/**
* @param importingClassMetadata 用于描述使用了 @Import 注解的配置类的注解元数据。
* @return 数组中的每个元素都是一个全限定类名,表示需要被导入到容器中的配置类。
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
AutoConfigurationImportSelector
实现了 ImportSelector
接口,并在 selectImports
方法中根据特定的条件或逻辑来选择需要导入的配置类,实现更加灵活和动态的配置。
java
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
// 这里的参数 annotationMetadata,就是 @EnableAutoConfiguration 注解的元数据
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry =
// 获取自动配置类
this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected static class AutoConfigurationEntry {
private final List<String> configurations; // 需要导入的
private final Set<String> exclusions; // 不需要导入的
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations =
// 获取所有候选的配置类
this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 获取所有配置类的类全名
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
this.getSpringFactoriesLoaderFactoryClass(), // EnableAutoConfiguration.class
this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
}
4、SpringFactoriesLoader
java
// org.springframework.core.io.support.SpringFactoriesLoader
/**
* 这里的 factoryType 就是 EnableAutoConfiguration.class
*/
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// org.springframework.boot.autoconfigure.EnableAutoConfiguration
String factoryTypeName = factoryType.getName();
// 获取 META-INF/spring.factories 文件中 EnableAutoConfiguration 对应的配置类的类全名
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
// 扫描jar包路径下的 META-INF/spring.factories 文件
Enumeration<URL> urls = classLoader != null ?
classLoader.getResources("META-INF/spring.factories") :
ClassLoader.getSystemResources("META-INF/spring.factories");
// 把 META-INF/spring.factories 解析成 Map
MultiValueMap<String, String> result = new LinkedMultiValueMap();
// ...
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
5、META-INF/spring.factories
下面看一下 META-INF/spring.factories
里的 EnableAutoConfiguration
对应的配置类
我们在里面挑一个常用的 DataSourceAutoConfiguration
类看一下
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@ConditionalOnMissingBean(type = {"io.r2dbc.spi.ConnectionFactory"})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({
DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {
}
java
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
private String driverClassName;
private String url;
private String username;
private String password;
// ...
}
通过 @ConfigurationProperties
+ @EnableConfigurationProperties
自动读取配置文件中的配置
yaml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: .....
username: root
password: root
6、SpringMVC相关装配
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
// 在 DispatcherServletAutoConfiguration 配置类之后配置
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
// MVC的相关的配置.....
}
java
@AutoConfigureOrder(Integer.MIN_VALUE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({DispatcherServlet.class})
// 在 ServletWebServerFactoryAutoConfiguration 配置类之后配置
@AutoConfigureAfter({ServletWebServerFactoryAutoConfiguration.class})
public class DispatcherServletAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Conditional({DefaultDispatcherServletCondition.class})
@ConditionalOnClass({ServletRegistration.class})
@EnableConfigurationProperties({WebMvcProperties.class})
protected static class DispatcherServletConfiguration {
protected DispatcherServletConfiguration() {
}
// 前端控制器 DispatcherServlet
@Bean(name = {"dispatcherServlet"})
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
// ...
return dispatcherServlet;
}
// 文件上传相关bean
@Bean
@ConditionalOnBean({MultipartResolver.class})
@ConditionalOnMissingBean(name = {"multipartResolver"})
public MultipartResolver multipartResolver(MultipartResolver resolver) {
return resolver;
}
}
}
java
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Integer.MIN_VALUE)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties({ServerProperties.class})
@Import({
BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, // 内置Tomcat
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {...}
二、源码分析 - 启动加载
1、SpringApplication - 静态run
看完了自动装配,我们继续看一下启动加载,还是从启动类开始
java
@SpringBootApplication
public class BasicProjectApplication {
public static void main(String[] args) {
SpringApplication.run(BasicProjectApplication.class, args);
}
}
java
// org.springframework.boot.SpringApplication
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 构建 SpringApplication实例 并执行run方法
return (new SpringApplication(primarySources)).run(args);
}
可以看到,启动类中的 SpringApplication.run
,最终会先构建 SpringApplication实例,再执行实例的run方法。
2、SpringApplication - 构造方法
java
// org.springframework.boot.SpringApplication
// SpringApplication的构造方法
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
// 资源加载器
this.resourceLoader = resourceLoader;
// primarySources 就是 启动类
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 判断Web程序的类型(无、基于Servlet的、基于响应式编程模型的)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// getSpringFactoriesInstances 就是获取 META-INF/spring.factories 中对应类的实例
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 推断 Spring Boot 应用程序的主类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return this.getSpringFactoriesInstances(type, new Class[0]);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = this.getClassLoader();
// loadFactoryNames应该很熟悉了,在「自动装配」里边讲过
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 构建实例
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 排序返回
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
3、SpringApplication - 实例run
构建完 SpringApplication 实例之后,就开始调用实例的run方法了
java
// org.springframework.boot.SpringApplication
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// Spring容器
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
// 开始监听
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备Environment
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 打印程序启动时的横幅标语
Banner printedBanner = this.printBanner(environment);
// 构建Spring容器
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 准备IoC容器
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// IoC容器的初始化
this.refreshContext(context);
// IoC容器初始化之后的一些操作
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
4、IoC容器的初始化
在执行 SpringApplication 实例的run方法时,通过refreshContext
方法进行IoC容器的初始化
java
// org.springframework.boot.SpringApplication
private void refreshContext(ConfigurableApplicationContext context) {
refresh((ApplicationContext) context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
@Deprecated
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
refresh((ConfigurableApplicationContext) applicationContext);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
// IoC容器的初始化
applicationContext.refresh();
}
然后,就到我们熟悉的 refresh()
方法了,总共有12大步骤(这属于Spring源码的内容,这里就不展开了)
java
// org.springframework.context.support.AbstractApplicationContext
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 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);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// 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) {
// logger...
// 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();
}
}
}
5、内置Tomcat原理
我们看到 refresh()
方法12大步骤中的 onRefresh()
方法,默认是空实现
java
// org.springframework.context.support.AbstractApplicationContext
protected void onRefresh() throws BeansException {
// For subclasses: do nothing by default.
}
ServletWebServerApplicationContext
重写了onRefresh方法:
java
// org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext
@Override
protected void onRefresh() {
super.onRefresh();
try {
// tomcat容器就是在这里创建并启动的
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
// 创建web容器
this.webServer = factory.getWebServer(getSelfInitializer());
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();
}
java
// org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
@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());
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);
// 启动Tomcat
return getTomcatWebServer(tomcat);
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
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();
}
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
// ...
this.tomcat.start(); // 启动Tomcat
// ...
}
// ...
}
}
三、自动装配 - 相关注解
1、自动装配 - 条件注解
@Conditional
是 Spring 框架中的一个元注解,根据注解的条件来决定是否应该应用某个配置类或组件。
Spring 还提供了许多子注解,用于更精细地定义条件。
常用注解 | 描述 |
---|---|
@ConditionalOnBean | 容器中存在指定的 Bean 时,配置才会生效。 |
@ConditionalOnMissingBean | 容器中不存在指定的 Bean 时,配置才会生效。 |
@ConditionalOnClass | 当指定的类存在时,配置才会生效。 |
@ConditionalOnMissingClass | 当指定的类不存在时,配置才会生效。 |
@ConditionalOnWebApplication | 只有在web环境下,配置才会生效。 |
@ConditionalOnNotWebApplication | 只有在非web环境下,配置才会生效。 |
@ConditionalOnJava | 系统的Java版本符合需求,配置才会生效。 |
@ConditionalOnExpression | 满足指定的SpEL表达式,配置才会生效。 |
@ConditionalOnProperty | 指定的属性有指定的值,配置才会生效。 |
@ConditionalOnResource | 类路径下存在指定资源文件,配置才会生效。 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或Bean是首选Bean |
2、自动装配 - 顺序注解
注解 | 描述 |
---|---|
@AutoConfigureAfter | 在指定配置类加载之后加载 |
@AutoConfigureBefore | 在指定配置类加载之前加载 |
@AutoConfigureOrder | 指定加载配置的优先级,默认0 |
四、自定义 starter
看完自动装配的源码,我们可以尝试自定义 starter,来实现自动装配。
1、pom文件
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 定义starter的坐标 -->
<groupId>jwt-utils</groupId>
<artifactId>jwt-spring-boot-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>jwt-spring-boot-starter</name>
</project>
2、Properties实体类
java
@Data
@ConfigurationProperties(prefix = "auth")
public class ClientProperties {
/**
* 客户端名称
*/
private String clientId;
/**
* 客户端秘钥
*/
private String secret;
/**
* 拦截器拦截路径
*/
private List<String> includeFilterPaths;
/**
* 拦截器放行路径
*/
private List<String> excludeFilterPaths;
}
3、Config配置类
java
@Slf4j
@Configuration
// 只有配置了 auth.clientId 和 auth.secret 属性,当前配置才会生效。
@ConditionalOnProperty(prefix = "auth", name = {"clientId", "secret"})
@EnableConfigurationProperties(ClientProperties.class)
public class AuthAutoConfiguration {
// 实现一些starter的逻辑....
}
4、spring.factories
在 resources/META-INF/spring.factories
目录下编写
properties
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.auth.config.AuthAutoConfiguration
5、使用自动装配
在要使用的模块的pom文件中添加starter依赖
xml
<dependency>
<groupId>jwt-utils</groupId>
<artifactId>jwt-spring-boot-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
在application.yaml
文件添加配置
yaml
auth:
clientId: user-service
secret: 1234
includeFilterPaths:
- /path1
- /path2