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

2022-05-11 14:13:38 浏览数 (1)

一、分页(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、添加依赖

代码语言:javascript复制
        <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如下:

代码语言:javascript复制
<?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>

1.1.2、添加配置

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

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

1.1.3、数据访问接口

StudentDao.java:

代码语言:javascript复制
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:

代码语言:javascript复制
<?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:

代码语言:javascript复制
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:

代码语言:javascript复制
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 

代码语言:javascript复制
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

代码语言:javascript复制
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)浏览器会禁止这种跨域请求。

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

代码语言:javascript复制
@Configuration
public class GlobalCorsConfig {

    @Bean
    public CorsFilter corsFilter() {
        //1. 添加 CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
        //放行哪些原始域
        config.addAllowedOrigin("*");
        //是否发送 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(全局跨域)

代码语言:javascript复制
@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:,表示该类的所有方法允许跨域。

代码语言:javascript复制
@RestController
@CrossOrigin(origins = "*")
public class HelloController {

    @RequestMapping("/hello")
    public String hello() {
        return "hello world";
    }
}

在方法上使用注解 @CrossOrigin:

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

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

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

代码语言:javascript复制
@RequestMapping("/index")
public String index(HttpServletResponse response) {

    response.addHeader("Access-Allow-Control-Origin","*");
    return "index";
}

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

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

代码语言:javascript复制
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如下:

代码语言:javascript复制
<!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="getfiles($event)"/>
        <button type="button" @click="upfiles($event)">提交</button>
    </div>
    <script src="vue/vue.js"></script>
    <script src="axios/axios.min.js"></script>
    <script>
        var app=new Vue({
            el:"#app",
            data:{
                file:{}
            },
            methods:{
                getfiles(event){
                    this.file=event.target.files[0];
                    console.log(this.file);
                },
                upfiles:function(event){
                    event.preventDefault;
                    var formdata=new FormData();
                    formdata.append("file",this.file);

                    axios.post("http://localhost:8080/upfile",formdata,{
                        Headers:{
                            "Content-Type":"multipart/form-data"
                        }
                    }).then(function(response){
                        console.log(response);
                        console.log("ok");
                    }).catch(function(error){
                        console.log(error);
                    });
                }
            }
        });
    </script>
</body>
</html>

application.yaml文件

代码语言:javascript复制
#文件的限制大小
  servlet:
    multipart:
      max-file-size: 100MB  #文件最大值
      max-request-size: 100MB #请求最大值
prop:
  up-folder: F:NFSpring bootdemosCORSDemo1uploads  #上传目标位置

后端控制器

代码语言:javascript复制
package com.zhangguo.mybatisdemo3.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

@RestController
public class UpfileController {
    @Value("${prop.up-folder}")
    String path;
    
    @PostMapping("/upfile")
    public String upfile(@RequestPart("file") MultipartFile file) throws IOException {
        String oldname=file.getOriginalFilename();
        //新的文件名=UUID 原文件后缀名
        String newname= UUID.randomUUID().toString() oldname.substring(oldname.lastIndexOf("."));
        file.transferTo(new File(path file.getOriginalFilename()));
        return newname;
    }
}

运行效果

四、启动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仓库中搜索)

代码语言:javascript复制
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

5.3、使用注解

代码语言:javascript复制
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、运行测试

测试类:

代码语言:javascript复制
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);
    }
}

测试结果:

代码语言:javascript复制
Department(departmentId=1, departmentName=研发部)

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

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

0 人点赞