• SpringBoot学习笔记(六)——分页、跨域、上传、定制banner、Lombok


    一、分页(pagehelper)

     pagehelper 是一个强大实用的 MyBatis 分页插件,可以帮助我们快速的实现MyBatis分页功能,而且pagehelper有个优点是,分页和Mapper.xml完全解耦,并以插件的形式实现,对Mybatis执行的流程进行了强化,这有效的避免了我们需要直接写分页SQL语句来实现分页功能。

    github项目地址:https://github.com/pagehelper/Mybatis-PageHelper

    中文帮助:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/README_zh.md

    1.1、快速起步

    1.1.1、添加依赖

            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper-spring-boot-starter</artifactId>
                <version>1.3.0</version>
            </dependency>

    这里需要注意也MyBatis的兼容问题,如果springboot pagehelper插件启动报错 [com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration]则需要更换版本,我使用2.5.13的Spring Boot与1.3.0的pagehelper是兼容的,示例项目完整的pom如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.5.13</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.zhangguo</groupId>
        <artifactId>mybatisdemo3</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>mybatisdemo3</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.2.2</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <optional>true</optional>
                <scope>true</scope>
            </dependency>
    
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper-spring-boot-starter</artifactId>
                <version>1.3.0</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <fork>true</fork>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    View Code

    1.1.2、添加配置

    在application.yaml文件中添加如下配置信息:

    # pagehelper
    pagehelper:
      helperDialect: mysql  #数据库类型
      reasonable: true #查询合理化 当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页
      supportMethodsArguments: true  #支持方法参数 支持通过 Mapper 接口参数来传递分页参数
      params: count=countSql  #参数

    1.1.3、数据访问接口

    StudentDao.java:

    package com.zhangguo.mybatisdemo3.dao;
    
    import com.zhangguo.mybatisdemo3.entity.Student;
    import java.util.List;
    
    public interface StudentDao {
        //查询学生并分页
        public List<Student> selectPager();
    }

    1.1.4、接口映射

    resource/mapper/StudentDao.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.zhangguo.mybatisdemo3.dao.StudentDao">
        <select id="selectPager" resultType="Student">
            SELECT
                student.id,
                student.`name`,
                student.sex
            FROM
                student
        </select>
    </mapper>

    1.1.4、实现分页

    StudentService.java:

    package com.zhangguo.mybatisdemo3.service;
    
    import com.github.pagehelper.PageHelper;
    import com.github.pagehelper.PageInfo;
    import com.zhangguo.mybatisdemo3.dao.StudentDao;
    import com.zhangguo.mybatisdemo3.entity.Student;
    import com.zhangguo.mybatisdemo3.util.PageRequest;
    import com.zhangguo.mybatisdemo3.util.PageResult;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    @Service
    public class StudentService {
        @Autowired
        StudentDao studentDao;
    
        public PageInfo<Student> selectPager(int pageNum,int pageSize){
            //开始分页,指定页码与每页记录数
            PageHelper.startPage(pageNum,pageSize);
            //执行查询,请求会被分页插件拦截
            List<Student> students = studentDao.selectPager();
            //返回分页对象与数据
            return new PageInfo<Student>(students);
        }
    }

    1.1.5、调用分页方法

     PageController.java:

    package com.zhangguo.mybatisdemo3.controller;
    
    import com.github.pagehelper.PageInfo;
    import com.zhangguo.mybatisdemo3.entity.Student;
    import com.zhangguo.mybatisdemo3.service.StudentService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class PageController {
    
        @Autowired
        StudentService studentService;
    
        @GetMapping
        public PageInfo<Student> hello(@RequestParam(value = "pageNum",required = false,defaultValue = "1") int pageNum, @RequestParam(value = "pageSize",required = false,defaultValue = "3")int pageSize){
            return studentService.selectPager(pageNum,pageSize);
        }
    }

    pageNum参数用于指定页号,默认值为1,pageSize用于指定每页记录数,默认值为3。

    运行结果:

    默认值情况

    带参数情况

    pageNum:当前页的页码
    pageSize:每页显示的条数
    size:当前页显示的真实条数
    total:总记录数
    pages:总页数
    prePage:上一页的页码
    nextPage:下一页的页码
    isFirstPage/isLastPage:是否为第一页/最后一页
    hasPreviousPage/hasNextPage:是否存在上一页/下一页
    navigatePages:导航分页的页码数
    navigatepageNums:导航分页的页码,[1,2,3,4,5]

    1.2、封装请求与结果

    默认情况下请求参数并没有使用对象封装,返回结果包含冗余信息且需要与具体的业务关联。

    1.2.1、请求参数封装

    PageRequest.java 

    package com.zhangguo.mybatisdemo3.util;
    /**
     * 分页请求
     */
    public class PageRequest {
        /**
         * 当前页码
         */
        private int pageNum;
        /**
         * 每页数量
         */
        private int pageSize;
        
        public int getPageNum() {
            return pageNum;
        }
        public void setPageNum(int pageNum) {
            this.pageNum = pageNum;
        }
        public int getPageSize() {
            return pageSize;
        }
        public void setPageSize(int pageSize) {
            this.pageSize = pageSize;
        }
    }

    1.2.2、响应结果封装

     PageResult.java

    package com.zhangguo.mybatisdemo3.util;
    import com.github.pagehelper.PageInfo;
    
    import java.util.List;
    /**
     * 分页返回结果
     */
    public class PageResult {
        /**
         * 当前页码
         */
        private int pageNum;
        /**
         * 每页数量
         */
        private int pageSize;
        /**
         * 记录总数
         */
        private long totalSize;
        /**
         * 页码总数
         */
        private int totalPages;
        /**
         * 数据模型
         */
        private List<?> content;
    
        public int getPageNum() {
            return pageNum;
        }
        public void setPageNum(int pageNum) {
            this.pageNum = pageNum;
        }
        public int getPageSize() {
            return pageSize;
        }
        public void setPageSize(int pageSize) {
            this.pageSize = pageSize;
        }
        public long getTotalSize() {
            return totalSize;
        }
        public void setTotalSize(long totalSize) {
            this.totalSize = totalSize;
        }
        public int getTotalPages() {
            return totalPages;
        }
        public void setTotalPages(int totalPages) {
            this.totalPages = totalPages;
        }
        public List<?> getContent() {
            return content;
        }
        public void setContent(List<?> content) {
            this.content = content;
        }
    
        /**
         * 将分页信息封装到统一的接口
         * @return
         */
        public static PageResult getPageResult(PageInfo<?> pageInfo) {
            PageResult pageResult = new PageResult();
            pageResult.setPageNum(pageInfo.getPageNum());
            pageResult.setPageSize(pageInfo.getPageSize());
            pageResult.setTotalSize(pageInfo.getTotal());
            pageResult.setTotalPages(pageInfo.getPages());
            pageResult.setContent(pageInfo.getList());
            return pageResult;
        }
    }

    二、跨域

    2.1、跨域概要

    跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。

    例如:a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。注意:跨域限制访问,其实是浏览器的限制。理解这一点很重要!!!

    同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域;

     2.2、返回新的CorsFilter(全局跨域)

    CORS,全称Cross-Origin Resource Sharing  ,是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(the same-origin security policy)浏览器会禁止这种跨域请求。

    示例:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        
        <script src="axios/axios.min.js"></script>
        <script>
            axios.get("http://localhost:8080/students").then(response=>{
                console.log(response.data);
            }).catch(error=>{
                console.log(error);
            });
        </script>
    </body>
    </html>

    前端访问提示

    在任意配置类,返回一个 新的 CorsFIlter Bean ,并添加映射路径和具体的CORS配置路径。

    package com.zhangguo.mybatis4.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.filter.CorsFilter;
    
    @Configuration
    public class GlobalCorsConfig {
    
        @Bean
        public CorsFilter corsFilter() {
            //1. 添加 CORS配置信息
            CorsConfiguration config = new CorsConfiguration();
            //放行哪些原始域
            config.addAllowedOrigin("http://127.0.0.1:5500/");
            //是否发送 Cookie
            config.setAllowCredentials(true);
            //放行哪些请求方式
            config.addAllowedMethod("*");
            //放行哪些原始请求头部信息
            config.addAllowedHeader("*");
            //暴露哪些头部信息
            config.addExposedHeader("*");
            //2. 添加映射路径
            UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
            corsConfigurationSource.registerCorsConfiguration("/**",config);
            //3. 返回新的CorsFilter
            return new CorsFilter(corsConfigurationSource);
        }
    }

    2.3、重写WebMvcConfigurer(全局跨域)

    @Configuration
    public class CorsConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    //是否发送Cookie
                    .allowCredentials(true)
                    //放行哪些原始域
                    .allowedOrigins("*")
                    .allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
                    .allowedHeaders("*")
                    .exposedHeaders("*");
        }
    }

    2.4、使用注解 (局部跨域)

    在控制器(类上)上使用注解 @CrossOrigin:,表示该类的所有方法允许跨域。

    @RestController
    @CrossOrigin(origins = "*")
    public class HelloController {
    
        @RequestMapping("/hello")
        public String hello() {
            return "hello world";
        }
    }

    在方法上使用注解 @CrossOrigin:

      @RequestMapping("/hello")
      @CrossOrigin(origins = "*")
       //@CrossOrigin(value = "http://localhost:8081") //指定具体ip允许跨域
      public String hello() {
            return "hello world";
      }

    2.5、手动设置响应头(局部跨域)

    使用 HttpServletResponse 对象添加响应头(Access-Control-Allow-Origin)来授权原始域,这里 Origin的值也可以设置为 “*”,表示全部放行。

    @RequestMapping("/index")
    public String index(HttpServletResponse response) {
    
        response.addHeader("Access-Allow-Control-Origin","*");
        return "index";
    }

    2.6、使用自定义filter实现跨域

    首先编写一个过滤器,可以起名字为MyCorsFilter.java

    package cn.wideth.aop;
    
    import java.io.IOException;
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyCorsFilter implements Filter {
    
      public void doFilter(ServletRequest req, ServletResponse res, 
      FilterChain chain) throws IOException, ServletException {
      
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
        chain.doFilter(req, res);
        
      }
      
      public void init(FilterConfig filterConfig) {}
      public void destroy() {}
    }

    三、上传

    前端使用Vue+Axios实现AJAX上传文件,upfile.html如下:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>上传文件示例</title>
    </head>
    
    <body>
        <div id="app">
            <h2>上传文件示例</h2>
            <input type="file" @change="selectFile($event)"/>
            <input type="button" value="上传"  @click="upfile($event)" />
            <p>
                <img :src="'files/'+filename" v-show="isUpload"/>
            </p>
        </div>
        <script src="vue/vue.js"></script>
        <script src="axios/axios.min.js"></script>
        <script>
            var app = new Vue({
                el: "#app",
                data:{
                    file:null,
                    filename:'',
                    isUpload:false
                },
                methods:{
                    selectFile(event){
                        //从input的文件集合中获取第1个文件对象,就是要上传的文件对象
                        this.file=event.target.files[0];
                        console.log(this.file);
                    },
                    upfile(event){
                        event.preventDefault(); //阻止默认事件
                        //创建一个表单数据对象
                        var formdata=new FormData();
                        //向表单数据对象中添加表单数据,指定名称与数据内容
                        formdata.append("file",app.file);
                        //使用axios上传数据
                        axios.post("http://localhost:8080/upfile",formdata,{
                            Headers:{
                                "Content-Type":"multipart/form-data"
                            }
                        }).then(response=>{
                            console.log(response.data);
                            //上传成功了
                            app.isUpload=true;
                            //指定上传成功后的文件名
                            app.filename=response.data;
                        }).catch(error=>{
                            console.log(error);
                        });
                        return false;
                    }
                }
            });
        </script>
    </body>
    
    </html>

    application.yaml文件

    #文件的限制大小
      servlet:
        multipart:
          max-file-size: 100MB  #文件最大值
          max-request-size: 100MB #请求最大值
    prop:
      up-folder: F:\NF\Spring boot\demos\CORSDemo1\uploads\  #上传目标位置

    后端控制器

    package com.zhangguo.mybatis4.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestPart;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.UUID;
    
    @RestController
    public class UpController {
        @Value("${prop.up-folder}")
        String upFolder;
    
        @PostMapping("/upfile")
        public String upfile(@RequestPart MultipartFile file) throws IOException {
            //获取旧文件名称
            String oldname=file.getOriginalFilename();
            //生成新文件名称
            String newname= UUID.randomUUID().toString()+oldname.substring(oldname.lastIndexOf("."));
            //文件保存路径
            String path=upFolder+newname;
            //保存文件
            file.transferTo(new File(path));
            return newname;
        }
    }

    运行效果

    在vscode中因为安装了Live Server会实时的刷新页面,上传后会自动刷新,可以忽视*.png与*.jpg文件上传到前端目录中,设置如下:

    点击设置

    点击扩展设置,找到如下项

     在中间添加两项

    ,
     "**/*.jpg",
    "**/*.png"

    保存配置即可。

    四、启动Banner定制

    我们在应用启动的时候,可以看到控制台显示了Spring的Banner信息,我们可以通过定制这个功能,来放置我们自己的应用信息。

     

     如果要定制自己的Banner, 只需要在 resources 下放置一个 banner.txt 文件,输入自己的banner字符即可。

     重新启动项目

    Banner字符可以通过类似以下网站生成:

    http://patorjk.com/software/taag

    http://www.network-science.de/ascii/

    五、lombok

    5.1、lombok概要

    Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。

    在项目中使用Lombok可以减少很多重复代码的书写。比如说getter/setter/toString等方法的编写。

    “Boilerplate”是一个术语,用于描述在应用程序的许多部分中很少改动就重复的代码。对Java语言最常见的批评就是在大多数项目中都可以找到这种类型的代码,由于语言本身的局限性而更加严重。龙目岛计划(Project Lombok)旨在通过用简单的注释集代替众多的代码。

    Lombok也存在一定风险,在一些开发工具商店中没有Project Lombok支持选择。 IDE和JDK升级存在破裂的风险,并且围绕项目的目标和实施存在争议。

    常用注解:

    1. @Setter :注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。
    2. @Getter :使用方法同上,区别在于生成的是getter方法。
    3. @ToString :注解在类,添加toString方法。
    4. @EqualsAndHashCode: 注解在类,生成hashCode和equals方法。
    5. @NoArgsConstructor: 注解在类,生成无参的构造方法。
    6. @RequiredArgsConstructor: 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。
    7. @AllArgsConstructor: 注解在类,生成包含类中所有字段的构造方法。
    8. @Data: 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
    9. @Slf4j: 注解在类,生成log变量,严格意义来说是常量。

    5.2、引入依赖

    在pom文件中添加如下部分。(不清楚版本可以在Maven仓库中搜索)

            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>

    5.3、使用注解

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data  //注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
    @AllArgsConstructor  // 注解在类,生成包含类中所有字段的构造方法。
    @NoArgsConstructor  //注解在类,生成无参的构造方法。
    public class Department {
      private Integer departmentId;
      private String departmentName;
    }

     5.4、运行测试

    测试类:

    package com.zhangguo.mybatisdemo3;
    
    import com.zhangguo.mybatisdemo3.entity.Department;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @SpringBootTest
    public class DepartmentTest {
        @Test
        public void lombokTest(){
            Department department=new Department(1,"研发部");
            System.out.println(department);
        }
    }

    测试结果:

    Department(departmentId=1, departmentName=研发部)

    可以看到有带参构造方法,toString方法也被重写过了。

    需要注意的是新版本的IDEA不再需要安装插件,已经默认整合了。

    六、视频

    https://www.bilibili.com/video/BV1fi4y1S79P?share_source=copy_web

    https://space.bilibili.com/87194917/video

    作业解答:https://www.bilibili.com/video/BV1Hs411F71x?share_source=copy_web

  • 相关阅读:
    [NOIP2002 提高组] 均分纸牌
    洛谷 P1303 A*B Problem
    OpenJudge 1.6.5 年龄与疾病
    hdu 3340 线段树思路活用
    poj 2464 线段树统计区间..两棵树
    hdu 4419 矩形面积覆盖颜色
    经典动态规划 dp Rqnoj 57
    最基础二维线段树 hdu 1823 (简单)
    hdu 3564 线段树+dp
    spoj 1557 线段树 区间最大连续和 (不重复数)
  • 原文地址:https://www.cnblogs.com/best/p/16229290.html
Copyright © 2020-2023  润新知