SpringBoot与web
1.对静态资源的映射
1.1 webjars
以jar包的形式引入web静态资源,webjars官网里面有一些常用的web前端框架的依赖。
1.2 本地资源
将静态资源(js、css、images)放在下列文件夹中即可,推荐放在static文件夹下。
class:/META-INF/resources/,
class:/resources/,
class:/static/,
class:/public/,
当前项目跟路径
如果要直接访问静态文件直接在浏览器输入:localhost:8080:/js/FileName.js
就可以访问,如果访问显示找不到路径,可以考虑重现启动项目和刷新maven的pom.xml。
1.3 欢迎页
默认的欢迎页是 index.html
将这个文件放在静态资源下,再次访问localhost:8080
就会直接访问到index.html
1.4 图标
springboot项目默认使用的图标是spring的叶子图标,如果想要修改自己的图标,只需要将图标名称改为:favicon.ico
。如果不显示可以尝试刷新maven的pom.xml。
2.模板引擎 Thymeleaf
2.1 Thymelefa
2.1.1 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.2.2 使用
只要把html界面放在类路径下的themlates
文件夹下,thymeleaf就会自动渲染。
2.2.2.1 导入名称空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
2.2.2.2 语法规则
th:任意html属性;来替换html的值
2.2.2.3 表达式语法
- ${…}:获取变量值
- *{…}:变量的选择表达式;与${…}基本相同,配合th:object来使用。
使用*{...}
<div th:object=${session.user}>
<p>*{name}</p>
<p>*{address}</p>
</div>
不使用
<div>
<p>${session.user.name}</p>
<p>${session.user.address}</p>
</div>
- #{…}:获取国际化内容
- @{…}:定义URL
<a href="index.html"
th:href="@{localhost:8080/hello(name=${user.name})}>
</a>
- ~{…}:片段引用
- 在表达式中还支持:字面量、文本操作、数学运算、布尔运算、比较运算、条件运算(三元运算符)。
2.2.2.4 Thymeleaf语法小demo
controller:
@RequestMapping("/success")
public String success(Map<String ,Object> map){
map.put("hello","<h1>你好!</h1>");
map.put("users", Arrays.asList("吴彦祖","梁朝伟","彭于晏"));
return "success";
}
html:
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>成功</h1>
<div th:text="${hello}"></div>
<hr>
<div th:utext="${hello}"></div>
<hr>
<!-- th:each写在的标签上,每次遍历都会生产改标签。-->
<h2 th:text="${user}" th:each="user : ${users}"></h2>
<hr>
<!-- th:text/th:utext的行内写法:[[${user}]]/[(${user})]-->
<div>
<span th:text="${usera}" th:each="usera : ${users}"></span>
</div>
</body>
</html>
成果:
2.2.2.5 小补充
th:text
的行内写法:[[${user}]]
th:utext
的行内写法:[(${user})]
3.扩展SpringMVC功能
在SpringBoot2.*
版本中如果自己扩展SpringMVC配置继承WebMvcConfigurer
类之后,就会关闭SpringBoot的自动配置,就会找不到引入的静态文件。所以改为实现WebMvcConfigurer
接口就可以实现在SpringBoot1.*中继承WebMvcConfigurationAdapter
一样的功能。
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
// 在请求/at路径时,会跳转到success界面。
registry.addViewController("/at").setViewName("success");
}
既保留的所有的自动配置也能用扩展的。
SpringMVC的自动配置和自己的扩展配置都能调用。
全面接管SpirngMVC:
关闭SpringBoot对SpirngMVC的自动配置,所有的东西都有自己来配置。只需要在配置类中添加@EnableWebMvc
。
4. 一个crud的demo
4.1 引入静态资源
将本地的css、js、image放在resources/static
下
引入Bootstrap依赖:
<!-- bootstrap 依赖-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>4.0.0</version>
</dependency>
修改html代码中引用本地css、image、js的路径:
<!-- jar包依赖中的bootstrap-->
<link href="asserts/css/bootstrap.min.css" th:href="@{webjars/bootstrap/4.0.0/css/bootstrap.css}" rel="stylesheet">
<!-- static下的signin.css-->
<link href="asserts/css/signin.css" th:href="@{asserts/css/signin.css}" rel="stylesheet">
4.2 国际化
4.2.1 编写国际化配置文件
在resources
文件中新建文件夹i18n
然后在i18n
中新建文件夹index(被国际化界面的名称)
然后新建index.properties
再新建index_en(语言简称)_US(国家简称).propertices
之后idea就会识别是要做国际化,会变成如图所示的文件夹名称;再右键index
文件夹就会在new
里面出现add propertices
,添加即可;idea提供了语言可视化,将想要国际化的部分命名和添加默认显示内容,特定国家显示内容就ok;如果使用可视化工具会报错,只能编写配置文件了。
index_zh_CN.properties:
index.btn=登录
index.password=密码
index.remember=记住我
index.tip=请登录
index.username=用户名
index.properties:
index.btn=登录1
index.password=密码
index.remember=记住我
index.tip=请登录
index.username=用户名
index_en_US.properties:
index.btn=login
index.password=password
index.remember= remember me
index.tip=Please Sing in
index.username=username
idea国际化可视化:
文件结构
4.2.2 配置国际化配置文件
在SpringBoot中自动配置了国际化,只需要将国际化的配置文件放在根目录下的message
文件夹下,但是创建的文件夹为i18n
,所以需要去主配置文件下指定:spring.messages.basename=i18n.index
4.2.3 界面获取国际化的值
使用thymeleaf模板中的#{Object.value}
可以获取国际化文件中的内容,并且会根据浏览器的设置来显示国际化内容。
乱码原因:idea中的properties文件没有进行编码最终会转换为Ascii码。
解决方法:file->setting->搜索file encoding->将下面改为utf-8并且选上自动转为ascii
可以了。
4.2.4 自己选择语言
- 编写自己的本地语言解析
public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
String l =null;
l = request.getParameter("l");
Locale locale = Locale.getDefault();
if (!StringUtils.isEmpty(l)){
String[] split = l.split("_");
locale = new Locale(split[0],split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
- 在SpringMVC配置文件中导入容器
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
- 前台传入语言参数
<a class="btn btn-sm" th:href="@{/(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/(l='en_US')}">English</a>
4.3 登录
在开发期间关闭thymeleaf的缓存spring.thymeleaf.cache=false
,就可以使用ctrl+f9
对项目于进行重新编译,不需要重启项目,就可以看到界面的变化,可以节省时间。
controller:
@Controller
public class LoginController {
@PostMapping(value = "/user/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
Map<String ,Object> map){
if (!StringUtils.isEmpty(username) && "123456".equals(password)){
// 防止表单重复提交,重定向到主页
return "redirect:/main.html";
}
map.put("msg","用户名或者密码错误");
return "index";
}
}
在mvc配置中新增:
registry.addViewController("/main.html").setViewName("dashboard");
拦截器进行登录检查
public class LoginHandleInterceptor implements HandlerInterceptor {
// 目标方法执行之前进行检查
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object username = request.getSession().getAttribute("username");
if (username == "" || username == null){
// 未登录
request.setAttribute("msg","没有权限,请登录");
request.getRequestDispatcher("/index.html").forward(request,response);
return false;
}else {
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
在SpringMVC配置中注册拦截器:
// 注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandleInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/index.html","/","/user/login");
}
4.4 查询全部员工
在开发时有些html代码是重复的,那么就可以用:
thymeleaf抽取模板
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Company name</a>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign out</a>
</li>
</ul>
</nav>
thymeleaf引入抽取的模板
有三种方式:
<div th:insert="~{dashboard::topbar}"></div>
<div th:replace="~{dashboard::topbar}"></div>
<div th:include="~{dashboard::topbar}"></div>
- insert:将公共片段插入指定元素中
- replace:将声明引入的元素替换为公共片段
- include:将被引入的片段内容包含进标签中。
就写到这里。
5.错误处理机制
5.1 错误处理机制原理
一旦系统发生4xx或者5xx的错误,ErrorPageCustomizer
就会生效,来到error
请求进行处理;BasicErroorController
会处理默认的error
请求,里面有两个方法,分别返回json
和html
;DefaultErrorViewResolver
会响应界面,如果错误存在就会返回界面,否则就返回null;
SpringBoot就会去找error
下的错误代码界面;可以使用模板引擎去解析界面;
5.2 自定义错误处理界面
5.2.1 有模板引擎
在templates
下新建文件夹error
,在新建错误代码文件(404.html…),发生状态码的错误就会响应指定的界面,如果命名为4xx.html
那么如果出现4**的错误就会返回到4xx.html
;5xx
也一样;精确状态码优先于xx界面。
5.2.2 没有模板引擎
在静态资源文件夹下找,
5.2.3 以上都没没有
来到SpringBoot默认的错误界面
5.3 自定义Json数据
返回自定义的Json数据,新建一个异常处理器,但是现在的代码有一个缺点,并不会自适应返回Json或者是html界面,也就是说无论是界面访问还是Postman客户端访问都会返回Json数据。
// 异常处理器
@ControllerAdvice
public class MyExceptionHandler {
@ResponseBody
@ExceptionHandler(UserNotExitException.class)
public Map<String, Object> handleException(Exception e){
Map<String ,Object> map = new HashMap<>();
map.put("code","user.notExist");
map.put("message",e.getMessage());
return map;
}
}
所以转发到/error
来实现自适应的效果
@ExceptionHandler(UserNotExitException.class)
public String handleException(Exception e,
HttpServletRequest request){
Map<String ,Object> map = new HashMap<>();
// 传入自己的异常代码
request.setAttribute("javax.servlet.error.status_code",500);
map.put("code","user.notExist");
map.put("message",e.getMessage());
return "forward:error";
}
上面的方法并不会携带自己定制的数据;
在出现错误时响应出去的数据是由getErrorAttribbutes
得到的,所以解决方法是:
- 编写一个
ErrorController
的实现类(或者是AbstractErrorController
的子类)放在容器中; - 界面上和Json中的数据都是由
errorAtttibutes.getErrorbutes
得到的
final:
修改上方的异常处理器:
// 异常处理器
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(UserNotExitException.class)
public String handleException(Exception e,
HttpServletRequest request){
Map<String ,Object> map = new HashMap<>();
// 传入自己的异常代码
request.setAttribute("javax.servlet.error.status_code",500);
map.put("code","user.notExist");
map.put("message",e.getMessage());
request.setAttribute("ext",map);
return "forward:/error";
}
}
新增自定义错误信息:
//给容器中添加自己定义的错误信息
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
// 返回的map就是界面和Json能获取的所有数据
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
map.put("company","jm");
// 异常处理器中携带的数据
Map<String ,Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", 0);
map.put("ext",ext);
return map;
}
}
最终实现了:返回自适应html和Json,以及自定义错误信息。
6. 嵌入式Servlet容器
SpringBoot使用的是嵌入式的servlet,Tomact实际上也是一个Servlet。
6.1修改Servlet容器的相关配置
- 修改和server有关的配置(application.properties):
server.prot=8080
#修改Servlet容器设置
server.xxx
#修改Tomcat配置
server.tomcat.xxx
- 编写一个EmbeddedServlletContainerCustomizer(在SpringBoot2.x中替换为
WebServerFactoryCustomizer
):嵌入式Servlet容器的定制器:
在自己的配置中添加:
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
// 自定义嵌入式Servlet
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8081);
}
};
}
6.2 注册servlet三大组建(Servlet,Filet,Listener)
6.2.1 注册Servlet
@Bean
public ServletRegistrationBean myServlet(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet");
return servletRegistrationBean;
}
6.2.2 注册Filter
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
// 设置拦截器
filterRegistrationBean.setFilter(new MyFilter());
// 设置拦截路径
filterRegistrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
return filterRegistrationBean;
}
6.2.3 注册Listener
@Bean
public ServletListenerRegistrationBean myListener(){
ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean(new MyListener());
return registrationBean;
}
6.2.4 使用其它的嵌入式Servlet容器
Jetty(长连接:聊天)
Undertw(不支持Jsp)
将web依赖中的Tomcat依赖排除再引入想要切换的依赖就行
undertow:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
Jetty:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
笔记总结自:B站BV1Et411Y7tQ
27~48