spring boot做web开发,十分方便,直接在pom.xml文件中引入web场景启动器依赖即可。省去了spring MVC的配置文件,这样我们就可以把重心放在编写我们的业务代码了。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring boot其实就是spring框架,只是避免了spring框架整合其他框架搭建的许多配置文件,spring boot在底层都为我们做好了。
web模块自动配置原理主要关注WebMvcAutoConfiguration
这个class文件。
一、静态资源引入方式
- 1、webjar库,https://www.webjars.org/,主要以jar包的方式引入静态资源,从源码得知,所有的/webjars/**,都去
classpath:META-INF/resources/webjars/
找资源
- 2、/** 访问当前项目的任何资源(静态资源的文件夹)
从WebMvcAutoConfiguration.class
中的addResourceHandlers
方法跳到ResourceProperties
中的getStaticLocations
方法,得知如下的静态资源类路径数组,这里面文件夹里存放的静态资源都可以被访问到,只要没被拦截处理就到这4个路径下找静态资源。
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
};
//项目结构中,main路径下的java和resource都是类根路径。
-
3、欢迎页,其实就是当访问/**时自动映射到上面提到的4个静态资源文件夹下的index.html页面。
二、Thymeleaf模板引擎
引入thymeleaf,springboot默认不支持jsp,推荐使用thymeleaf模板引擎
<!--添加pom.xml依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--修改属性版本值-->
<properties>
<!--thymeleaf3版本的主程序则得适配layout2版本以上-->
<springboot-thymeleaf.version>3.0.9.RELEASE</springboot-thymeleaf.version>
<thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version>
</properties>
语法
推荐好文:thymeleaf3语法详析
记得在写thymeleaf页面的时候,要先声明名称空间 xmlns:th="http://www.thymeleaf.org
,这样才能获得thymeleaf语法提示,thymeleaf是支持数据快速渲染的。
<!DOCTYPE html>
<!--名称空间-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Thymeleaf 开始</title>
</head>
<body>
<p>基本跟jsp用法类似,在html中渲染传输数据,只是有自己的一套规则</p>
</body>
</html>
附:IDEA写springboot时,当我们修改html等静态资源时不需要重启服务器才能看效果,实现热部署,参考文章:https://blog.csdn.net/qq_33172029/article/details/91412810
三、springboot对springMVC配置的解析
1、springboot对springMVC的自动配置原理
从WebMvcAutoConfiguration.class
文件中的viewResolver视图解析方法,跳进ContentNegotiatingViewResolver这个类中,得知其实就是组合容器中所有的视图解析器的,于是我们也可以自定义一个视图解析器然后被自动组合进来。
2、扩展springMVC
新建配置类,加上注解@Configuration
,继承WebMvcConfigurerAdapter
类,不能标注@EnableWebMvc
3、全面接管springMVC
在配置类加上一个注解@EnableWebMvc
,表示springboot不对springMVC做自动配置了,需求什么springMVC组件就都自己配置,比如视图解析器。
四、thymeleaf+bootstrap开发国际化登录页面
国际化:指的是页面根据浏览器的语言信息自动切换语言显示。
步骤:
1、创建一个i18n包,在里面放置3个国际化属性文件,分别是默认语言,中文,英文。
2、springboot自动配置好了管理国际化资源文件的组件了,我们只需要在配置文件中加入spring.messages.basename=i18n.login
即可。
3、在页面使用thymeleaf的标签属性格式进行数据渲染,获取国际化的值。
注意:IDEA编写.properties属性文件的时候在运行时页面默认是会中文乱码的,得去other settings-->default setting(全局默认设置)-->file encoding里设置UTF-8编码并且自动转换为ASCALL码,这样页面就不会中文乱码了。
从上图源码可以看出默认是在请求头中的区域信息获取Locale进行国际化的,我们可以通过自己编写一个MyLocaleResolver的bean注册到容器中,来从链接中解析获取Locale,然后把这个bean给注册到容器中生效,这样底层源码检测到容器中有这个bean就不会去默认请求头中获取Locale了。
实现点击链接的方式切换国际化功能,其实就是在点击链接时带上参数,以下贴源码
<!--没贴上css样式-->
<body class="body">
<!--
If we can only encounter each other rather than stay with each other,
then I wish we had never encountered.
如果只是遇见,不能停留,不如不遇见。
-->
<div class="container mask">
<img th:src="@{/images/login_face.png}" style="margin-top: 50px">
<h1 th:text="#{login.title}">项目管理系统</h1>
<form action="/TBMS/login" method="post">
<div class="form-group">
<!--thymeleaf判断msg不为空再出现提示-->
<span th:if="${not #strings.isEmpty(msg)}"><img th:src="@{/images/login_tip.png}" /></span>
<label class="error" th:text="${msg}"></label>
</div>
<div class="form-group input-group">
<span><img th:src="@{/images/nickname.png}" /></span>
<input class="input" type="text" name="userId" th:placeholder="#{login.userid}"/>
</div>
<div class="form-group input-group">
<span><img th:src="@{/images/locking.png}" /></span>
<input class="input" type="password" name="password" th:placeholder="#{login.password}"/>
</div>
<button class="btn" th:text="#{login.btn}">登录</button>
</form>
<div style="margin-top: 20px;">
<a th:href="@{/toLogin(l='zh_CN')}">中文</a>
<a th:href="@{/toLogin(l='en_US')}" style="margin-left: 10px">English</a>
</div>
</div>
</body>
编写自定义区域解析器MyLocaleResolver.java
/**
* FileName: MyLocaleResolver
* Author: 小江
* Date: 2019/12/18 22:49
* History:
*/
package com.zhbit.xiaojiang.component;
import org.springframework.web.servlet.LocaleResolver;
import org.thymeleaf.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
/**
*@Author 小江 [com.zhbit]
*@Date 2019/12/22 16:37
*Description 自定义区域信息解析器
*/
public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String str = httpServletRequest.getParameter("l");
//初始化先使用默认的区域信息
Locale locale = Locale.getDefault();
if(!StringUtils.isEmpty(str)){
String[] split = str.split("_");
//根据传来的区域信息创建一个locale对象
locale = new Locale(split[0],split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
注入自定义区域解析器bean
/**
* FileName: WebConfig
* Author: 小江
* Date: 2019/12/18 21:20
* History:
*/
package com.zhbit.xiaojiang.config;
import com.zhbit.xiaojiang.component.MyLocaleResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@ComponentScan
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
/**
*@Author 小江 [com.zhbit]
*@Date 2019/12/18 22:58
*Description 注册自定义的区域信息解析bean到容器中
*/
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
}
最后配置声明国际化资源文件的基础名称以及编写国际化资源文件
经过以上步骤,登录页国际化功能就实现了。
注意:springboot项目中无法直接访问templates下的html文件时,可以添加如下配置即可直接访问templates下的页面:
开发期间,IDEA修改thymeleaf模板引擎代码,页面是不会实时生效的,得在配置文件中先禁用模板引擎的缓存
spring.thymeleaf.cache=false
,之后IDEA修改完后按ctrl+f9重新编译即可实时生效了。
五、thymeleaf公共片段元素抽取
管理系统后台的每个页面一般都是有固定的顶部导航栏和侧边菜单栏,如果每次写页面都要复制粘贴这部分代码的话就显得页面代码很冗余,所以thymeleaf页面可以抽取出来页面固定的部分作为公共片段,然后其他页面如果需要就直接引入公共片段就好。
比如在index.html中抽取顶部导航栏,在nav标签里写上th:fragment="topbar"
,即定义抽取片段名
<nav class="navbar navbar-default top-navbar" role="navigation" th:fragment="topbar">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".sidebar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" th:href="@{/admin/index}"><i class="fa fa-comments"></i> <strong>项目管理系统</strong></a>
</div>
</nav>
然后在empty.html中引入顶部导航栏,th:replace="~{/admin/index::topbar}"
,即引入页面路径名::抽取片段名
<!--引入抽取出来的顶部导航栏-->
<div th:replace="~{/admin/index::topbar}"></div>
关于thymeleaf引入抽取片段有3种方式,效果各有不同
-
<div th:insert="~{/admin/index::topbar}"></div>
,效果是无保留的把topbar片段插入到div标签中 -
<div th:replace="~{/admin/index::topbar}"></div>
,效果是把top片段替换掉div标签 -
<div th:include="~{/admin/index::topbar}"></div>
,效果是只取top片段的内容合入到div标签中