目录
一、Lombok优雅的编码
1.1 配置Lombok
-
IDEA安装lombok插件
步骤:file-》setting-》Plugins-》Marketplace-》搜索lombok-》intall-》重启IDEA
2.允许enable注解处理器
步骤:在Settings设置页面,我们点击Build,Execution,Deployment-->选择Compiler-->选中Annotation Processors
,然后在右侧勾选Enable annotation processing
即可。
3.安装的lombok和Maven一样是一个插件处理器,需要在pom中声明调用查看最新版本:https://mvnrepository.com/artifact/org.projectlombok/lombok/
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> <scope>provided</scope> </dependency>
1.2 常用Lombok方法
1.2.1 @Getter@Setter
不使用lombok的实体类:
@Getter@Setter
简化get和set函数:
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name="t_user")
@Getter
@Setter
public class UserEntity implements Serializable{
@Id
@GeneratedValue
@Column(name="t_id")
private Long id;
@Column(name = "t_name")
private String name;
@Column(name = "t_age")
private int age;
@Column(name = "t_address")
private String address;
@Column(name = "t_password")
private Long password;
}
test函数:
@Test
public void testLombok()
{
//测试Getter/Setter
UserEntity user = new UserEntity();
user.setName("测试lombok");
user.setAge(10);
user.setAddress("测试地址");
System.out.println(user.getName()+" " + user.getAge() +" "+user.getAddress());
}
运行结果:测试lombok 10 测试地址
,getter/setter没毛病
1.2.3 @ToString()
实体类
@Getter
@Setter
@ToString
public class UserEntity implements Serializable{
...
测试
System.out.println(user.toString());
运行结果:UserEntity(id=null, name=测试lombok, age=10, address=测试地址, password=null)
Lombok自动创建的toString方法会将所有的属性都包含并且调用后可以输出。
1.2.3 @AllArgsConstructor@NoArgsConstructor
实体类:
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class UserEntity implements Serializable{
...
- @AllArgsConstructor
UserEntity user = new UserEntity(1l,"Lombok",20,"构造lombok",123456l);
2.@NoArgsConstructor
和1.2.1中写法相同UserEntity user = new UserEntity();.....
@Test
public void testLombok2()
{
//测试AllArgsConstructor
UserEntity user = new UserEntity(1l,"Lombok",20,"构造lombok",123456l);
//id:1l,password:123456l是因为Long类型写法
System.out.println(user.getName()+" " + user.getAge() +" "+user.getAddress());
System.out.println(user.toString());
}
测试结果:
测试lombok 10 测试地址
UserEntity(id=1, name=Lombok, age=20, address=构造lombok, password=123456)
1.2.3 @Data
使用@Data
注解就可以涵盖@ToString、@Getter、@Setter
方法,当然我们使用构造函数时还是需要单独添加注解,下面我们修改实体类添加@Data注解代码如下所示
import lombok.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserEntity implements Serializable{
...
重复以上测试均无误。所以以后用
@Data @AllArgsConstructor @NoArgsConstructor
就ok了!
常用注解:
# | 注解 | 用法 | 作用 |
---|---|---|---|
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变量,严格意义来说是常量。private static final Logger log = LoggerFactory.getLogger(UserController.class); |
1.2.4 @Slf4j
日志输出,替代System.out.println();而且可以保存在日志中。
import lombok.extern.slf4j.Slf4j;
@Slf4j
....
log.info(user.toString());
结果:
2019-08-14 11:40:42.923 INFO 31844 --- [ main] c.s.three.ThreeApplicationTests : UserEntity(id=1, name=Lombok, age=20, address=构造lombok, password=123456)
二、Aop异常统一管理
2.1 为什么要用Aop?
-
Aop认识
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。在日常开发当中经常用来记录日志,方法跟踪、事务,权限等。 -
三种默认
Springboot默认error:-》启动该应用,访问一个不存在的URL。
控制台打印出:2019-08-14 13:38:08.949 WARN 28636 --- [nio-8090-exec-9] o.s.web.servlet.PageNotFound : No mapping for POST /abc
默认异常原理:
Spring Boot提供了一个默认的映射:/error,当处理中抛出异常之后,会转到该请求中处理,并且该请求有一个全局的错误页面用来展示异常内容。
~
对应引入的spring-boot-autoconfigure-2.1.7.RELEASE.jar
包中,有这样一个错误控制层函数BasicErrorController
,其中有返回html和json的方法:
-》用postman测试:响应的json如下
-
统一异常处理:
- 场景1 返回HTML页面(比较简单,参考链接)
- 场景2 返回JSON页面 ?(我们只想要一个json返回结果,而不是跳转到硬生生的error页面,选择这种方式)
原版:data
加强版Result结果:
2.2 Aop配置
2.2.1 文档说明
查看springboot Guide官方文档针对异常处理部分Error Handling解释
快速链接:Spring Boot-2.1.7 Reference Guide(29.1.11 Error Handling)
结论:
- 我们可以通过
@ControllerAdvice
(控制增强)+@ExceptionHandler
进行统一异常处理, - ExceptionHandler注解可以设置全局处理控制里的异常类型来拦截要处理的异常。 例如:@ExceptionHandler(value = Exception.class)
2.2.2 Aop引入
项目目录:
-
pom引入Aop依赖
<!--AOP主要依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
-
首先定义响应状态码枚举类
ResultCode.java
,返回json,其中包含- code:错误码,0表示没有异常信息。
- message:异常提示信息
- date:无异常返回具体内容。
因为是枚举类,所以class类型为Enum:
package com.springboot.three.aop; public enum ResultCode { SUCCESS(200),//成功 FAIL(400),//失败 UNAUTHORIZED(401),//未认证(签名错误) NOT_FOUND(404),//接口不存在 INTERNAL_SERVER_ERROR(500);//服务器内部错误 private final int code; ResultCode(int code) { this.code = code; } public int code() { return code; } }
-
定义 响应类
Result.java
package com.springboot.three.aop; import com.fasterxml.jackson.annotation.JsonInclude; /** * 响应类 */ public class Result { // 状态响应码 private int code; // 响应结果 成功/失败 private boolean success; // 响应信息 private String message; // 响应数据 @JsonInclude(JsonInclude.Include.NON_NULL) private Object data; public Result setCode(ResultCode resultCode) { this.code = resultCode.code(); return this; } public int getCode() { return code; } public boolean isSuccess() { return success; } public Result setSuccess(boolean success) { this.success = success; return this; } public String getMessage() { return message; } public Result setMessage(String message) { this.message = message; return this; } public Object getData() { return data; } public Result setData(Object data) { this.data = data; return this; } }
-
对响应结果封装,做预处理
ResultGenerator.java
package com.springboot.three.aop; /** * 响应结果,预处理 */ public class ResultGenerator { private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS"; /** * 成功响应 * code:200 * 无参返回data:null * 成功信息:message:SUCCESS * @return */ public static Result genSuccessResult() { return new Result() .setCode(ResultCode.SUCCESS) .setSuccess(true) .setMessage(DEFAULT_SUCCESS_MESSAGE); } /** * 成功响应 * code:200 * 有参返回data:实体类或集合 * 成功信息:message:SUCCESS * @param data * @return */ public static Result genSuccessResult(Object data) { return new Result() .setCode(ResultCode.SUCCESS) .setSuccess(true) .setMessage(DEFAULT_SUCCESS_MESSAGE) .setData(data); } /** * 错误响应 * code:400等 * 无参返回data:null * 错误信息message:"/by zero"等 * @param message * @return */ public static Result genFailResult(String message) { return new Result() .setCode(ResultCode.FAIL) .setSuccess(false) .setMessage(message); } }
-
全局异常处理类(这是监控error)
ExceptionControllerAdvice.java
package com.springboot.three.aop; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.servlet.http.HttpServletRequest; /** * 异常处理类 * restful 风格处理异常信息 */ @RestControllerAdvice public class ExceptionControllerAdvice { /** * 全局异常捕捉处理 * @param req * @param e * @return */ @ExceptionHandler(Exception.class) public Result jsonErrorHandler(HttpServletRequest req, Exception e){ return ResultGenerator.genFailResult(e.getMessage()); } }
aop配置部分参考于:SpringBoot2.X (十六): SpringBoot 全局异常配置
仅仅多加了一些注释。
2.3 返回值测试
-
Controller中:一个简单的测试
【get类型,成功:只返回成功data对象,不返回code,message;失败:返回错误码和提示】
@GetMapping(value="/abc") public Object testException(){ int i = 5/0;//分母不为0,会报错400 return i; }
get请求:
http://127.0.0.1:8090/user/abc
结果:会报错400,message报错:分母不为0
-》修改为post类型,重启测试:(post要加上条件查询)
@RequestMapping(value="/abc",method = RequestMethod.POST) public Object testException(Integer num){ int i = 5/num;//分母不为0,会报错400 return i; }
post请求:
http://127.0.0.1:8090/user/abc?num=0
:报错400
http://127.0.0.1:8090/user/abc?num=1
:仅显示5/1结果==5
-
【Post类型,成功:不返回成功data对象,只返回code,message等;失败:返回错误码和提示】
//aop测试:【post类型,成功:有返回值,失败:无返回值】 @RequestMapping(value ="/aop1",method = RequestMethod.POST) public Result aopTest2(Integer num,Result result,Exception e) { int i = 5/num;//分母不为0,会报错400 return ResultGenerator.genSuccessResult(); }
post请求:
http://127.0.0.1:8090/user/aop1?num=0
和http://127.0.0.1:8090/user/aop1?num=1
结果:Result结果
-
【Post类型,成功:返回成功data对象,返回code,message等;失败:返回错误码和提示】
//aop测试:【post类型,全返回】 @RequestMapping(value ="/aop",method = RequestMethod.POST) public Result aopTest(String name, Long password,Exception e) { UserEntity userEntity= userJPA.findByNameAndPassword(name,password); if(userEntity!=null){ return ResultGenerator.genSuccessResult(userEntity); }else { return ResultGenerator.genFailResult(e.getMessage()); } }
post请求:
http://127.0.0.1:8090/user/aop?name=test&password=123456
(数据库有的,查询到了)
http://127.0.0.1:8090/user/aop?name=test&password=123
(数据库没有的,查询不到)
结果:Result结果
-》这是data返回集合:同理,调用ResultGenerator.genSuccessResult(userlist);
就行@RequestMapping(value = "/aop2", method = RequestMethod.GET) public Result aopList3(Exception e) { List<UserEntity> userlist = userJPA.findAll(); if(userlist!=null){ return ResultGenerator.genSuccessResult(userlist); }else { return ResultGenerator.genFailResult(e.getMessage()); } }
get请求:
http://127.0.0.1:8090/user/aop2
结果:data可以存放list
大致就是上面这些了!
2.4 log日志配置
新建TestAspect.java
,封装日志信息
test示例:
package com.springboot.three.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class TestAspect {
//com.springboot.three.controller 包中所有的类的所有方法切面
//@Pointcut("execution(public * com.springboot.three.controller.*.*(..))")
//只针对 MessageController 类切面
//@Pointcut("execution(public * com.springboot.three.controller.MessageController.*(..))")
//统一切点,对com.springboot.three.controller及其子包中所有的类的所有方法切面
@Pointcut("execution(public * com.springboot.three.controller..*.*(..))")
public void Pointcut() {
}
//前置通知
@Before("Pointcut()")
public void beforeMethod(JoinPoint joinPoint){
log.info("调用了前置通知");
}
//@After: 后置通知
@After("Pointcut()")
public void afterMethod(JoinPoint joinPoint){
log.info("调用了后置通知");
}
//@AfterRunning: 返回通知 rsult为返回内容
@AfterReturning(value="Pointcut()",returning="result")
public void afterReturningMethod(JoinPoint joinPoint,Object result){
log.info("调用了返回通知");
}
//@AfterThrowing: 异常通知
@AfterThrowing(value="Pointcut()",throwing="e")
public void afterReturningMethod(JoinPoint joinPoint, Exception e){
log.info("调用了异常通知");
}
//@Around:环绕通知
@Around("Pointcut()")
public Object Around(ProceedingJoinPoint pjp) throws Throwable {
log.info("around执行方法之前");
Object object = pjp.proceed();
log.info("around执行方法之后--返回值:" +object);
return object;
}
}
请求http://localhost:8090/send_message?name=aop&password=1111
后端:
aspect类内部执行顺序:
完善:
package com.springboot.three.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Aspect
@Component
@Slf4j
public class TestAspect {
//com.springboot.three.controller 包中所有的类的所有方法切面
//@Pointcut("execution(public * com.springboot.three.controller.*.*(..))")
//只针对 MessageController 类切面
//@Pointcut("execution(public * com.springboot.three.controller.MessageController.*(..))")
//统一切点,对com.springboot.three.controller及其子包中所有的类的所有方法切面
@Pointcut("execution(public * com.springboot.three.controller..*.*(..))")
public void Pointcut() {
}
//@Around:环绕通知
@Around("Pointcut()")
public Object Around(ProceedingJoinPoint pjp) throws Throwable {
Map<String,Object> data = new HashMap<>();
//获取目标类名称
String clazzName = pjp.getTarget().getClass().getName();
//获取目标类方法名称
String methodName = pjp.getSignature().getName();
//记录类名称
data.put("clazzName",clazzName);
//记录对应方法名称
data.put("methodName",methodName);
//记录请求参数
data.put("params",pjp.getArgs());
//开始调用时间
// 计时并调用目标函数
long start = System.currentTimeMillis();
Object result = pjp.proceed();
Long time = System.currentTimeMillis() - start;
//记录返回参数
data.put("result",result);
//设置消耗总时间
data.put("consumeTime",time);
System.out.println(data);
return result;
}
}
请求:http://127.0.0.1:8090/user/abc?num=1
运行效果:
{result=5, consumeTime=0, methodName=testException, params=[Ljava.lang.Object;@71747c2, clazzName=com.springboot.three.controller.UserController}
我觉得不太满意,用HashMap存储,可视化太不良好了,下面是给出的一个优化:
完整:TestAspect.java
package com.springboot.three.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
@Aspect
@Component
@Slf4j
public class TestAspect {
//com.springboot.three.controller 包中所有的类的所有方法切面
//@Pointcut("execution(public * com.springboot.three.controller.*.*(..))")
//只针对 MessageController 类切面
//@Pointcut("execution(public * com.springboot.three.controller.MessageController.*(..))")
//统一切点,对com.springboot.three.controller及其子包中所有的类的所有方法切面
@Pointcut("execution(public * com.springboot.three.controller..*.*(..))")
public void Pointcut() {
}
//@Around:环绕通知
@Around("Pointcut()")
public Object Around(ProceedingJoinPoint pjp) throws Throwable {
log.info("我是环绕通知!!!");
log.info("方法 :"+pjp.getSignature().getName());
//AOP代理类的名字
log.info("方法所在包 :"+pjp.getSignature().getDeclaringTypeName());
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
String[] strings = methodSignature.getParameterNames();
log.info("参数名 :"+ Arrays.toString(strings));
log.info("参数值ARGS : " + Arrays.toString(pjp.getArgs()));
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest req = attributes.getRequest();
// 记录下请求内容
log.info("请求URL : " + req.getRequestURL().toString());
log.info("HTTP_METHOD : " + req.getMethod());
log.info("IP : " + req.getRemoteAddr());
log.info("CLASS_METHOD : " + pjp.getSignature().getDeclaringTypeName() + "." + pjp.getSignature().getName());
//开始调用时间
// 计时并调用目标函数
long start = System.currentTimeMillis();
Object result = pjp.proceed();
Long time = System.currentTimeMillis() - start;
//记录返回参数
log.info("请求返回结果result: "+result);
//设置消耗总时间
log.info("环绕通知完毕!!!");
log.info("请求总耗时time:"+time);
return result;
}
}
三、项目源码
https://github.com/cungudafa/SpringBoot/tree/master/springboot学习Day7
??????????????