前面看到了,使用 spring boot 零配置就可以运行起来(笔者在 yml 中配置了端口号,不配的话,它有一个默认的 8080 端口号),这就是 SpirngBoot 自动装配的能力了。
可以看看运行的入口源码
org.springframework.boot.SpringApplication#run(java.lang.Class<?>, java.lang.String...)
/**
静态助手,可用于使用默认设置从指定源运行 SpringApplication 。
参数:
primarySource –要加载的主要源
args –应用程序参数(通常从 Java main 方法传递)
返回:
正在运行的 ApplicationContext
*/
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
对于 @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 {
我们需要关注的有以下几个:
● @ComponentScan 组件扫描指令
● @SpringBootConfiguration
● @EnableAutoConfiguration
@SpringBootConfiguration
@Configuration
public @interface SpringBootConfiguration {
}
该注解被 @Configuration 所注解,之前在写 spring mvc 的时候,写了一堆的 xml 文件,然后可以new ClassPathXmlApplicationContext(xx.xml) 就得到了一个 SPring 容器,这里这是一样的含义,表示是一个容器配置
@ComponentScan
该注解 API 文档上有说明,如果未定义特定的程序包,则将从声明此批注的类的程序包中进行扫描。
也就是说:我们的启动入口类是 cn.mrcode.foodiedev.api.Application 那么它默认扫描的类路径是 cn.mrcode.foodiedev.api 下所有需要被 spring 所管理的组件。
@EnableAutoConfiguration
启用 Spring Application Context 的自动配置,尝试猜测和配置您可能需要的 bean。 通常根据您的类路径和定义的 bean 来应用自动配置类
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
它有使用了两个注解,导入了一个 AutoConfigurationImportSelector 从字面上来看,是一个自动配置导入选择器,该类里面有一个 selectImports 方法
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 主要关注这个方法,获取自动配置实例
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取候选配置
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
// 获取候选配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), 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;
}
getCandidateConfigurations 中检查了 configurations 不能为空,根据异常里面的信息:在 META-INF/spring.factories 中没有找到自动配置类。如果您正在使用自定义打包,请确保该文件是正确的。
META-INF/spring.factories 该文件就在上面类所在包里面,内容如下所示
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
...
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
...
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
....
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
可以看到里面包含了很多的自动配置类,其中我们主要关注下 EmbeddedWebServerFactoryCustomizerAutoConfiguration 从名称翻译含义为 嵌入式 Web 服务器出厂自定义程序自动配置
@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
/**
* 如果正在使用 Tomcat,则为嵌套配置。
*/
@Configuration
@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
public static class TomcatWebServerFactoryCustomizerConfiguration {
@Bean
public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(
Environment environment, ServerProperties serverProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
}
}
....
可以看到,首当其一的就是 Tomcat 相关的。
public class Tomcat {
private static final StringManager sm = StringManager.getManager(Tomcat.class);
private final Map<String, Logger> pinnedLoggers = new HashMap();
protected Server server;
protected int port = 8080;
protected String hostname = "localhost";
查看这个 Tomcat 类,就能看到它的默认端口就是 8080
另外我们要关注的是 WebMvcAutoConfiguration
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link EnableWebMvc Web MVC}.
*/
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
public static final String DEFAULT_PREFIX = "";
public static final String DEFAULT_SUFFIX = "";
private static final String[] SERVLET_LOCATIONS = { "/" };
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled",
matchIfMissing = true)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
可以看到它最主要的功能就是为 EnableWebMvc Web MVC 开启自动配置。
再者需要关注的是 ServletWebServerFactoryAutoConfiguration
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
org.apache.catalina.startup.Tomcat 这里有一个 tomcat 启动相关的代码,这也是为什么嵌入式 tomcat 会启动的原因
这一章主要学习到了通过查看源代码的方式。理解 spring boot 零配置的一个大体原理,最主要的一点:不要去深入理解每一句代码,不是做科研调查,向上面的过程一样,只找大体流程相关的。
从这个入口可以看到它自动配置了哪些东西,包括配置类是什么,以后就会有一个方向了。