导入静态资源
直接找到WebMvcAutoConfiguration配置类(对应web开发)
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } Duration cachePeriod = this.resourceProperties.getCache().getPeriod(); CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl(); if (!registry.hasMappingForPattern("/webjars/**")) { customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/") .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } String staticPathPattern = this.mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())) .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } }
发现项目路径resources/webjars/映射到了http://localhost:8080/webjars/
另外去https://www.webjars.org/得到jquery
<dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.5.1</version> </dependency>
添加到pom.xml,左边会发现
(表示jQuery资源被放到了classpath:/META-INF/resources/)
http://localhost:8080/webjars/jquery/3.5.1/jquery.js
(除了classpath:/META-INF/resources/)其他存放静态资源的位置
http://localhost:8080/1.js
首页定制
还是在WebMvcAutoConfiguration配置类中
@Bean public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) { WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping( new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(), this.mvcProperties.getStaticPathPattern()); welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider)); welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations()); return welcomePageHandlerMapping; } private Optional<Resource> getWelcomePage() { String[] locations = 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"); } private boolean isReadable(Resource resource) { try { return resource.exists() && (resource.getURL() != null); } catch (Exception ex) { return false; } }
通常并不是将index.html放到到上述目录进行访问,而是放到templates下,通过controller进行跳转
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>首页</h1> </body> </html>
/** * templates目录下的资源只能通过controller进行跳转而访问 * 这种访问方式需要导入依赖 */ @Controller public class IndexController { @GetMapping("/index") public String index(){ return "index"; } }
http://localhost:8080/
ico图标显示暂时没搞定(不同版本不一样https://docs.spring.io/spring-boot/docs/2.3.2.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-pathmatch)
Thymeleaf模板引擎
上文已经导入了依赖
这个东西就类似于freemarker(电商网站拿来生成静态页面用的)
它的自动配置类ThymeleafAutoConfiguration(叫这个名字跑不了了),对应的ThymeleafProperties
(视图解析)
在html的<html lang="en">标签中加入xmlns:th="http://www.thymeleaf.org"属性(导入命名空间/约束/头文件)
(用法:链接使用@ 文本使用#)
@Controller public class TestController { @RequestMapping("/test") public String test(Model model){ //存入数据 model.addAttribute("msg","Hello,Thymeleaf"); //classpath:/templates/test.html return "test"; } @RequestMapping("/test2") public String test2(Map<String,Object> map){ //存入数据 map.put("msg","<h1>Hello</h1>"); map.put("users", Arrays.asList("xiaoming","xiaohong")); //classpath:/templates/test.html return "test"; } }
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--th:text就是将div中的内容设置为它指定的值--> <div th:text="${msg}"></div> <!--不转义--> <div th:utext="${msg}"></div> <!--遍历数据--> <!--th:each每次遍历都会生成当前这个标签:官网#9--> <h4 th:each="user :${users}" th:text="${user}"></h4> <h4> <!--行内写法:官网#12--> <span th:each="user:${users}">[[${user}]]</span> </h4> </body> </html>
MVC配置原理
官网学习https://docs.spring.io/spring-boot/docs/2.3.2.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration
If you want to keep those Spring Boot MVC customizations and make more MVC customizations
(interceptors, formatters, view controllers, and other features)
, you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.
(通常自定义的配置类写在config包里边)
//应为类型要求为WebMvcConfigurer,这是一个接口 // (一个人当过兵,我们说他是一个兵;一个人会下象棋,我们说他是一个棋手;接口丶) //使用自定义类扩展视图解析器的功能 @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Bean //放到bean中 进入容器 public ViewResolver myViewResolver(){ return new MyViewResolver(); } //我们写一个静态内部类,视图解析器就需要实现ViewResolver接口 private static class MyViewResolver implements ViewResolver{ @Override public View resolveViewName(String s, Locale locale) throws Exception { return null; } } }
给 DispatcherServlet 类的 doDispatch 方法加断点,随便访问一个页面
发现自定义的视图解析器已经在容器中了(当然上面是为了说明原理,官方推荐的是如下做法实现视图跳转)
重写WebMvcConfigurer的方法 Alt+Insert
@Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { // 浏览器发送/test , 就会跳转到test页面; registry.addViewController("/test4").setViewName("test"); } }
http://localhost:8080/test4就轻松的跳转成功了
上面说自定义配置类不能加@EnableWebMvc这个注解,是因为这个注解会向容器中导入DelegatingWebMvcConfiguration这个类(WebMvcConfigurationSupport的子类),有了这个类之后,WebMvcAutoConfiguration就不满足@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)了,所以不能加丶
@EnableWebMvc全面接管SpringMVC的操作,据说是大厂写自己的starter才会用到,后面再继续了解
总之,看到了别人代码自定义的配置类(@Configuration修饰的XxxConfig),立马就要去看看他扩展了什么功能,因为这表示他动了springboot原有的东西丶