文章目录
1.1 起步依赖分析
1.1.1 spring-boot-starter-parent分析
- 该文件是SpringBoot默认控制依赖版本,所有SpringBoot都继承自这个
1.1.1.1 常见依赖管理
按住ctrl ,单击spring-boot-starter-parent后,看到spring-bbot-dependencies
继续点击spring-bbot-dependencies,可发现这个pom文件中在properties标签中有很多项目中常用依赖名称和版本号,这些是当前SpringBoot版本给你选好了的依赖版本。
再继续往下翻,会看到有个dependencyManagement的标签,这个标签管理着各种常见依赖。
1.1.1.1 关于application文件的加载
spring-bbot-dependencies下面可看到
不管是yml,还是yaml或properties都在这里有定义资源路径和名称
所以在项目的resources文件夹下创建application*.yml(yaml,properties)都可以加载
1.1.1 spring-boot-starter-web分析
按住ctrl ,单击spring-boot-starter-web后
看到dependencies下面有spring-boot-starter、spring-boot-tomcat,spring-web,spring-webmvc等等依赖。
点进spring-boot-tomcat后你会发现dependencies标签下有apache之类的一些依赖。
1.2 自动配置原理解析
1.2.1 @SpringBootApplication注解解析
安装ctrl,点击@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 {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
}
@SpringBootApplication = {@Configuration, @EnableAutoConfiguration,@ComponentScan}
1.2.1.1 @SpringBootConfiguration 注解
单击进入后发现,代码如下:
package org.springframework.boot;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
- 这个类中主要的就是@Configuration注解,这个声明当前类,是配置类。
- @SpringBootConfiguration继承自@Configuration,内部并无任何改变,因此@SpringBootConfiguration功能和@Configuration是一样的
1.2.1.2 @EnableAutoConfiguration 注解
- 点进@EnableAutoConfiguration注解后内容如下
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
这里关键是@Import({AutoConfigurationImportSelector.class}),通过AutoConfigurationImportSelector.class,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IOC容器。
接下来我们进入AutoConfigurationImportSelector.class,可以看到内部有一个方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);//这里是重点
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
我们看到这个函数中调用了getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata)方法,进入到getAutoConfigurationEntry()下,在这个方法中有如下一段代码:
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
点进getCandidateConfigurations()函数,代码如下:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), 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;
}
这段代码中可以看到这段字符串,“No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.”
在方法中借助Spring框架原有的一个工具类SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自动配置功效才得以大功告成!
接下来查看META-INF/spring.factories
查看当前所处包路径,可看到路径:package org.springframework.boot.autoconfigure;
打开箭头所指的地方,找到org.springframework.boot.autoconfigure,打开后可看见里面文件如下:
这里面存在spring.factories,打开spring.factories
spring.factories文件里每一个xxxAutoConfiguration文件一般都会有下面的条件注解:
- @ConditionalOnClass : classpath中存在该类时起效
- @ConditionalOnMissingClass : classpath中不存在该类时起效
- @ConditionalOnBean : DI容器中存在该类型Bean时起效
- @ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
- @ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
- @ConditionalOnExpression : SpEL表达式结果为true时
- @ConditionalOnProperty : 参数设置或者值一致时起效
- @ConditionalOnResource : 指定的文件存在时起效
- @ConditionalOnJndi : 指定的JNDI存在时起效
- @ConditionalOnJava : 指定的Java版本存在时起效
- @ConditionalOnWebApplication : Web应用环境下起效
- @ConditionalOnNotWebApplication : 非Web应用环境下起效
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
- SpringFactoriesLoader
SpringFactoriesLoader属于Spring框架私有的一种扩展方案(类似于Java的SPI方案java.util.ServiceLoader),其主要功能就是从指定的配置文件MATA-INF/spring-factories加载配置,Spring-factories是一个典型的Java Properties文件,只不过Key和Value都是Java类型的完整类名,
对于@EnableAutoConfiguration来讲,SpringFactoriesLoader的用途稍微不同一些,其本意是为了提供SPI拓展的场景,而在@EnableAutoConfiguration场景中,它提供了一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfig.EnableAutoConfiguration作为查找的KEY,获得对应一组@Configuration类。
SpringFactoriesLoader是一个抽象类,类中定义的静态属性,定义了其加载资源的路径public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”,此外还有三个静态方法:
- loadFactories:加载指定的factoryClass并进行实例化。
- loadFactoryNames:加载指定的factoryClass的名称集合。
- instantiateFactory:对指定的factoryClass进行实例化。
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();
private SpringFactoriesLoader() {
}
public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
}
List<T> result = new ArrayList(factoryNames.size());
Iterator var5 = factoryNames.iterator();
while(var5.hasNext()) {
String factoryName = (String)var5.next();
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, 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 {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
try {
Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
if (!factoryClass.isAssignableFrom(instanceClass)) {
throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
} else {
return ReflectionUtils.accessibleConstructor(instanceClass, new Class[0]).newInstance();
}
} catch (Throwable var4) {
throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), var4);
}
}
}
loadFactories方法首先获取类加载器,然后调用loadFactoryNames方法获取所有的指定资源的名称集合、接着调用instantiateFactory方法实例化这些资源类并将其添加到result集合中。最后调用AnnotationAwareOrderComparator.sort方法进行集合的排序。
- SpringBoot配置文件参数加载
spring.factories文件中:
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
按住ctrl,点击org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration
@Configuration
@ConditionalOnClass({Servlet.class, ServerContainer.class})
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@AutoConfigureBefore({ServletWebServerFactoryAutoConfiguration.class})
public class WebSocketServletAutoConfiguration {
public WebSocketServletAutoConfiguration() {
}
@Configuration
@ConditionalOnClass({Bootstrap.class})
static class UndertowWebSocketConfiguration {
UndertowWebSocketConfiguration() {
}
@Bean
@ConditionalOnMissingBean(
name = {"websocketServletWebServerCustomizer"}
)
public UndertowWebSocketServletWebServerCustomizer websocketServletWebServerCustomizer() {
return new UndertowWebSocketServletWebServerCustomizer();
}
}
@Configuration
@ConditionalOnClass({WebSocketServerContainerInitializer.class})
static class JettyWebSocketConfiguration {
JettyWebSocketConfiguration() {
}
@Bean
@ConditionalOnMissingBean(
name = {"websocketServletWebServerCustomizer"}
)
public JettyWebSocketServletWebServerCustomizer websocketServletWebServerCustomizer() {
return new JettyWebSocketServletWebServerCustomizer();
}
}
@Configuration
@ConditionalOnClass({Tomcat.class, WsSci.class})
static class TomcatWebSocketConfiguration {
TomcatWebSocketConfiguration() {
}
@Bean
@ConditionalOnMissingBean(
name = {"websocketServletWebServerCustomizer"}
)
public TomcatWebSocketServletWebServerCustomizer websocketServletWebServerCustomizer() {
return new TomcatWebSocketServletWebServerCustomizer();
}
}
}
根据上述代码和代码之前的注解说明,可看出这些代码在什么时候生效。
接下来继续看当前类上:@AutoConfigureBefore({ServletWebServerFactoryAutoConfiguration.class})
,点进ServletWebServerFactoryAutoConfiguration.class
@Configuration
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
public ServletWebServerFactoryAutoConfiguration() {
}
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
......
}
自动装配过程中,通过@EnableConfigurationProperties({ServerProperties.class}),配置服务器相关属性。
点进ServerProperties.class
@ConfigurationProperties(
prefix = "server",
ignoreUnknownFields = true
)
public class ServerProperties {
private Integer port;
private InetAddress address;
@NestedConfigurationProperty
private final ErrorProperties error = new ErrorProperties();
private Boolean useForwardHeaders;
private String serverHeader;
private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8L);
private Duration connectionTimeout;
@NestedConfigurationProperty
private Ssl ssl;
@NestedConfigurationProperty
private final Compression compression = new Compression();
@NestedConfigurationProperty
private final Http2 http2 = new Http2();
private final ServerProperties.Servlet servlet = new ServerProperties.Servlet();
private final ServerProperties.Tomcat tomcat = new ServerProperties.Tomcat();
private final ServerProperties.Jetty jetty = new ServerProperties.Jetty();
private final ServerProperties.Undertow undertow = new ServerProperties.Undertow();
......
}
可以看见port等等字段,这些自动在程序启动是通过@ConfigurationProperties(
prefix = “server”,
ignoreUnknownFields = true
)注解加载,如果不配置,它会有默认值,从additional-spring-configuration-metadata.json中加载。
可以看到server.port默认值是8080,如果我们不对它进行覆盖的话,Springboot启动后,默认端口就是8080
对于这些属性的覆盖可以通过application.yml(yaml,properties)进行覆盖,如
这样,启动项目后端口就会是8888。