项目框架搭建
这一块做 一般项目搭建后的准备工作,主要包括 新建项目、配置文件、公共返回类、异常处理、日志;
这些应该是 所有项目通用的,把它放到这一块了。
1、新建项目
选择组件,一般选择:Spring boot devtools、Mysql Driver、Mybatis framework、Thymeleaf、Spring web、lombok。
先配置文件,然后再测试能否启动
(1)配置文件 application.yml
要做事情:
- 创建一个管理公共配置的 application.yml
- 创建开发环境 application-dev.yml、 生产环境application-pro.yml,【测试环境application-test.yml】的配置文件;
注意:这两个文件 分别连接对应环境的 数据库、redis、rabbitmq等服务,设置不同端口,日志输出级别等等,对应了其所在的环境
2、配置文件
1、公共配置 application.yml
spring:
#如果有thymeleaf 模板引擎
thymeleaf:
mode: HTML
cache: false #关闭缓存
# 选用哪个配置文件,对应与 application-dev.yml和application-pro.yml 的文件
profiles:
active: dev
# 如果用 mybatis-plus,就换成下面
#mybatis-plus:
mybatis:
# 对应的 实体类的包
type-aliases-package: com.tab.entity
#对应的 mapper文件的包,在resources文件夹下面
mapper-locations: classpath:mapper/*.xml
configuration:
#下划线 - 驼峰映射关系
map-underscore-to-camel-case: true
下面举例 开发环境 和 生产环境,实际有所不同
2、开发环境 application-dev.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
# xxxx对应要连接数据库的名字, 后面的是可选的 修改配置信息,看实际情况修改,比如时区等
url: jdbc:mysql://localhost:3306/xxxx?characterEncoding=utf8&useSSL=true&serverTimezone=UTC&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 'root'
#数据库连接池 hikari:
hikari:
pool-name: DateHikariCP
#最小链接数
minimum-idle: 5
#空闲链接存活最大时间, 默认
idle-timeout: 1000000
#最大连接数
maximum-pool-size: 10
#从连接池返回的连接自动提交
auto-commit: true
#连接最大存活时间; 0 永久存活 默认
max-lifetime: 1000000
#连接超时时间, 默认30
connection-timeout: 30000
# 测试连接是否可以用
#connection-init-sql: SELECT 1
#日志输出级别、文件
logging:
level:
root: info
com.tab: debug
file:
path: log/blog-dev.log
#服务端口
server:
port: 1316
3、生产环境(日志级别可以设 高一点)
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/xxxx?characterEncoding=utf8&useSSL=true&serverTimezone=UTC&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 'root'
#数据库连接池 hikari:
hikari:
pool-name: DateHikariCP
#最小链接数
minimum-idle: 5
#空闲链接存活最大时间, 默认
idle-timeout: 1000000
#最大连接数
maximum-pool-size: 10
#从连接池返回的连接自动提交
auto-commit: true
#连接最大存活时间; 0 永久存活 默认
max-lifetime: 1000000
#连接超时时间, 默认30
connection-timeout: 30000
# 测试连接是否可以用
#connection-init-sql: SELECT 1
logging:
level:
root: warn
com.tab: info
file:
path: log/blog-pro.log
server:
port: 1317
配置文件弄好了,可以尝试开启项目,成功表示数据库连接 ok;
3、创建公共返回结果
公共返回结果,含有 状态信息、传递信息,方便后续在 controller层 作为返回值;
要创建两个, 一个 RespBeanEnum, 一个 RespBean;
RespBeanEnum:设置一些状态码
package com.tab.seckilltest.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
@AllArgsConstructor
@Getter
@ToString
public enum RespBeanEnum {
//通用状态码
SUCCESS(200, "success"),
ERROR(500, "服务端异常");
//登录模块状态码
private final Integer code;
private final String meesage;
}
RespBean 设置返回的状态信息的 通用函数
/**
* projectName: SeckillTest
* fileName: RespBean.java
* packageName: com.tab.seckilltest.dto
* date: 2021-10-03 21:05
* copyright(c) 2017-2020 xxx公司
*/
package com.tab.seckilltest.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @version: V1.0
* @author: Tab
* @className: RespBean
* @packageName: com.tab.seckilltest.dto
* @description: 返回的结果对象
* @data: 2021-10-03 21:05
**/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class RespBean {
private long code;
private String message;
private Object obj;
public static RespBean success(){
return new RespBean(RespBeanEnum.SUCCESS.getCode(),
RespBeanEnum.SUCCESS.getMeesage(), null);
}
public static RespBean success(Object obj){
return new RespBean(RespBeanEnum.SUCCESS.getCode(),
RespBeanEnum.SUCCESS.getMeesage(), obj);
}
public static RespBean error(RespBeanEnum respBeanEnum){
return new RespBean(respBeanEnum.getCode(),
respBeanEnum.getMeesage(),null);
}
}
4、全局异常处理
spingmvc将所有类型的异常处理 从各处理过程 解耦出来,实现了异常信息的统一处理和维护;
Springboot全局异常处理主要种种方式
- 使用 @ControllerAdvice 和 @ExceptionHandler 注解
- 使用 ErrorController类来实现
区别:
1、使用@ControllerAdvice方式只能处理控制器抛出的异常,此时请求已经进入控制器中。
2、ErrorContorller类方式可以处理所有异常,包括未进入控制器的错误,比如404、401等错误。
3、如果应用中两者共同存在,则@ControllerAdvice方式处理控制器抛出的异常,ErrorController处理未进入控制器的异常;
4、@ControllerAdvice方式可以定义多个拦截器方法,拦截不同的异常类,并且可以获取抛出异常信息,自由度更大;
(1)我们创建 一个自定义的全局异常类(根据实际情况,我们可以定义不同的异常类), 和一个处理全局异常的 GlobalException;
这个类 有一个成员 RespBeanEnum(看上面), 里面有 异常的 信息;
/**
* projectName: SeckillTest
* fileName: GlobalException.java
* packageName: com.tab.seckilltest.exception
* date: 2021-10-03 21:27
* copyright(c) 2017-2020 xxx公司
*/
package com.tab.seckilltest.exception;
import com.tab.seckilltest.dto.RespBeanEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* @version: V1.0
* @author: Tab
* @className: GlobalException
* @packageName: com.tab.seckilltest.exception
* @description: 设定一个 异常种类的 成员变量
* @data: 2021-10-03 21:27
**/
@Data
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class GlobalException extends RuntimeException{
private RespBeanEnum respBeanEnum;
}
(2)GlobalExceptionHandler
可以在 controller 直接抛出 自定义类型的异常信息(本身也要返回 RespBean对象),前端能获取信息,呈现给用户,比如:用户名密码错误;
/**
* projectName: SeckillTest
* fileName: GlobalExceptionHandler.java
* packageName: com.tab.seckilltest.exception
* date: 2021-10-03 21:30
* copyright(c) 2017-2020 xxx公司
*/
package com.tab.seckilltest.exception;
import com.tab.seckilltest.dto.RespBean;
import com.tab.seckilltest.dto.RespBeanEnum;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* @version: V1.0
* @author: Tab
* @className: GlobalExceptionHandler
* @packageName: com.tab.seckilltest.exception
* @description:
* @data: 2021-10-03 21:30
**/
@RestControllerAdvice
//这里用 RestController,返回的是 json数据包
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public RespBean ExceptionHandler(Exception e){
//如果是 自定义异常,就返回他的 异常信息
if(e instanceof GlobalException){
GlobalException ex = (GlobalException) e;
return RespBean.error(ex.getRespBeanEnum());
}
//如果是 springboot 框架绑定的异常, 就处理
else if(e instanceof BindException){
BindException ex = (BindException) e;
RespBean respBean = RespBean.error(RespBeanEnum.BIND_ERROR);
respBean.setMessage("参数校验异常" + ex.getBindingResult().getAllErrors().get(0).getDefaultMessage());
return respBean;
}
//返回错误
return RespBean.error(RespBeanEnum.ERROR);
}
}
5、日志处理
如果我们想要 知道用户访问 一些信息,比如 url、ip、classMethod、args等等,可以在用户访问Controller的 时候,用aop 来截取信息;
/**
* projectName: SeckillTest
* fileName: LogAspect.java
* packageName: com.tab.seckilltest.aspect
* date: 2021-10-03 21:58
* copyright(c) 2017-2020 xxx公司
*/
package com.tab.seckilltest.aspect;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* @version: V1.0
* @author: Tab
* @className: LogAspect
* @packageName: com.tab.seckilltest.aspect
* @description:
* @data: 2021-10-03 21:58
**/
@Slf4j
@Aspect
@Component
public class LogAspect {
//定义切面,到controller包
@Pointcut("execution(* com.tab.seckilltest.controller.*.*(..))")
public void log(){}
//定义请求信息 类
@AllArgsConstructor
@Data
@ToString
@NoArgsConstructor
private class RequestLog{
private String url;
private String ip;
private String classMethod;
private Object[] args;
}
@Before("log()")
public void doBefore(JoinPoint joinPoint){
log.info("-----doBefore-----");
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String url = request.getRequestURL().toString();
String ip = request.getRemoteAddr();
String classMethod = joinPoint.getSignature().getDeclaringTypeName() + "." +
joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
RequestLog requestLog = new RequestLog(url,ip,classMethod,args);
log.info("Request : {}", requestLog);
}
@After("log()")
public void doAfter(JoinPoint joinpoint){
log.info("-----doAfter-----");
}
}