• Spring Boot 整合 Web


    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路径进行拦截,可以用于权限验证、解决乱码、操作日志记录、性能监控、异常处理等。
  • 相关阅读:
    325日报
    323日报
    Http请求种类
    http1.1和1.0的区别
    Java EE常用名词
    USEA、HASA、ISA
    基本类型及其封装类型的 == 和 equals()
    AOP中的连接点(Joinpoint)、切点(Pointcut)、增强(Advice)、引介(Introduction)、织入(Weaving)、切面(Aspect)
    B树和B+树
    DNS的寻址过程
  • 原文地址:https://www.cnblogs.com/tszr/p/15418763.html
Copyright © 2020-2023  润新知