1. 程式人生 > 其它 >SpringBoot學習筆記(六)——分頁、跨域、上傳、定製banner、Lombok

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)瀏覽器會禁止這種跨域請求。

在任意配置類,返回一個 新的 CorsFIlter Bean ,並新增對映路徑和具體的CORS配置路徑。

@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(全域性跨域)

@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="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檔案

#檔案的限制大小
  servlet:
    multipart:
      max-file-size: 100MB  #檔案最大值
      max-request-size: 100MB #請求最大值
prop:
  up-folder: F:\NF\Spring boot\demos\CORSDemo1\uploads\  #上傳目標位置

後端控制器

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倉庫中搜索)

        <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不再需要安裝外掛,已經預設整合了。