SpringBoot-原理初探

435 阅读3分钟

自动配置

在pom.xml中有一个parent

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.4.3</version>
	<relativePath/> <!-- lookup parent from repository -->
</parent>

而在spring-boot-starter-parent中还有一个parent:spring-boot-dependencies

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.4.3</version>
  </parent>

其中包含了大量的核心依赖,我们在我们在导入一些springboot依赖时不需要指明版本号便是因为在spring-boot-dependencies已经声明了

启动器

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.4.3</version>
      <scope>compile</scope>
    </dependency>

即springboot 的启动场景 如导入spring-boot-starter-web,便会帮我们导入所需的所有web依赖 需要什么只需导入对应的starter即可

主程序

//主程序入口
//标注为springboot程序
@SpringBootApplication
public class SpringBoot01Application {

	public static void main(String[] args) {
		//将程序启动
		SpringApplication.run(SpringBoot01Application.class, args);
	}

}

@SpringBootConfiguration

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

在@SpringBootApplication有两个注解@SpringBootConfiguration 和@EnableAutoConfiguration

在@SpringBootConfiguration有一个@Configuration 指明这是一个配置类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {}

而在@Configuration中有@Component 指明这是一个组件

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {}

@EnableAutoConfiguration(自动配置)

// @EnableAutoConfiguration:自动配置注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

在@EnableAutoConfiguration中有AutoConfigurationPackage和 Import(AutoConfigurationImportSelector.class) 两个注解

@AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}

@Import(AutoConfigurationPackages.Registrar.class)导入自动注册包 给容器批量导入一些组件

@Import(AutoConfigurationImportSelector.class)

获取需要导入的配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
利用工厂加载所有需要的组件
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader)
默认从META-INF/spring.factories扫描需要加载的组件
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

web静态资源导入

1.WebMVcAutoConfiguration.java中有这么一段代码

@Configuration(proxyBeanMethods = false)
	@Import(EnableWebMvcConfiguration.class)
	@EnableConfigurationProperties({ WebMvcProperties.class,
			org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })
	@Order(0)
	public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {

通过点击WebMvcProperties.class进入对应的类

@EnableConfigurationProperties({ WebMvcProperties.class,...)

发现

/**
	 * Path pattern used for static resources.
	 */
	private String staticPathPattern = "/**";

因此获得了静态资源加载的第一个路径

  1. webjars 在WebMVcAutoConfiguration.java中有这么一个函数
@Override
		protected void addResourceHandlers(ResourceHandlerRegistry registry) {
			super.addResourceHandlers(registry);
                        //如果有指明映射则提示默认配置失效
			if (!this.resourceProperties.isAddMappings()) {
				logger.debug("Default resource handling disabled");
				return;
			}
			ServletContext servletContext = getServletContext();
                        //如果导入有webjars则从classpath:/META-INF/resources/webjars/开始加载静态资源否则返回空
			addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
                        //如果没有导入webjars,则从this.resourceProperties.getStaticLocations()开始加载静态资源
			addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
				registration.addResourceLocations(this.resourceProperties.getStaticLocations());
				if (servletContext != null) {
					registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION));
				}
			});
		}

因此我们获得了静态资源加载的第二个路径:classpath:/META-INF/resources/webjars/,将其加载到了/webjars/** 下

3.在上面的代码中有这么一段

addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
				registration.addResourceLocations(this.resourceProperties.getStaticLocations());

这其中this.mvcProperties.getStaticPathPattern()便是获取我们第一个得到的路径 /**

通过点击getStaticLocations()获得

public String[] getStaticLocations() {
			return this.staticLocations;
		}

再次点击staticLocations得到

private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;

最终我们获得

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
				"classpath:/resources/", "classpath:/static/", "classpath:/public/" };

这是我们获取的第三个静态资源加载路径,并且是将其加载到了/** 下

总结:

classpath:/META-INF/resources/webjars/ 映射到 localhost:8080/webjars/

public static /** resources 映射到 localhost:8080/

优先级:resource>static(默认)>public

我们可以通过spring.mvc.static-path-pattern= /hello/**,classpath:/line/ 来修改默认的地址

首页

WebMVcAutoConfiguration.java中有这么一段代码

private Resource getWelcomePage() {
                        //遍历静态资源目录
			for (String location : this.resourceProperties.getStaticLocations()) {
				Resource indexHtml = getIndexHtml(location);
				//成功获取index.html并返回
                                if (indexHtml != null) {
					return indexHtml;
				}
			}
			ServletContext servletContext = getServletContext();
			if (servletContext != null) {
				return getIndexHtml(new ServletContextResource(servletContext, SERVLET_LOCATION));
			}
			return null;
		}