Springboot5 开发常用技术整合--笔记1-thymeleaf&异常处理

前言

记录学习过程中中途碰到问题及解决办法和查找相关资料的。
现在公司比较流行SpringCloud或阿里的dobbo,这2个技术都需要Springboot,因为Springboot在微服务的基础。
Beetl模板与thymeleaf模板各有什么优势,应该用哪种呢?留后期调研。

微框架,与Spring4一起诞生,比如SpringMVC中的@RestController,使用它可以在web页面直接放回一个json到web页面或app
Springboot可以快速上手,整合了一下子项目(开源框架或第三方开源库)
Springboot可以依赖很少的配置,就可以十分快速的搭建并运行项目。

这里主要采用的基础项目
Spring Boot 2多模块 Spring Boot 项目--之多模块重构
项目下载地址

https://yunpan.360.cn/surl_yFtZK2c74HC (提取码:a184)

涉及到的知识点

  • 构建Springboot项目
  • Springboot接口返回json及jackson使用
  • Springboot热部署
  • Springboot资源属性配置
  • Springboot模板引擎--thymeleaf
  • springboot异常处理

Springboot特点

  • 基于Spring,使开发者快速入门,门槛很低。spring的全家桶提供了非常强大的功能。
  • Springboot可以创建独立运行的应用,而不依赖与容器。
  • 不需要打包成war包,可以放入tomcat中直接运行
  • Springboot提供manven极简配置,不用在看过多的xml(默认配置),切点是会引入狠多你不需要的包
  • Springboot提供可视化的相关功能,方便监控,比如性能,应用的健康程度。
  • Springboot微微服务SpringCloud铺路,SpringBoot可以整合很多各式各样的框架来构建微服务,比如dubbo,thrift

构建Springboot项目(参考之前的文章)

Spring Boot 1第一个项目-helloWorld

#官方地址
https://spring.io/

SpringBoot接口返回json及Jackson的基本演绎

Jackson快速入门-csdn

#添加数据web api方式返回POST
localhost:8080/person/saveApi?name=牵手生活22&age=18&password=123456
#获取模拟数据返回GET
localhost:8080/person/getUserDemo

先看看实现的效果时什么样的,实现中功能修改了2个文件User.java、UserController.java,并增加了Api接口外的类IYounghareJsonResult.java(来自github的LeeJSONResult.java)

saveApi 以api的方式返回--没有jackson
saveApi 以api的方式返回--没有jackson

修改的User.java实体如下

public class User {
    private int id;
    private String name;
    private int age;  //年龄
    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss a",locale = "zh",timezone = "GMT+8")
    private  Date birthday;  //出生年月

    @JsonIgnore //json返回时,忽略
    private String password; //密码

    @JsonInclude(JsonInclude.Include.NON_NULL)  //当数据为空时不返回
    private String desc;  //描述


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                ", password='" + password + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }

}

UserController.java增加了saveApi接口


@RestController  //表示是一个Rest接口  ==@Controller+ @ResponseBody(方法位置)
public class UserController {

    private  final UserRepostory userRepostory;  //没有初始化,这里采用构造器注入的方式

    @Autowired
    public UserController(UserRepostory userRepostory){
        this.userRepostory = userRepostory;

    }

    @PostMapping("/person/save")
    //@RequestBody  ==采用@RestController 不再需要这个
    public User save(@RequestParam String name,@RequestParam int age,@RequestParam String password){

        User user = new User();
        user.setName(name);
        user.setAge(age);
        user.setPassword(password);

        user.setBirthday(new Date());

        user.setDesc(null);

        if (userRepostory.save(user)){
            System.out.printf("用户对象:%s 保存成功\n",user);
        }
        return user;
    }


    @PostMapping("/person/saveApi")
    public IYounghareJSONResult saveApi(@RequestParam String name,@RequestParam int age){

        User user = new User();
        user.setName(name);
        user.setAge(age);

        user.setBirthday(new Date());

        user.setDesc(null);

        if (userRepostory.save(user)){
            System.out.printf("用户对象:%s 保存成功\n",user);
        }
        //return user;
        return IYounghareJSONResult.ok(user);
    }

    @GetMapping("/person/getUserDemo")
    //@RequestBody  ==采用@RestController 不再需要这个
    public User getUserDemo(){

        User user = new User();
        user.setName("牵手");
        user.setAge(18);
        user.setPassword("123456");

        user.setBirthday(new Date());

        user.setDesc(null);


        return user;
    }

}




Get模拟User数据返回

IYounghareJsonResult.java 工具类代码


/**
 *
 * @Title: LeeJSONResult.java
 * @Package com.lee.utils
 * @Description: 自定义响应数据结构
 *              这个类是提供给门户,ios,安卓,微信商城用的
 *              门户接受此类数据后需要使用本类的方法转换成对于的数据类型格式(类,或者list)
 *              其他自行处理
 *              200:表示成功
 *              500:表示错误,错误信息在msg字段中
 *              501:bean验证错误,不管多少个错误都以map形式返回
 *              502:拦截器拦截到用户token出错
 *              555:异常抛出信息
 * Copyright: Copyright (c) 2016
 * Company:Nathan.Lee.Salvatore
 *
 * @author leechenxiang
 * @date 2016年4月22日 下午8:33:36
 * @version V1.0
 */

public class IYounghareJSONResult {

    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();

    // 响应业务状态
    private Integer status;

    // 响应消息
    private String msg;

    // 响应中的数据
    private Object data;

    private String ok;  // 不使用

    public static IYounghareJSONResult build(Integer status, String msg, Object data) {
        return new IYounghareJSONResult(status, msg, data);
    }

    public static IYounghareJSONResult ok(Object data) {
        return new IYounghareJSONResult(data);
    }

    public static IYounghareJSONResult ok() {
        return new IYounghareJSONResult(null);
    }

    public static IYounghareJSONResult errorMsg(String msg) {
        return new IYounghareJSONResult(500, msg, null);
    }

    public static IYounghareJSONResult errorMap(Object data) {
        return new IYounghareJSONResult(501, "error", data);
    }

    public static IYounghareJSONResult errorTokenMsg(String msg) {
        return new IYounghareJSONResult(502, msg, null);
    }

    public static IYounghareJSONResult errorException(String msg) {
        return new IYounghareJSONResult(555, msg, null);
    }

    public IYounghareJSONResult() {

    }

//    public static LeeJSONResult build(Integer status, String msg) {
//        return new LeeJSONResult(status, msg, null);
//    }

    public IYounghareJSONResult(Integer status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    public IYounghareJSONResult(Object data) {
        this.status = 200;
        this.msg = "OK";
        this.data = data;
    }

    public Boolean isOK() {
        return this.status == 200;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    /**
     *
     * @Description: 将json结果集转化为LeeJSONResult对象
     *              需要转换的对象是一个类
     * @param jsonData
     * @param clazz
     * @return
     *
     * @author leechenxiang
     * @date 2016年4月22日 下午8:34:58
     */
    public static IYounghareJSONResult formatToPojo(String jsonData, Class<?> clazz) {
        try {
            if (clazz == null) {
                return MAPPER.readValue(jsonData, IYounghareJSONResult.class);
            }
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (clazz != null) {
                if (data.isObject()) {
                    obj = MAPPER.readValue(data.traverse(), clazz);
                } else if (data.isTextual()) {
                    obj = MAPPER.readValue(data.asText(), clazz);
                }
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     *
     * @Description: 没有object对象的转化
     * @param json
     * @return
     *
     * @author leechenxiang
     * @date 2016年4月22日 下午8:35:21
     */
    public static IYounghareJSONResult format(String json) {
        try {
            return MAPPER.readValue(json, IYounghareJSONResult.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     *
     * @Description: Object是集合转化
     *              需要转换的对象是一个list
     * @param jsonData
     * @param clazz
     * @return
     *
     * @author leechenxiang
     * @date 2016年4月22日 下午8:35:31
     */
    public static IYounghareJSONResult formatToList(String jsonData, Class<?> clazz) {
        try {
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (data.isArray() && data.size() > 0) {
                obj = MAPPER.readValue(data.traverse(),
                        MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }

    public String getOk() {
        return ok;
    }

    public void setOk(String ok) {
        this.ok = ok;
    }
}

代码先保存,方便之后修改或提前
https://yunpan.360.cn/surl_yFtcg3T5PvF (提取码:7df9)


SpringBoot开发环境热部署(可行我好像没有热部署成功)

开发环境中,如果修改某个文件,如修改User或、UserController需要重启服务器
Springboot使用devtools进行热部署

idea中配置Springboot热部署

在pom.xml文件中添加

<!-- 热部署 -->
        <!-- devtools可以实现页面热部署(即页面修改后会立即生效,
            这个可以直接在application.properties文件中配置spring.thymeleaf.cache=false来实现) -->
        <!-- 实现类文件热部署(类文件修改后不会立即生效),实现对属性文件的热部署。 -->
        <!-- 即devtools会监听classpath下的文件变动,并且会立即重启应用(发生在保存时机),
            注意:因为其采用的虚拟机机制,该项重启是很快的 -->
        <!-- (1)base classloader (Base类加载器):加载不改变的Class,例如:第三方提供的jar包。 -->
        <!-- (2)restart classloader(Restart类加载器):加载正在开发的Class。 -->
        <!-- 为什么重启很快,因为重启的时候只是加载了在开发的Class,没有重新加载第三方的jar包。 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <!-- optional=true, 依赖不会传递, 该项目依赖devtools; 
                之后依赖boot项目的项目如果想要使用devtools, 需要重新引入 -->
            <optional>true</optional>
        </dependency>

修改application.properties 添加如下内容(#开始的是注释)

#热部署生效
spring.devtools.restart.enabled=true
#设置重启的目录,添加哪个目录的文件需要重启
spring.devtools.restart.additional-paths=src/main/java
# 为amybatis设置,生成环境可以删除
#restart.include.mapper=/mapper-[\\w-\\.]+jar
#restart.include.pagehelper=/pagehelper-[\\w-\\.]+jar
#排除哪个目录的文件不需要重启
#spring.devtools.restart.exclude=static/**,public/**
#classpath目录下的WEB-INF文件夹内容修改不重启(静态文件的修改不需要重启)
#spring.devtools.restart.exclude=WEB-INF/**
idea设置热部署

SpringBoot 资源文件属性配置application.properties

在pom.xml文件中添加依赖

        <!--#资源文件属性配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

我在使用@configurationProperties注解时 idea弹出 Spring Boot Annotion processor not found in classpath

创建czgResource.properties资源文件、创建Resouce.java类、创建ResourceController.java类


添加类及资源文件

访问路径及效果

localhost:8080/resouce/getResouce
访问的效果

czgResource.properties


com.younghare.opensource.name=牵手生活--简书
com.younghare.opensource.website=https://www.jianshu.com/u/e09dc5872735
com.younghare.opensource.desc=笔记整理。分享是一种美德,牵手生活,携手前行
# spring使用@Value标签读取.properties文件的中文乱码问题的解决 https://blog.csdn.net/J3oker/article/details/53839210

CzgResouce.java


@Configuration  //表示要引用资源文件的配置
@ConfigurationProperties(prefix="com.younghare.opensource")
@PropertySource(value= "classpath:czgResource.properties")

public class CzgResouce {
    private String name;
    private String webSite;



    private String desc;

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getWebSite() {
        return webSite;
    }

    public void setWebSite(String webSite) {
        this.webSite = webSite;
    }

}

ResourceController.java

@RestController  //表示是一个Rest接口  ==@Controller+ @ResponseBody(方法位置)
public class ResourceController {

    @Autowired
    private CzgResouce czgResouce;
    @RequestMapping("/resouce/getResouce")
    public IYounghareJSONResult getCzgResouce(){
        CzgResouce bean_Czg_resouce = new CzgResouce();
        BeanUtils.copyProperties(czgResouce, bean_Czg_resouce);
        return IYounghareJSONResult.ok(bean_Czg_resouce);

    }
}


SpringBoot 资源文件配置server以及tomcat及端口

Springboot默认是使用tomcat,更多细节参考

SpringBoot配置属性之Server

修改application.properties文件如下:

#####################################################
#
# Server 服务器端相关配置
#####################################################
#配置api端口
server.port=8081
#设定应用的context-path,一般来说这个配置在正式发布的时候不配置
#server.servlet.context-path=/IYounghare
#错误页面,指定发生错误时,跳转的URL -->BasicErrorController
server.error.path=/error
#session最大超时时间(分钟),默认30分钟
server.servlet.session.timeout=60
#该服务器绑定IP地址,启动服务器时如果本机不是该IP地址则抛出异常,启动失败,
#只有特殊需求的情况下才配置,具体根据各自的业务来设置

#####################################################
#
# Server --tomcat服务器端相关配置
#####################################################
#tomcat最大的线程数,默认200
#server.tomcat.max-threads=180
#tomcat的URI编码
server.tomcat.uri-encoding=UTF-8
#存放tomcat的日志,dump等文件的临时文件夹,默认为系统的tmp文件夹
#如:c:\users\younghare\AppData\Local\Temp\
server.tomcat.accesslog.enabled=true
#server.tomcat.accesslog.pattern=
#accesslog目录,默认在basedir/logs
#server.tomcat.accesslog.directory=
#日志文件目录
logging.path= D:/czg/czgTemp/springboot-tomcat-tmp
#日志文件名称,默认为spring.log
#logging.file= myapp.log
日志文件名及日志文件路径

SpringBoot 整合模板引擎freemarker(略)、thyemleaf

在resources资源下创建templates文件夹


创建templates文件夹
整合thymeleaf

在pom.xml中添加依赖

        <!-- 引入 thymeleaf 模板依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

在application.properties 添加thymeleaf的配置信息


#####################################################
#
#thymeleaf 静态资源配置
#
#####################################################
#前缀,默认文件夹
spring.thymeleaf.prefix=classpath:/templates/
#后缀时.html
spring.thymeleaf.suffix=.html
#模式使用html5
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
#deprecated configuration property spring.thymeleaf.content-type 意思是这个属性已经过时不被使用
#spring.thymeleaf.content-type=text/html
#content-type 是text和html
spring.thymeleaf.servlet.content-type=text/html 
# 关闭缓存,即时刷新,上线生成环境需要改为true
spring.thymeleaf.cache=false

springboot集成themeleaf报Namespace 'th' is not bound

thymeleaf增加的文件

ThymeleafController.java

@Controller
@RequestMapping("th_czg") //设置访问路径
public class ThymeleafController {



    @RequestMapping("dynamicThymeleaf")
    public String dynamicThymeleaf(ModelMap map) {
        map.addAttribute("name", "thymeleaf-为什么进不了这个断点"); ////需要SpringMVC的依赖库
        return "thymeleaf/dynamicThymeleaf/dynamicThymeleafDemo";
    }
    @RequestMapping("staticThymeleaf")
    public String staticThymeleaf() {
        return "thymeleaf/staticThymeleaf/thymeleafDemo";
    }
}

thymeleafDemo.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
Thymeleaf模板引擎--静态demo
<h1>thymeleafDemo page</h1>

</body>
</html>

dynamicThymeleafDemo.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head >
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
Thymeleaf模板引擎

<!-- 注意: th 等thymeleaf表达式 ,在直接用浏览器打开该html文件时,不会被加载,只有后台有返回对应的值时才有对应的内容-->

<h1 th:text="${name}">hello world~~~~~~~</h1>
</body>
</html>

测试地址

#测试静态地址
http://localhost:8080/th_czg/staticThymeleaf
#加载动态
http://localhost:8080/th_czg/dynamicThymeleaf
成功加载静态thymeleaf
加载动态thymeleaf失败--需要查原因
Whitelabel Error Page
This application has no configured error view, so you are seeing this as a fallback.

Sun Mar 31 23:30:21 CST 2019
There was an unexpected error (type=Internal Server Error, status=500).
Failed to invoke handler method with resolved arguments: [0][type=org.springframework.validation.support.BindingAwareConcurrentModel][value={}] on public java.lang.String com.younghare.springBoothelloworld.controller.ThymeleafController.dynamicThymeleaf(org.springframework.ui.ModelMap)

网络解决方案--好像也没办法解决----有哪位大神帮我留言一下
http://javaetmoi.com/2017/12/migration-spring-web-mvc-vers-spring-webflux/
【spring boot学习】Model&ModelMap&ModelAndView--csdn

在pom.xml中引入SpringMVC的支持,就解决了

        <!--添加springmvc的支持  public String test(ModelMap map)//需要SpringMVC的依赖库-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

thymeleaf常用标签的使用方法

thymeleaf的简单用法-常用标签-csdm

  • 基本使用方式
  • 对象引用方式
  • 时间类型转换
  • text 与utext
  • URL
  • 引入静态资源文件js/css
  • 条件判断 th:if 或th:unless
  • 循环th:each
  • th:swith与th:case

thymeleaf综合例子test.html,来自github

#测试路径
http://localhost:8080/th_czg/test
<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head lang="en">
    <meta charset="UTF-8" />
    <title></title>

    <!--    <script th:src="@{/static/js/test.js}"></script> -->

</head>
<body>

<div>
    用户姓名:<input th:id="${user.name}" th:name="${user.name}" th:value="${user.name}"/>
    <br/>
    用户年龄:<input th:value="${user.age}"/>
    <br/>
    用户生日:<input th:value="${user.birthday}"/>
    <br/>
    用户生日:<input th:value="${#dates.format(user.birthday, 'yyyy-MM-dd')}"/>
    <br/>
</div>

<br/>

<div th:object="${user}">
    用户姓名:<input th:id="*{name}" th:name="*{name}" th:value="*{name}"/>
    <br/>
    用户年龄:<input th:value="*{age}"/>
    <br/>
    用户生日:<input th:value="*{#dates.format(birthday, 'yyyy-MM-dd hh:mm:ss')}"/>
    <br/>
</div>

<br/>

text 与 utext :<br/>
<span th:text="${user.desc}">abc</span>
<br/>
<span th:utext="${user.desc}">abc</span>
<br/>
<br/>

URL:<br/>
<a href="" th:href="@{http://www.imooc.com}">网站地址</a>
<br/>

<br/>
<form th:action="@{/th/postform}" th:object="${user}" method="post" th:method="post">
    <input type="text" th:field="*{name}"/>
    <input type="text" th:field="*{age}"/>
    <input type="submit"/>
</form>
<br/>

<br/>
<div th:if="${user.age} == 18">十八岁的天空</div>
<div th:if="${user.age} gt 18">你老了</div>
<div th:if="${user.age} lt 18">你很年轻</div>
<div th:if="${user.age} ge 18">大于等于</div>
<div th:if="${user.age} le 18">小于等于</div>
<br/>

<br/>
<select>
    <option >选择框</option>
    <option th:selected="${user.name eq 'lee'}">lee</option>
    <option th:selected="${user.name eq 'imooc'}">imooc</option>
    <option th:selected="${user.name eq 'LeeCX'}">LeeCX</option>
</select>
<br/>

<br/>
<table>
    <tr>
        <th>姓名</th>
        <th>年龄</th>
        <th>年龄备注</th>
        <th>生日</th>
    </tr>
    <tr th:each="person:${userList}">
        <td th:text="${person.name}"></td>
        <td th:text="${person.age}"></td>
        <td th:text="${person.age gt 18} ? 你老了 : 你很年轻">18岁</td>
        <td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd hh:mm:ss')}"></td>
    </tr>
</table>
<br/>

<br/>
<div th:switch="${user.name}">
    <p th:case="'lee'">lee</p>
    <p th:case="#{roles.manager}">普通管理员</p>
    <p th:case="#{roles.superadmin}">超级管理员</p>
    <p th:case="*">其他用户</p>
</div>
<br/>

</body>
</html>

Springboot异常处理

Springboot配置全局的异常捕获--web页面跳转

页面跳转形式
ajax形式
统一返回异常的形式

创建模拟异常的ErrorController.java


@Controller
@RequestMapping("err")
public class ErrorController {

    @RequestMapping("/error")
    public String error() {

        int a = 1 / 0;    //除零错误

        return "thymeleaf/error";//不存在的地址
    }

    @RequestMapping("/ajaxerror")
    public String ajaxerror() {

        return "thymeleaf/ajaxerror";
    }

    @RequestMapping("/getAjaxerror")
    @ResponseBody
    public IYounghareJSONResult getAjaxerror() {

        int a = 1 / 0;

        return IYounghareJSONResult.ok();
    }
}
#故意除零异常
http://localhost:8080/err/error

springMVC出现Cannot resolve symbol 'HttpServletRequest

在pom.xml中引入SpringMVC的支持

        <!--添加springmvc的支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

访问网页http://localhost:8080//err/error 发现错误如下图

除零错误提示

编写我们自己的异常处理类IYounghareExceptionHandler.java
注意注解部分

@ControllerAdvice  //spring mvc提供了ControllerAdvice注解对异常进行统一的处理,拿到这些异常信息后,可以做一些处理,比如提供一个统一的web界
public class IYounghareExceptionHandler {

    public static final String YOUNGHARE_ERROR_VIEW = "thymeleaf/error";

    @ExceptionHandler(value = Exception.class)
    public Object errorHandler(HttpServletRequest reqest,
                               HttpServletResponse response, Exception e) throws Exception {

        e.printStackTrace();

        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", reqest.getRequestURL());  //发生错误的地址
        mav.setViewName(YOUNGHARE_ERROR_VIEW);  //设置错误页面
        return mav;
    }

}

编写自己的error.html提示页面

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head lang="en">
    <meta charset="UTF-8" />
    <title>捕获全局异常</title>
</head>
<body>

拦截到了我们捕获的异常,我们可以根据项目需要美化该页面
<h1 style="color: red">发生错误:</h1>
<div th:text="${url}"></div>
<div th:text="${exception.message}"></div>
</body>
</html>

Spring Boot 系列(八)@ControllerAdvice 拦截异常并统一处理

i自定义异常处理页面的显示效果
SpringBoot配置全局的异常捕获 - ajax形式
测试地址:
http://localhost:8080//err/ajaxerror

在application.properties文件中为SpringMVC 设置static地址

#Spring Boot的默认静态资源的路径为:===主要是js的文件如ajaxerror.js
#spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
#优先级从从高到低。
spring.mvc.static-path-pattern=/static/**
在chrome中开发者选项中设置debug 断点

到这里先把工程做个备份

https://yunpan.360.cn/surl_yLKZ52FG45K (提取码:1a6e)

统一异常处理

定义自己的异常处理类
用@ControllerAdvice+@ExceptionHandler(ServiceException.class) //要捕获的异常类

统一异常处理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容