wuchangjian

SpringBoot Web 开发

静态资源

打开WebMvcAutoConfiguration类里面的静态类WebMvcAutoConfigurationAdapter里面的addResourceHandlers()方法

@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();
    
    // 1. WebJars 方式, 只需要输入 /webjars/**
    if (!registry.hasMappingForPattern("/webjars/**")) {
        customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/")
                .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)
                .setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));
    }

    // 2. 获得静态资源的路径
    String staticPathPattern = this.mvcProperties.getStaticPathPattern();
    if (!registry.hasMappingForPattern(staticPathPattern)) {
        customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
                .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
                .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)
                .setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));
    }
}

1、 WebJars (一般不使用)

WebJars 官网

访问资源路径: /webjars/** 映射到 classpath:/META-INF/resources/webjars/
访问路径:http://localhost:8080/webjars/**

测试

可以使用Maven 引入 JQuery.

<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.5.1</version>
</dependency>

如果你要访问 jquery 相当于你只要输入 : http://localhost:8080/webjars/jquery/3.5.1/jquery.js 就可以访问到。

在这里插入图片描述

2、 获得静态资源路径

getStaticPathPattern() 码源为:

public String getStaticPathPattern() {
     return this.staticPathPattern;
 }
// 点进 staticPathPattern
/**
 * Path pattern used for static resources.
 */
private String staticPathPattern = "/**";

WebMvcAutoConfiguration类里面的静态类WebMvcAutoConfigurationAdapter类码源:

@EnableConfigurationProperties({ WebMvcProperties.class,
        org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
    ....
}

// 点进 WebProperties 配置类
@ConfigurationProperties("spring.web")
public class WebProperties {
    public static class Resources {

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

        /**
         * Locations of static resources. Defaults to classpath:[/META-INF/resources/,
         * /resources/, /static/, /public/].
         */
        private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
    }
}

访问资源路径:/** 映射到 “classpath:/META-INF/resources/”, “classpath:/resources/”, “classpath:/static/”, “classpath:/public/”
这4个目录都能够被识别到

访问路径:http://localhost:8080/**

优先级: public < static(默认) < resources

3、 自定义配置 (一般不配置,使用第二种默认的)
application.properties

spring.mvc.static-path-pattern=/xxxx

首页

EnableWebMvcConfiguration类的内部类EnableWebMvcConfiguration码源:

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
        FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
    WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
            new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
            this.mvcProperties.getStaticPathPattern());// this.mvcProperties.getStaticPathPattern() 获取自定义的配置
    welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
    welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
    return welcomePageHandlerMapping;
}

    ....

private Optional<Resource> getWelcomePage() {
    // this.resourceProperties.getStaticLocations() 点进去是系统定义的4个静态资源目录
    String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations()); 
    return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}

private Resource getIndexHtml(String location) {
    // 在静态资源目录下找 index.html ,返回首页
    return this.resourceLoader.getResource(location + "index.html");
}

所以将 index.html 放在静态资源目录下即可直接访问到。

在 templates 目录下的所有页面,只能通过 Controller 来跳转。需要模板引擎的支持。(thymeleaf)

Thymeleaf 模板引擎

什么是 Thymeleaf ?

 Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎,能够处理HTML,XML,JavaScript,CSS甚至纯文本。
 jsp 就是一个模板引擎。

Thymeleaf 官网
Thymeleaf Github
SpringBoot Start

需要带入依赖:

<!-- Thymeleaf : 基于 3.x 开发 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

导入的东西都有xxxProperties自动配置类,查看 Thymeleaf 的自动配置类:ThymeleafProperties的源码:

@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
	private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
        // 默认前缀
	public static final String DEFAULT_PREFIX = "classpath:/templates/";
        // 默认后缀
	public static final String DEFAULT_SUFFIX = ".html";
        ...
}

测试在 templates 目录下新建 test.html, 编写 Controller:

@Controller
public class IndexController {
    @RequestMapping("/test")
    public String index(){
        return "test";
    }
}

输入 http://localhost:8080/test 即可访问到。

Thymeleaf 基础语法 (另外再自学吧,淦!)

所有的 html 元素都可以被 thymeleaf 替换接管: th:元素名

导入命名空间

<html xmlns:th="http://www.thymeleaf.org">

属性优先级:

Order Feature Attributes  
1 Fragment inclusion th:insert、th:replace  
2 Fragment iteration th:each  
3 Conditional evaluation th:if、th:unless、th:switch、th:case  
4 Local variable definition th:object、th:with  
5 General attribute modification th:attr、th:attrprepend、th:attrappend  
6 Specific attribute modification th:value、th:href、th:src、…  
7 Text (tag body modification) th:text、th:utext  
8 Fragment specification th:fragment  
9 Fragment removal th:remove  

1、 简单变量

  • 变量表达式: ${…}
<div th:text="${msg}"></div>
  • 选择表达式: *{…}
<div th:object="${session.user}">
    <p>Name: <span th:text="*{name}"></span></p>
    <p>Age: <span th:text="*{age}"></span></p>
    <p>Sex: <span th:text="*{sex}"></span></p>
</div>
<!-- 等价于 -->
<div>
    <p>Name: <span th:text="${session.user.name}"></span></p>
    <p>Age: <span th:text="${session.user.age}"></span></p>
    <p>Sex: <span th:text="${session.user.sex}"></span></p>
</div>
  • 消息表达式: #{…}
    用于国际化

  • 链接表达式: @{…}

<a th:href="@{test.html}">Content 路径,默认访问静态资源目录下的文件</a>
  • 片段表达式: ~{…} (模板插入: th:insert, th:replace)
<!--侧边栏-->
<nav th:fragment="sidebarMenu">...</nav>

<!-- 侧边栏插入 -->
<div th:insert="~{文件名::sidebarMenu}"></div>
templates/conmons/commons.html 
<nav th:fragment="sidebarMenu">...</nav>
    
<!-- 使用界面的侧边栏引入 -->
<div th:replace="~{commons/commons::sidebarMenu}"></div>

2、 文字

  • 文本文字(单引号): ‘one text’, ‘Another one!’,…
  • 数字: 0, 34, 3.0, 12.3,…
  • 布尔值: true, false
  • 空值: null

3、 文本操作:

  • 文本连接符: +
  • 文本替换 : |The name is ${name}|

4、比较运算符:

  • 比较: >, <, >=, <= (gt, lt, ge, le)
  • 相等: ==, != (eq, ne)

5、条件运算符:

  • If-then: (if) ? (then)
  • If-then-else: (if) ? (then) : (else)
  • Default: (value) ?: (defaultvalue)

6、 循环
th:each

<tr th:each="user,iterStat : ${users}">
    <td th:text="${user.name}"></td>
    <td th:text="${user.age}"></td>
    <td th:text="${user.sex}"></td>
</tr>

iterStat 对象包含以下属性:

  • index,从0开始的角标
  • count,元素的个数,从1开始
  • size,总元素个数
  • current,当前遍历到的元素
  • even/odd,返回是否为奇偶,boolean值
  • first/last,返回是否为第一或最后,boolean值

MVC 配置原理

官方文档

Spring Boot为Spring MVC提供了自动配置,适用于大多数应用程序。
自动配置在 Spring 的默认值之上添加了以下功能:

  • 包含ContentNegotiatingViewResolverBeanNameViewResolverbeans。
  • 支持提供静态资源,包括对 WebJars 的支持。
  • 自动注册ConverterGenericConverterFormatterbeans。
  • 支持HttpMessageConverters
  • 自动注册MessageCodesResolver
  • 静态index.html支持。
  • 自动使用ConfigurableWebBindingInitializerbean。

如果你想保留 Spring Boot MVC 功能,并且你想添加额外的 MVC 配置(拦截器,格式化程序,视图控制器和其他功能),你可以编写自己 MVC配置类 添加上@Configuration注解,并且这个配置类的类型必须为WebMvcConfigurer类(他是一个接口类,实现即可) 但不加@EnableWebMvc注解。

如果您想完全控制 Spring MVC,可以添加注释@EnableWebMvc

初体验

自定义一个视图解析器

// 扩展 Spring MVC
@Configuration
public class MyNvcConfig implements WebMvcConfigurer {
    // ContentNegotiatingViewResolver ——> ViewResolver 实现了视图解析器接口,我们就可以把他看做视图解析器

    // 放入到 Spring 容器中
    @Bean
    public ViewResolver myViewResolver(){
        return new MyViewResolver();
    }
    // 自定义一个视图解析器 MyViewResolver
    public static class MyViewResolver implements ViewResolver {

        @Override
        public View resolveViewName(String viewName, Locale locale) throws Exception {
            return null;
        }
    }
}

所有的请求都会经过DispatcherServlet类,在此类源码的一下方法打上断点。 debug 启动 SpringBoot

protected void doDispatch(HttpServletRequest request, HttpServletResponse response)

可以看到自己写的视图解析器配置类:

在这里插入图片描述

如果需要自定义一些功能,只需要写组件,然后将它交给 SpringBootSpringBoot会帮我们自动装配。

首页跳转实现

输入:http://localhost:8080/index.htmlhttp://localhost:8080/都会跳转到 index.html

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 首页跳转实现
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }
}

国际化

在 static 目录下新建 i18n(internationalization的缩写,中间有18个字母) 文件夹,新建 login.properties 文件,再建一个 login_zh_CN.properties 文件
IDEA 会自动生成 Resource Bundle ‘login’ 文件夹,右击可以新建其他语言的配置文件

在这里插入图片描述

在这里插入图片描述

打开某个配置文件,点击编辑器底部的 Resource Bundle ,即可同时编写三个文件。

自动配置类:MessageSourceAutoConfiguration

配置文件:

# 国际化配置文件位置
spring.messages.basename=i18n.login

html: 使用 Thymeleaf 的 #{xxx} 获取,链接传递的参数:@{/index.html(l=‘zh_CN’)}

<body class="text-center">
<form class="form-signin">
  <img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
  <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
  <label for="inputEmail" class="sr-only" th:text="#{login.username}">Username</label>
  <input type="text" id="inputEmail" class="form-control" th:placeholder="#{login.username}" required autofocus>
  <label for="inputPassword" class="sr-only" th:text="#{login.password}">Password</label>
  <input type="password" id="inputPassword" class="form-control" th:placeholder="#{login.password}" required>
  <div class="checkbox mb-3">
    <label>
      <input type="checkbox" value="remember-me" th:text="#{login.remember}">
    </label>
  </div>
  <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
  <p class="mt-5 mb-3 text-muted">&copy; 2017-2020

分类:

技术点:

相关文章: