1.自动配置原理
首先我们进入@SpringBootApplication这个注解
我们会发现里面又这三个注解
@SpringBootConfiguration
这个注解点进去有@Configuration这个注解,再进去有@Component这个注解,主要告诉我们这个类是一个spring配置、组件。(参照spring 的javaConfig 类配置方式)
@ComponentScan
这个注解是spring扫描注解,用来扫描某个路径下的java类是否符合条件,若符合条件就会注入到ioc容器中,在这里是给这个注解添加两个ExcludeFilter
(条件是类上有没有@Component注解,当然@Controller、@Service也算,因为他们内部也有@Component注解)
@EnableAutoConfiguration
我们重点看一下这个注解,开启自动配置
进入这个注解
这两个注解需要看一下
@AutoConfigurationPackage
点进去,我们发现,它引入了Registrar.class这样一个组件(@Import注解在传统的xml配置文件里就是import标签,引入别的配置文件,在javaConfig中,就是引入一个Configuration,也就是一个组件Component)
点进Registerar.class
这里面的结构是一个抽象类,AutoConfigurationPackages,抽象类里有Registrar引入的这个类。
这里面的两个方法,
第一个方法是把@AutoConfigurationPackage这个注解下的类的包路径下的所有带有@Component注解的类作为组件找出来。
第二个方法是把刚刚找出来的组件加载到ioc容器中。
我们再看一下导入的这个组件AutoConfigurationImportSelector.class,重点
获取所有配置方法
进入getAutoConfigurationEntry()方法,这个方法是获取所有实体(就是 配置 封装的实体)
进入getCandidateConfigurations() ,获取所有候选配置
所有的候选配置在
spring.factories文件里,这里面写着springboot所有可以自动配置的组件
比如说springmvc,打开spring.factories文件,里面有一堆自动配置的路径,每一行代表一个组件,找到这个
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
点进去,会发现它就是一个javaConfig。里面给我们配好了,handlerMapping 、handlerAdapter、viewResolver、DispatcherServlet等springmvc所有需要的组件。也就是说,把这个组件配置进spring容器,springmvc就相当于配置好了。这上面带Conditional的注解都是是否把这个配置类引入spring容器的条件,条件满足才引入。
补充:
application.yml里的配置是如何实现的。
比如说,tomcat的端口号配置, server.port 这个配置默认是8080
去搜ServerProperties这个类,就明白了。
javaConfig类上有这个注解@ConfigurationProperties(prefix = "server") 这样的就可以在application.yml;里配置了。
(另外,spring-configuration-metadata.json这里配置了所有application.yml里的默认配置。比如server.port之类的配置)
回过头来,我们再看loadFactoryNames方法,大概意思就是从spring.factories文件里把所有配置都加载出来。
2.springboot中web模块的研究
在springboot中,web模块是核心模块,其实就是集成了springmvc。
我们在依赖中找到这个文件,所有的自动配置写在这里
打开
找到这个配置,这就是web模块的自动配置类,点进去
先看它的注解,第1个配置注解,说明这个类是一个spring配置。第2、3、4个注解带Conditional的,对照上面的表。
@AutoConfigureOrder注解规定了此配置加载到spring容器的顺序。
@AutoConfigureAfter是加载完此配置,然后再去加载的东西,dispatcherServlet、任务执行模块、校验模块。
1)添加静态资源处理器
这个东西是springmvc的一部分。如果对springmvc的原理清楚的话,应该知道,所有的客户端请求都会先经过dispatcherServlet,但是请求会有两种情况,第一种是请求handler进行业务处理,第二种是请求js、css、jpg等静态资源。处理静态资源的请求就需要下面这个资源处理器来处理了。
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
}
前三行的意思是,如果在application.yml里面有spring.resources的配置的话,默认配置就失效。
后面写了两种获取静态资源的方式。
# 1.通过webjars寻找静态资源(一般不用)
webjars的一些东西,webjars就是可以通过maven引入前端的一些东西如jquery angular等js。具体maven坐标可以去webjars官网去找。引入后,它添加了一个handler,它的requestMapping是 /webjars/** 映射的物理地址是classpath:/META-INF/resources/webjars/
/webjars/** 映射到 物理路径classpath:/META-INF/resources/webjars/
举例,在浏览器中搜索,http://localhost:8080/webjars/angular/1.7.9/angular.js,由于/webjars/angular匹配上了/webjars/** springmvc就会到物理路径 classpath:/META-INF/resources/webjars/ 下去寻找angular/1.7.9/angular.js。
# 2. 通过静态资源路径 寻找静态资源
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
的意思是获取mvcProperties配置类中的静态资源的匹配路径。点进去,我们会发现默认值是 /**
添加路径时是从这里获取的this.resourceProperties.getStaticLocations()
点进去private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"}; (他们几个有优先级)
这里有四个都是默认的资源路径。也就是会去这四个路径下面去寻找 /** 的静态资源。
总结一下,就是 请求路径里的 /** 映射到物理路径 CLASSPATH_RESOURCE_LOCATIONS这几个路径
举例,比如, http://localhost:8080/1.txt 因为 /1.txt 和 /**匹配,所以就会去 映射的物理路径里去找静态资源。
2)欢迎页的配置
欢迎页就是index.html页面,就是web项目里的默认页面。也就是只写localhost:8080/ 会请求到的页面。这个东西之前是在web.xml里配置的。
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
return welcomePageHandlerMapping;
}
private Optional<Resource> getWelcomePage() {
String[] locations = WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}
大概意思是把"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" 路径下的index.html设置为欢迎页。
但是一般我们不这么做,我们会写一个controller来跳转index页面。
另外,还有一个classpath:/template 目录 这个相当于 之前的WEB-INF目录,不能直接访问里面的静态资源,只能通过Controller间接访问。
3)模板引擎的使用
模板引擎就是用来替代jsp的,这种技术用来在服务端把数据渲染到页面上。springboot推荐使用thymeleaf模板,先在pom里引入依赖。
我们查找一下ThymeleafProperties这个类,这个类里有thymeleaf的配置。
@Configurationproperties 注解 spring.thymeleaf
说明可以在application.yml里通过这个配置。
服务端的视图解析器找模板文件的前缀和后缀,这个和jsp很像。之前用jsp时,prefix是 /WEB-INF/jsp/ suffix是 .jsp
http://localhost:8080/helloThymeLeaf 通过controller 找到模板文件。和jsp一样。
4)视图解析器
我们打开这个接口WebMvcConfigurer,这里面是springmvc的配置。视图解析器的配置就在里面。
找到ViewResolverRegistry-》ContentNegotiatingViewResolver-》ViewResolver
ViewResolver是一个接口,只要实现了这个接口的类就是视图解析器了。
这个接口只有一个方法resolveViewName。找到实现类里这个方法。里面有个getCandidateViews获取候选视图方法。这里面会遍历viewResolvers(从ioc容器里获取的)所有视图解析器,选择其中一个最好的,来获取候选视图。
找到DispatcherServlet类,里有个doDispatch方法,这里面就是,dispatcherServlet从接收请求到调用handlerMapping、handlerAdapter、viewResolver等的全部过程,打个断点,看断点里的this会发现
里面又很多视图解析器,其中前两个是默认就有的,第三个是Thymeleaf的,第四个是我自定义的。
3.主配置类启动流程