跳至主要內容

SpringBoot

LiCheng大约 7 分钟

SpringBoot

介绍🎈

  • 2019/7
  • 一些常用SpringBoot用法

必读指南

注意

  • 标题后面带 ! 的内容将可能不在此更新(但不代表不能用),
  • 改从 JavaOrm 插件提供的模板进行更新
  • JavaOrm

规范😎

  • spring-boot 项目的所有文件必须utf-8

打包原生脚本执行🪲

  • maven配置

<build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <executable>true</executable>
            </configuration>
        </plugin>
    </plugins>
</build>
  • 如何运行
chmod +x ./YourName.jar  && ./YourName.jar

启动和部署🍉

# 前台指定端口启动
java -jar xxx.jar --server.port=80  
# 后台部署
nohup java -jar $jar_name > log.log 2>&1 &
  • 部署脚本
#!/bin/bash
# 定义jar包名称
jar_name=spring-boot-web-template.jar
# 获取进程id
pid=$(ps -ef | grep $jar_name | grep -v grep | awk '{print $2}')
# 存在则kill
if [ -n "$pid" ];then kill -9 $pid;fi
# 启动jar包
nohup java -jar $jar_name > log.log 2>&1 &
  • 配置文件
//优先读取同级目录的 application.yml 文件,后读取jar包内的 application.yml 文件,只读取一个
--|
  - java -jar xxx.jar
  - application.yml

Cors跨域配置🍎 !

package com.zkyt.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;

import java.time.Duration;

/**
 * 跨域配置
 * @author lc
 * @since  2022/7/11
 */
@Configuration  
public class CorsConfig {  
    private CorsConfiguration buildConfig() {  
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 1允许任何域名使用
        corsConfiguration.addAllowedOrigin("*");
        // 2允许任何头
        corsConfiguration.addAllowedHeader("*");
        // 3允许ajax异步请求带cookie信息
        corsConfiguration.setAllowCredentials(true);
        // 4允许任何方法(post、get等)
        corsConfiguration.addAllowedMethod("*");
        // 5设置OPTIONS预检请求的过期时间
        corsConfiguration.setMaxAge(Duration.ofDays(1));
        return corsConfiguration;  
    }  
  
    @Bean  
    public CorsFilter corsFilter() {  
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();  
        source.registerCorsConfiguration("/**", buildConfig());  
        return new CorsFilter(source);  
    }  
}

yml配置💞

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/java_web?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai #保证插入的时间不会少8小时,取出来少8小时
    username: root
    password: root

  jackson: #屏蔽null字段返回
    default-property-inclusion: non_null
    date-format: yyyy-MM-dd HH:mm:ss #时间格式
    time-zone: GMT+8 #时区

测试搭建👻


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>
package com.example.demo; 

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class DemoApplicationTests {

	@Test
	void contextLoads() {
		System.out.println("Hello World hi");
	}
}

Controller传参🐔

  • map
http://localhost:8080/test1?aaa=111&aad=agasg       //直接k,v格式即可

@RequestMapping("/test1")
@ResponseBody
public String test1(@RequestParam HashMap<String,String> map){  //必加@RequestParam注解
//service接受的map类型必须一致否则无法强转,例:HashMap<String,String>
    return map.toString();
}
  • list
http://localhost:8080/test1?list=fas,fasfa,asdfas    

@Controller
public class TestController {
    @RequestMapping("/test1")
    public String test1(@RequestParam List<String> list){
        System.out.println(list);
        return list.toString();
    }
}

全局异常处理🐟

package com.dmeo.config.exception;

import com.dmeo.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 异常处理
 *
 * @author lc
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 处理 ServiceException 异常
     *
     * @since 2021/6/23
     */
    @ExceptionHandler(ServiceException.class)
    public JsonResult<String> doHandleServiceException(ServiceException e) {
        return JsonResult.fail(e.getMessage());
    }

    /**
     * 处理 Exception 异常
     *
     * @since 2021/6/23
     */
    @ExceptionHandler(Exception.class)
    public JsonResult<String> doHandleServiceException(Exception e) {
        e.printStackTrace();
        return JsonResult.fail("系统繁忙");
    }
}
  • 异常类
package com.template.exception;

/**
 * 自定义异常
 * @author lc
 * @since 2023/1/14
 */
public class ServiceException extends RuntimeException{
    public ServiceException(String message) {
        super(message);
    }
}

文件上传下载🍇

@PostMapping("upload")
public JsonResult<String> upload(MultipartFile file) {
    String fileName =  file.getOriginalFilename();
    try {
        if (fileName == null){
            return JsonResult.fail("文件名为空");
        }
        File f = new File(fileName);
        file.transferTo(f);
    } catch (IOException e) {
        e.printStackTrace();
        throw new RuntimeException("上传失败");
    }
    return JsonResult.okMsg("上传成功");
}

@RequestMapping("file")
public void file(HttpServletResponse response) throws IOException {
    File file = new File("文件");
    FileInputStream fileInputStream = new FileInputStream(file);
    byte[] by = new byte[1024 * 8];
    int i;
    response.setCharacterEncoding("utf-8");   //设置处理编码
    //设置接收的长度,单位字节
    response.setHeader("Content-Length", ""+file.length());
    ServletOutputStream outputStream = response.getOutputStream();
    //URLEncoder.encode("CentOS7_min.rar","utf-8")把指定的字符串 转换为指定编码的String并返回
    response.setHeader("Content-Disposition","attachment; filename="+ URLEncoder.encode(file.getName(),"utf-8"));
    while ((i = fileInputStream.read(by)) != -1) {
    outputStream.write(by,0,i);
    }
    outputStream.flush();
    outputStream.close();
    fileInputStream.close();
    System.out.println("程序结束!");
}

提示

需要配置上传文件的大小

spring:
  servlet:
    multipart:
      max-file-size: 100MB #单个文件最大
      max-request-size: 500MB #总和大小

SpringBootUtil🎁 !

  • 基于SpringBoot+第三方封装的依赖

HttpServletUtil🐟 !


package com.aiwan.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.Objects;


/**
 * 必须在web环境使用,否则会出现空指针异常
 * @author lc
 * @since 2021/8/30
 */
public final class HttpServletUtil {
    private HttpServletUtil() {
    }

    /**
     * @return 当前请求的路径
     */
    public static String getPath() {
        return getHttpServletRequest().getRequestURI();
    }


    /**
     * 获取HttpServletRequest
     */
    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    }

    /**
     * 获取请求参数
     */
    public static Map<String, String> getParamMap() {
        return ServletUtil.getParamMap(getHttpServletRequest());
    }

    /**
     * web环境使用
     */
    public static String getHeader(String name) {
        return getHttpServletRequest().getHeader(name);
    }


    /**
     * 获取请求类型GET,POST
     */
    public static String getMethod() {
        return getHttpServletRequest().getMethod();
    }

    /**
     * 路径+参数等于唯一key
     */
    public static String getKey(String str) {
        return getPath() + str;
    }

    /**
     * web环境使用
     */
    public static void isParamMap(String... str) {
        Map<String, String> map = getParamMap();
        for (String s : str) {
            if (StrUtil.isBlank(map.get(s))) {
                throw new RuntimeException("参数错误,请检查!");
            }
        }
    }
}

返回对象封装🐷 !

package com.zkyt.util;

import lombok.Data;

/**
 * 泛型支持
 * @author lc
 * @since  2020/11/30
 */
@Data
public class R<T> {
    private Object msg;
    private int code ;
    private T data;
    public static <T> R<T> okMsg(T msg){
        R<T> jsonResult = new R<>();
        jsonResult.setMsg(msg);
        jsonResult.setCode(0);
        return jsonResult;
    }
    public static <T> R<T> okData(T data){
        R<T> jsonResult = new R<>();
        jsonResult.setData(data);
        jsonResult.setCode(0);
        return jsonResult;
    }
    public static <T> R<T> fail(T msg){
        R<T> jsonResult = new R<>();
        jsonResult.setMsg(msg);
        jsonResult.setCode(-1);
        return jsonResult;
    }
}

TokenUtil⭐

package com.zkyt.util;

import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.zkyt.exception.ServiceException;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT的封装用于更易用
 * @author lc
 * @since 7/11/22
 */
public class TokenUtil  {
    /** 密钥串 **/
    public static String SECRET_KEY = "YOUR_SECRET_KEY";
    private static final  String USER_ID_KEY = "USER_ID";
    private static final  String EXPIRE_TIME_KEY = "EXPIRE_TIME";
    /**
     * 获取Token
     * @since 7/11/22
     */
    public static String getToken(Serializable userId){
        Map<String, Object> map = new HashMap<>();
        map.put(USER_ID_KEY, userId);
        map.put(EXPIRE_TIME_KEY, System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 3);
        return JWTUtil.createToken(map, SECRET_KEY.getBytes());
    }

    /**
     * 获取用户id
     * @since 7/11/22
     */
    public static String getUserId(String token){
        JWT jwt = JWTUtil.parseToken(token);
        if (!JWTUtil.verify(token, SECRET_KEY.getBytes())) {
            throw new ServiceException("无效Token!");
        }
        Object payload = jwt.getPayload(EXPIRE_TIME_KEY);
        if (payload == null || System.currentTimeMillis() > (long)payload) {
            throw new ServiceException("登入失效!");
        }
        return jwt.getPayload(USER_ID_KEY).toString();
    }

    public static void main(String[] args) {
        //测试
        String token = getToken(123456);
        System.out.println(token);
        String userId = getUserId(token);
        System.out.println(userId);
    }
}

FileUtil🐷 !

package com.zkyt.util;

import lombok.Data;

import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;

/**
 *
 * linux or windows 特殊目录下使用会出现权限问题,不会出现错误,但无法获取信息!
 * @author lc
 * @since 7/11/22
 */
public final class FileUtil {
    private FileUtil(){}
    /**
     * 获取目录所有文件的字节数-不包含子目录
     * @since 7/11/22
     */
    public static long getFolderByteSize(File file){
        long byteSize = 0;
        File[] files = file.listFiles();
        if (!file.isDirectory() || files == null) {
            return byteSize;
        }
        for (File f : files) {
            if (f.isFile()) {
                byteSize += f.length();
            }
        }
        return byteSize;
    }

    @Data
    static class FileInfo implements Serializable {

        private String fileName;
        /**
         *  1 是目录,0 默认文件
         */
        private int isDirectory;
    }

    /**
     * 获取目录所有文件名-不包含子目录,目录也会显示
     * @since 7/11/22
     */
    public static ArrayList<FileInfo> getAllFileName(String path){
        return getAllFileName(new File(path));
    }

    /**
     * 获取目录所有文件名-不包含子目录,目录也会显示
     * @since 7/11/22
     */
    public static ArrayList<FileInfo> getAllFileName(File file){
        ArrayList<FileInfo> list = new ArrayList<>();
        File[] files = file.listFiles();
        if ( files == null) {
            return list;
        }
        for (File f : files) {
            FileInfo info = new FileInfo();
            info.setFileName(f.getName());
            if (f.isDirectory()) {
                info.setIsDirectory(1);
            }
            list.add(info);
        }
        return list;
    }

    /**
     *
     * 获取文件夹下的所有文件-包含子目录
     * @since 7/11/22
     */
    public static ArrayList<Object> getFolderAllFolderFileName(File file){
        ArrayList<Object> list = new ArrayList<>();
        File[] files = file.listFiles();
        if (!file.isDirectory() || files == null) {
            return list;
        }
        for (File f : files) {
            if (f.isFile()) {
                list.add(f.getName());
            }else {
                HashMap<String, Object> map = new HashMap<>();
                map.put(f.getName(),getFolderAllFolderFileName(f));
                list.add(map);
            }
        }
        return list;
    }


    public static void main(String[] args) {
        String path = "/home/lco/Downloads";
        File file = new File(path);
        System.out.println(getAllFileName(path));
        System.out.println(getFolderByteSize(file));
        File f = new File("/home/lco/Downloads");
        System.out.println(getFolderAllFolderFileName(f));
    }
}

拦截器⭐

  • 配置代码

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author lc
 */
@Configuration
public class AuthorizeConfig implements WebMvcConfigurer  {
    @Autowired
    private InterceptorConfig interceptorConfig;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //拦截所有路径
        registry.addInterceptor(interceptorConfig).addPathPatterns("/**");
    }
}
  • 配置类

import com.zkyt.annotations.PassToken;
import com.zkyt.entity.User;
import com.zkyt.exception.ServiceException;
import com.zkyt.service.UserService;
import com.zkyt.util.HttpServletUtil;
import com.zkyt.util.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author lc
 */
@Configuration
@Slf4j
public class InterceptorConfig implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //排除静态资源
        if (!(handler instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;

        PassToken token = handlerMethod.getMethodAnnotation(PassToken.class);
        if (token != null) {
            return true;
        }
        return true;
    }
}
  • 注解配置
import java.lang.annotation.*;

/**
 * 排除需要登入的接口
 * @author lc
 * @since 8/1/22
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PassToken {
}

参数校验😃

日期

  • 2022/7/31
  • 更优雅的处理参数校验
注解作用
@NotEmpty判断null和空字符串
@Size设置最大和最小限制

依赖🍒


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

DTO❤️

package com.zkyt.dto;

import lombok.Data;

import javax.validation.constraints.NotEmpty;


/**
 * @author lc
 * @since 2022/7/29
 */
@Data
public class UserLoginDto {
    /**
     * 用户名
     */
    @NotEmpty(message = "用户名为空")
    private String username;
    /**
     * 用户密码
     */
    @NotEmpty(message = "用户密码为空")
    private String password;
}

Controller⭐


package com.zkyt.controller;

import com.zkyt.dto.UserLoginDto;
import com.zkyt.service.UserService;
import com.zkyt.util.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

/**
 * @author lc
 * @since 2022/7/19
 */

@RestController
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("login")
    public Object login(@Valid  UserLoginDto userLoginDto ){
        return R.okData(userService.login(userLoginDto));
    }

}