SpringBoot
大约 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));
}
}