Spring Boot 项目中在 src/main/resources 下面有两个文件夹,static 和 templates。

static  static 目录中存放静态页面。Spring Boot 通过 classpath/static(classpath 指 resources 根目录)目录访问静态资源。

Templates  templates 中存放动态页面,在 Spring Boot 中不推荐使用 jsp 作为视图层技术,而是 默认使用 Thymeleaf 来做动态页面。Templates 目录则是存放类似于 Thymeleaf 这样的模板引擎。
SpringBoot 默认指定的可以存放静态资源的目录位置 
 classpath:/META-INF/resources/      ##需创建/META-INF/resources/ 目录 
 classpath:/resources/                     ## 需创建/resources/目录 
 classpath:/static/                           ## 工具自动生成的 static 目录,也是用的最多的目录 
 classpath:/public/                          ## 需创建/public/ 目录 

在上面四个目录下存放静态资源(比如:login.html 等),可以直接访问(http://localhost:8080/login.html)。它们的优先级从上到下。所以,如果 static 里面有个 index.html,public 下面也有个 index.html,则优先会加载 static 下面的 index.html,因为优先 级高!而且 Spring Boot 默认的首页是放在任一个静态资源目录下的 index.html 。
2.把静态资源打成 jar 包引入系统后供
       由于我们把 Web 项目最后会打成 Jar 包,发布线上。引入 Bootstrap,jQuery 等静态资 源文件就不能放在 Webapp 文件夹下(也没有 Webapp 文件夹),我们必须通过把静态资源打 成 Jar 包,添加至 pom.xm,如我们将 jquery 引入到项目中,依赖如下:
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
导入后,查看org.webjars:jquery的目录文件:
org.webjars:jquery:3.3.1 
    ->jquery-3.3.1.jar 
       ->META-INF 
       ->maven 
       ->resources 
          ->webjars 
        ->jquery 
           ->3.3.1 
             ->jquery.js 
             ->jquery.min.js 
所有/webjars/*都从 classpath:/META-INF/resources/webjars/路径下去找对应的静态资源。 所以我们启动项目,访问:http://localhost:8080/webjars/jquery/3.3.1/jquery.js 即可。
 前端常用的模板引擎有 JSP、Velocity、Freemarker、Thymeleaf 等,模板引擎的作用是把数据和静态模板进行绑定,生成我们想要的 HTML。Spring Boot 推荐使用 Thymeleaf,语法简单、功能强大。
  传统的 JSP+JSTL 组合已经过去了,Thymeleaf 是现代服务端的模板引擎。Thymeleaf 的主要目标是将优雅的自然模板带到开发工作流程中,并将 HTML 在浏 览器 中正确显示,并且可以作为静态原型,让开发团队能更容易地协作。Thymeleaf 能够 处理 HTML,XML,JavaScript,CSS 甚至纯文本。
1.引入 thymeleaf 
在 pom.xml 中引入 thymeleaf 依赖,如下:

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-thymeleaf</artifactId> 
</dependency> 
Thymeleaf 模板的默认位置放在 classpath:/templates/目录下,默认的后 缀是 html,thymeleaf 就能自动渲染。thymeleaf 使用步骤如下:
    使用thymeleaf在页面导入 thymeleaf 的命名空间,以获得更好的提示,代码如下:
 <html xmlns:th="http://www.thymeleaf.org">

创建 controller,代码如下:
//UserController
package com.beixi.helloworld.controller;
@Controller
public class UserController {
    @GetMapping("/index")
    public String index(Model model){
        List<User> list=new ArrayList<>();
        for (int i = 0; i <5 ; i++) {
            User u=new User();
    
        u.setId(i);
            u.setName("贝西 "+i);
            u.setAddress("山西 "+i);
            list.add(u);
        }
        model.addAttribute("list", list);
        return "index";
    }
}
class User{
    private int id;
    private String name;
    private String address;
    //省略get/set方法
  }




在 controller 中我们返回视图层和数据,我们需要在 classpath:/templates/目录下新建一个 视图层名为 index.html 的 thymeleaf 模板文件。
创建 thymeleaf 模板,代码如下:
//index.html
<!DOCTYPE html>
<!--引入命名空间-->
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table border="1" width="60%" align="center">
        <tr>
            <td>编号</td>
            <td>姓名</td>
            <td>地址</td>
        </tr>
        <tr th:each="user:${list}">
            <td th:text="${user.id}"></td>
            <td th:text="${user.name}"></td>
            <td th:text="${user.address}"></td>
        </tr>
    </table>
</body>
</html>
在 thymeleaf 中,通过 th:each 指令来遍历集合,数据的展示通过 th:text 来实现。配置完 成后启动项目,访问 http://localhost:8080/index,就可以看到数据集合。
      另外,thymeleaf 支持在 js 中直接获取 Model 中存储的变量,代码如下:
@Controller
public class UserController {
    @GetMapping("/index")
    public String index(Model model){
        model.addAttribute("name", "贝西");
        return "index";
    }
}
在页面模板中直接通过js来获取,代码如下:
<script th:inline="javascript">
    var name= [[${name}]]
    console.log(name);
</script>
Thymeleaf 中常用的表达式

 

 

 

Thymeleaf 中常用的标签

 

 

 

Thymeleaf 中常用的函数

 

 

 

在项目开发中,接口与接口之间,前后端之间数据的传输都使用Json格式,在 Spring Boot 中,接口返回Json格式的数据很简单,在 Controller 中使用@RestController注解即可返回 Json 格式的数据。
 Spring Boot 中默认使用的 JSON 解析框架是 Jackson。
常用数据类型转为 JSON 格式
在实际项目中,常用的数据结构无非有类对象、List 对象、Map 对象,我们看一下默认的 Jackson 框架如何将这三个常用的数据结构转成 JSON 格式的。
1.创建实体类
创建User 类,代码如下:
public class User {
    private int id;
    private String name;
    private String password;
  /* 省略get、set和带参构造方法 */
}
创建 Controller 类
然后我们创建一个 Controller,分别返回 User 对象、List 和 Map,代 码如下:
@RestController
@RequestMapping("/json")
public class JsonController {
    @RequestMapping("/user")
    public User getUser() {
        return new User(10,"贝西","11");
    }
    @RequestMapping("/list")
    public List<User> getUserList() {
        List<User> userList = new ArrayList<>();
        User user1 = new User(1, "贝西", "123456");
        User user2 = new User(2, "贾志杰", "123456");
        userList.add(user1);
        userList.add(user2);
        return userList;
    }
   
 @RequestMapping("/map")
    public Map<String, Object> getMap() {
        Map<String, Object> map = new HashMap<>(3);
        User user = new User(1, "贾志杰", "123456");
        map.put("作者信息", user);
        map.put("博客地址", "https://blog.csdn.net/beixishuo");
        map.put("公众号", "贝西奇谈");
        map.put("B站", "贝西贝西");
        return map;
    }
}
3. 测试不同数据类型返回的 JSON
控制层接口完成后,分别返回了 User 对象、List 集合和 Map 集合。接下来我们依次测试下效果.
    在浏览器中输入:localhost:8080/json/user,返回 JSON 如下:
{id: 10,name: "贝西",password: "11"} 

在浏览器中输入:localhost:8080/json/list,返回 JSON 如下:
[{"id":1,"name":"贝西","password":"123456"},{"id":2,"name":"贾志杰","password":"123456"}]

在浏览器中输入:localhost:8080/json/map,返回 JSON 如下:
{"作者信息":{"id":1,"name":"贾志杰","password":"123456"},"博客地址":"https://blog.csdn.net/beixishuo","公众号":”贝西奇谈”,"B站":"贝西贝西"}
Jackson 中对 null 的处理
 在实际项目中,我们难免会遇到一些 null 值。当我们转 JSON 时,不希望这些 null 出 现,比如我们希望所有的 null 在转 JSON 时都变成空字符串。
  在 Spring Boot 中,我们做一下配置即可,新建一个 Jackson 的配置类:
@Configuration
public class JacksonConfig {
    @Bean
    @Primary
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
            @Override
            public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                jsonGenerator.writeString("");
            }
        });
        return objectMapper;
    }
}
然后我们修改一下上面返回的 Map 接口,将几个值改成 null 进行测试,代码如下:
@RequestMapping("/map")
public Map<String, Object> getMap() {
    Map<String, Object> map = new HashMap<>(3);
    User user = new User(1, "贾志杰", null);
    map.put("作者信息", user);
    map.put("博客地址", "https://blog.csdn.net/beixishuo");
    map.put("公众号", "贝西奇谈");
    map.put("B站", null);
    return map;
}
重启项目,再次输入:localhost:8080/json/map,可以看到 Jackson 已经将所有 null 字 段转成空字符串了。
封装统一返回的数据结构
 在实际项目中,我们需要封装一个统一的 Json返回结构存储返回信息。
  1.定义统一Json结构
由于封装的 json 数据的类型不确定,所以在定义统一的 json 结构时,我们需要用到泛型。代码如下:
public class JsonResult<T> {
    private T data;
    private String code;
    private String msg;
    /**
     * 若没有数据返回,默认状态码为0,提示信息为:操作成功!
     */
    public JsonResult() {
        this.code = "0";
        this.msg = "操作成功!";
    }
  
  /**
     * 若没有数据返回,可以人为指定状态码和提示信息
     * @param code
     * @param msg
     */
    public JsonResult(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    /**
     * 有数据返回时,状态码为0,默认提示信息为:操作成功!
     * @param data
     */
    public JsonResult(T data) {
        this.data = data;
        this.code = "0";
        this.msg = "操作成功!";
    }
 
   /**
     * 有数据返回,状态码为0,人为指定提示信息
     * @param data
     * @param msg
     */
    public JsonResult(T data, String msg) {
        this.data = data;
        this.code = "0";
        this.msg = msg;
    }
    // 省略get和set方法
}
2.修改 Controller 中的返回值类型及测试
 根据以上的 JsonResult, 我们改写一下 Controller,代码如下
@RestController
@RequestMapping("/jsonresult")
public class JsonController {
    @RequestMapping("/user")
    public JsonResult<User> getUser() {
        User user = new User(10, "贝西", "11");
        return new JsonResult<>(user);
    }

    @RequestMapping("/list")
    public JsonResult<List<User>> getUserList() {
        List<User> userList = new ArrayList<>();
        User user1 = new User(1, "贝西", "123456");
        User user2 = new User(2, "贾志杰", "123456");
        userList.add(user1);
        userList.add(user2);
        return new JsonResult<>(userList, "获取用户列表成功");
    }
   
 @RequestMapping("/map")
    public JsonResult<Map<String, Object>> getMap() {
        Map<String, Object> map = new HashMap<>(3);
        User user = new User(1, "贾志杰", "123456");
        map.put("作者信息", user);
        map.put("博客地址", "https://blog.csdn.net/beixishuo");
        map.put("公众号", "贝西奇谈");
        map.put("B站", "贝西贝西");
        return new JsonResult<>(map);
    }
}
我们重新在浏览器中输入:localhost:8080/jsonresult/user,返回 JSON 如下:
{"code":"0","data":{"id":10,”name”:”贝西”,"password":"11"},"msg":"操作成功! "}
在浏览器中输入:localhost:8080/jsonresult/list,返回 JSON 如下:
{"code":"0","data":[{"id":1,”name”:”贝西”,"password":"123456"},{"id":2,”name”:”贾志杰”,”password”:”123456”}],"msg":"获取用户列表成功"} 

在浏览器中输入:localhost:8080/jsonresult/map,返回 JSON 如下:
{"code":"0","data":{"作者信息":{"id":1,”name”:”贾志杰”,"password":"123456"},”博客地址”: "https://blog.csdn.net/beixishuo","公众号":"贝西奇谈"},"msg":"操作成功!"}
在项目开发过程中,不可避免会遇到各种可预知的、不可预知的异常需要处理。Spring Boot 框架异常处理有五种处理方式,从范围来说包括有全局异常捕获处理方式和局部异常捕获处 理方式
自定义异常错误页
 在遇到异常时,Spring Boot 会自动跳到一个默认的异常页面,如请求上述 http://localhost:8080/exceptionMethod 路径时发生 500 错误,Spring Boot 会有一个默认的页面 展示给用户,如图所示

 

 

Spring Boot 默认的异常处理机制是程序中出现了异常 Spring Boot 就会请求 /error 的 url 。
以 Thymleaf 为例,Thymleaf 页面模板默认处于 classpath:/templates/ 下, 因此在该目录下创建 error.html 文件即可,代码如下:
//error.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>自定义 springboot 异常处理页面</title>
</head>
<body>
Springboot BasicExceptionController  错误页面
<br>
   <span th:text="${msg}"></span>
</body>
</html>
使用@ExceptionHandler注解处理局部异常
SpringMVC 提供了@ExceptionHandler 这个注解,在 SpringBoot 里面,我们同样可以 使用它来做异常捕获。直接在对应的 Controller 里面增加一个异常处理的方法,并使用 @ExceptionHandler 标识它即可,属于局部处理异常,代码如下:
@Controller
public class ExceptionController {
    private static final Logger log = LoggerFactory.getLogger(ExceptionController.class);
    @RequestMapping("/exceptionMethod")
    public String exceptionMethod(Model model) throws Exception {
        model.addAttribute("msg", "没有抛出异常");
        int num = 1/0;
        log.info(String.valueOf(num));
        return "index";
    }
    /**
     * 描述:捕获 ExceptionController 中的 ArithmeticException 异常
     * @param model 将Model对象注入到方法中
     * @param e 将产生异常对象注入到方法中
     * @return 指定错误页面
     */
使用 @ControllerAdvice 注解处理全局异常
实际开发中,需要对异常分门别类的进行处理,使用 @ControllerAdvice + @ExceptionHandler 注解能够处理全局异常,这种方式推荐使用,可以根据不同的异常对不 同的异常进行处理。
   使用方式:定义一个类,使用 @ControllerAdvice 注解该类,使用 @ExceptionHandler 注解方法,这里我定义了一个 GlobalException 类表示来处理全局异常,代码如下:
//GlobalException.java
@ControllerAdvice
public class GlobalException {
    private static final Logger log = LoggerFactory.getLogger(GlobalException.class);
    /**
     * 描述:捕获 ArithmeticException 异常
     * @param model 将Model对象注入到方法中
     * @param e 将产生异常对象注入到方法中
     * @return 指定错误页面
     */
  
  @ExceptionHandler(value = {ArithmeticException.class})
    public String arithmeticExceptionHandle(Model model, Exception e) {
        model.addAttribute("msg", "@ControllerAdvice + @ExceptionHandler :" + e.getMessage());
        log.info(e.getMessage());
        return "error";
    }
}

如果需要处理其他异常,如 NullPointerException 异常,则只需要在 GlobalException 类 中定义一个方法使用 @ExceptionHandler(value = {NullPointerException.class}) 注解该方法, 在该方法内部处理异常就可以了。
配置 SimpleMappingExceptionResolver 类处理异常
通过配置 SimpleMappingExceptionResolver 类处理异常也是全局范围的,通过将 SimpleMappingExceptionResolver 类注入到 Spring 容器,代码如下:
 @Configuration
public class GlobalException {
    @Bean
    public SimpleMappingExceptionResolver
    getSimpleMappingExceptionResolver(){
        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
        Properties mappings = new Properties();
        /*
         * 参数一:异常的类型,注意必须是异常类型的全名
         * 参数二:视图名称
         */
        mappings.put("java.lang.ArithmeticException", "error");
        //设置异常与视图映射信息的
        resolver.setExceptionMappings(mappings);
        return resolver;
    }
}
实现 HandlerExceptionResolver 接口处理异常
通过实现 HandlerExceptionResolver 接口处理异常,首先编写类实现 HandlerExceptionResolver 接口,代码如下:
@Configuration
public class HandlerExceptionResolverImpl implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
                                         Exception ex) {
        ModelAndView modelAndView = new ModelAndView();
       modelAndView.addObject("msg", "实现 HandlerExceptionResolver 接口处理异常");
        //判断不同异常类型,做不同视图跳转
        if(ex instanceof ArithmeticException){
            modelAndView.setViewName("error");
        }
        return modelAndView;
    }
}
一劳永逸
 当然了,异常很多,比如还有 RuntimeException,数据库还有一些查询或者操作异常等 等。由于 Exception 异常是父类,所有异常都会继承该异常,所以我们可以直接拦截 Exception 异常,一劳永逸,代码如下:
@ControllerAdvice
public class GlobalException{
    private static final Logger log= LoggerFactory.getLogger(GlobalException.class);
    /**
     * 系统异常 预期以外异常
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    //@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public JsonResult handleUnexpectedServer(Model model,Exception ex) {
        model.addAttribute("msg", "系统发生异常,请联系管理员");
        log.info(e.getMessage());
        return "error";
    }
}
实际项目中,可以把拦截 Exception 异常写在 GlobalException最下面,如果都没有找到,最后再拦截一下 Exception 异常,保证异常得到处理。
没有使用Spring Boot开发时,需要安装tomcat环境,项目打成war包后进行部署。 
而Spring Boot默认使用tomcat作为嵌入式的Servlet容器。
如何定制和修改 Servlet 容器的相关配置
     在内置的 Tomcat 中,不再有 web.xml 文件可以供我们修改,在 SpringBoot 中修改 Servlet 容器相关的配置有两种方式可供选择:
   (1) 在application.roperties或者application.yml/yaml配置文件中修改,代码如下:
spring.mvc.date-format=yyyy-MM-dd
spring.thymeleaf.cache=false 
spring.messages.basename=i18n.login
server.port=8081
server.context-path=/
server.tomcat.uri-encoding=UTF-8
编写一个 WebServerFactoryCustomizer:嵌入式的 Servlet 容器定制器,来修改 Servlet 容器   的配置。 
新建 MyMvcConfig 类,代码如下:
@Configuration
public class MyMvcConfig {
    @Bean
    public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
        return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
            @Override
            public void customize(ConfigurableWebServerFactory factory) {
                factory.setPort(8081);
            }
        };
    }
}
注册Servlet三大组件【Servlet、Filter、Listener】
Spring Boot对整合这些基本的Web组件(Servlet、Filter、Listener)也提供了很好的支持。
由于Spring Boot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件。所以用如下方式在Spring Boot项目中添加三个组件:
@WebServlet("/servlet")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("Hello MyServlet");
        System.out.println("name:"+req.getParameter("name"));
    }
}

@WebFilter("/")
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("MyFilter--init");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("myFilter--doFilter");
        filterChain.doFilter(servletRequest, servletResponse);
    }
    @Override
    public void destroy() {
        System.out.println("MyFilter--destroy");
    }
}
@WebListener
public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("web项目启动了。。。");
    }
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("web项目销毁了。。。");
    }
}
当然想要使用三大组件的注解,就必须先在SpringBoot主配置类(即标注了@SpringBootApplication注解的类)上添加@ServletComponentScan注解,以实现对Servlet、Filter及Listener的扫描,代码如下:
@ServletComponentScan
@SpringBootApplication
public class HelloworldApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloworldApplication.class, args);
    }
}
启动项目,在浏览器中输入http://localhost:8080/servlet?name=beixi ,在控制台查看日志信息
替换为其他嵌入式 Servlet 容器
 Spring Boot 默认使用的是 Tomcat,当然也是可以切换成其他的容器,而且切换的方式 也很简单,只需要引入其他容器的依赖,将当前容器的依赖排除即可。
jetty :在 pom.xml 文件中导入相关依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions><!-- 移除Tomcat -->
        <exclusion>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>
<!-- 引入其他的Servlet容器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
undertow(不支持 JSP,但是是一个高性能的非阻塞的 Servlet 容器,并发性能好)。 引入 undertow 的方式同 jetty 一样,依赖如下:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>
<!-- 引入其他的Servlet容器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
Spring Boot延续了Spring MVC中提供的AOP风格拦截器,拥有精细的拦截处理能力,在Spring Boot中拦截器的使用更加方便。这里只是用登陆的例子来展现拦截器的基本使用,拦截器用途很广,比如可以对URL路径进行拦截,可以用于权限验证、解决乱码、操作日志记录、性能监控、异常处理等。