SpringBoot 自动装配简述(八)

2022-06-14 20:04:06 浏览数 (1)

前面看到了,使用 spring boot 零配置就可以运行起来(笔者在 yml 中配置了端口号,不配的话,它有一个默认的 8080 端口号),这就是 SpirngBoot 自动装配的能力了。

可以看看运行的入口源码

代码语言:javascript复制
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 注解,我们需要关注下

代码语言:javascript复制
@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

代码语言:javascript复制
@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

代码语言:javascript复制
启用 Spring Application Context 的自动配置,尝试猜测和配置您可能需要的 bean。 通常根据您的类路径和定义的 bean 来应用自动配置类

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

它有使用了两个注解,导入了一个 AutoConfigurationImportSelector 从字面上来看,是一个自动配置导入选择器,该类里面有一个 selectImports 方法

代码语言:javascript复制
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 该文件就在上面类所在包里面,内容如下所示

代码语言:javascript复制
# 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 服务器出厂自定义程序自动配置

代码语言:javascript复制
@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 相关的。

代码语言:javascript复制
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

代码语言:javascript复制
/**
 * {@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

代码语言:javascript复制
@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 零配置的一个大体原理,最主要的一点:不要去深入理解每一句代码,不是做科研调查,向上面的过程一样,只找大体流程相关的。

从这个入口可以看到它自动配置了哪些东西,包括配置类是什么,以后就会有一个方向了。

0 人点赞