一,了解自定义注解
1.1 自定义注解的定义、描述
定义:
注解是一种能被添加到java源代码中的元数据,方法、类、参数和包都可以用注解来修饰。
注解可以看作是一种特殊的标记,可以用在方法、类、参数和包上,程序在编译或者运行时可以检测到这些标记而进行一些特殊的处理。
注解不会对所修饰的代码产生直接的影
使用范围:
注解又许多用法,其中有:为编译器提供信息 - 注解能被编译器检测到错误或抑制警告。
编译时和部署时的处理 - 软件工具能处理注解信息从而生成代码,XML文件等等。
运行时的处理 - 有些注解在运行时能被检测到。
1.2 自定义注解的基本语法
注解类型的声明部分:
注解在Java中,与类、接口、枚举类似,因此其声明语法基本一致,只是所使用的关键字有所不同@interface。
在底层实现上,所有定义的注解都会自动继承java.lang.annotation.Annotation接口。
格式:public @Interface 注解名 { 定义内容 }
public @interface MyAnnotation {
}
注解类型的实现部分:
根据我们在自定义类的经验,在类的实现部分无非就是书写构造、属性或方法。但是,在自定义注解中,其实现部分只能定义一个东西:注解类型元素(annotation type element)。咱们来看看其语法:
public @interface MyAnnotation {
public String name();
int age();
}
创建注解的基本元素:
修饰符
访问修饰符必须为public,不写默认为pubic;
关键字
关键字为@interface;
注解名称
注解名称为自定义注解的名称,例如上面的XinLinLog 就是注解名称
注解类型元素
注解类型元素是注解中内容,根据需要标志参数,例如上面的注解的value;
1.3 常用的自定义注解(@Target、@Retention、@Inherited、@Documented)
1.3.1 @Target:用于描述注解的使用范围,该注解可以使用在什么地方
Target类型 描述
ElementType.TYPE 应用于类、接口(包括注解类型)、枚举
ElementType.FIELD 应用于属性(包括枚举中的常量)
ElementType.METHOD 应用于方法
ElementType.PARAMETER 应用于方法的形参
ElementType.CONSTRUCTOR 应用于构造函数
ElementType.LOCAL_VARIABLE 应用于局部变量
ElementType.ANNOTATION_TYPE 应用于注解类型
ElementType.PACKAGE 应用于包
备注:例如@Target(ElementType.METHOD),标志的注解使用在方法上,但是我们在这个注解标志在类上,就会报错
1.3.2 @Retention:表明该注解的生命周期
生命周期类型 描述
RetentionPolicy.SOURCE 编译时被丢弃,不包含在类文件中
RetentionPolicy.CLASS JVM加载时被丢弃,包含在类文件中,默认值
RetentionPolicy.RUNTIME 由JVM 加载,包含在类文件中,在运行时可以被获取到
1.3.3 @Inherited:是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
1.3.4 @Documented:表明该注解标记的元素可以被Javadoc 或类似的工具文档化
public static void main(String[] args) {
// 利用反射在程序运行时拿到注解
for(Annotation a :TestController.class.getAnnotations()){
System.out.println(a);
if(a instanceof MyAnnotation){
System.out.println(((MyAnnotation) a).message());
}
}
// 判断遍历出来的注解是否属于某个注解
MyAnnotation myAnnotation = TestController.class.getAnnotation(MyAnnotation.class);
if (myAnnotation!=null){
System.out.println(myAnnotation.message());
}
}
二,切面日志
2.0 传统的方法
2.1 定义切面类
package com.yk.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Slf4j
public class LogAop {
// 定义切入点
// @Pointcut("execution(* com.yk.controller.*Controller.*(..))")
@Pointcut("@annotation(com.yk.annotation.MyLog)")
public void logger(){}
@Around("logger()")
public Object around(ProceedingJoinPoint point){
// 获得当前执行的方法名称
Signature methodName = point.getSignature();
// 日志输出
log.info(methodName+"方法执行");
long l1 = System.currentTimeMillis();
// 让方法执行
Object obj = null;
try {
obj = point.proceed(point.getArgs());
} catch (Throwable e) {
e.printStackTrace();
}
// 日志输出
log.info(methodName+"方法结束"+"\t耗时"+(System.currentTimeMillis()-l1));
return obj;
}
}
2.2 编写测试类
package com.yk.controller;
import com.yk.annotation.MyAnnotation;
import com.yk.annotation.MyLog;
import org.aspectj.bridge.Message;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@MyLog
@RequestMapping("/add")
public String add(){
return "yes";
}
@RequestMapping("/del")
public String del(){
return "yes";
}
@RequestMapping("/upd")
public String upd(){
return "yes";
}
@MyLog
@RequestMapping("/list")
public String list(){ return "yes"; }
}
测试:
2.3 现在利用注解来获取日志 定义一个专用切面的注解
package com.yk.annotation;
import java.lang.annotation.*;
/**
* @author lgs
*/
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}
2.4 定义注解切入点
2.5 在对应方法上注入
测试 :
三,前端响应
3.1 自定义注解
package com.yk.res;
import java.lang.annotation.*;
/**
* @author hgh
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.METHOD})
public @interface ResponseResult {
}
3.2 响应对象封装类
package com.yk.res;
import lombok.Data;
import java.io.Serializable;
/**
* 响应对象封装类
*
* @author hgh
*/
@Data
public class Result<T> implements Serializable {
private final int code;
private final String message;
private final T data;
/**
* 私有构造, 只允许通过static调用构造
*
* @param resultCode 结果枚举
* @param data 响应数据
*/
private Result(ResultCode resultCode, T data) {
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
this.data = data;
}
/**
* 成功调用返回的结果(无数据携带)
*
* @return Result
*/
public static Result success() {
return success(null);
}
/**
* 成功调用返回的结果(数据携带)
*
* @return Result
*/
public static <T> Result success(T data) {
return new Result(ResultCode.SUCCESS, data);
}
/**
* 失败调用返回的结果(数据携带)
*
* @param resultCode 状态枚举
* @param data 携带的数据
* @return Result
*/
public static <T> Result failure(ResultCode resultCode, T data) {
return new Result(resultCode, data);
}
/**
* 失败调用返回的结果(无数据携带)
*
* @param resultCode 状态枚举
* @return Result
*/
public static Result failure(ResultCode resultCode) {
return failure(resultCode, null);
}
}
3.3 响应结果码枚举
package com.yk.res;
import java.io.Serializable;
/**
* 响应结果码枚举
*
* @author hgh
*/
public enum ResultCode implements Serializable {
/* 正常状态 */
SUCCESS(100, "成功"),
FAILURE(101, "失败"),
UNKNOWN(102, "未知响应"),
/**
* 用户code范围: 200~300;
*/
USER_ACCOUNT_NOT_FIND(201, "用户名不存在"),
USER_ACCOUNT_DISABLED(202, "该用户已被禁用"),
USER_PASSWORD_NOT_MATCH(203, "该用户密码不一致"),
USER_PERMISSION_ERROR(204, "该用户不具备访问权限"),
USER_STATE_OFF_LINE(205, "该用户未登录");
private final Integer code;
private final String message;
ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
public static ResultCode queryCode(Integer code) {
for (ResultCode value : values()) {
if (code.equals(value.code)) {
return value;
}
}
return UNKNOWN;
}
}
3.4 ResponseParse
package com.yk.res;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* @author hgh
*/
@RestControllerAdvice
public class ResponseParse implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
//返回值决定他是否需要进入beforeBodyWrite
return methodParameter.getMethod().isAnnotationPresent(ResponseResult.class);
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//更改返回值
if (o == null) {
return Result.success();
}
if (o instanceof Integer) {
return Result.failure(ResultCode.queryCode((Integer) o));
}
if (o instanceof ResultCode) {
return Result.failure((ResultCode) o);
}
if (o instanceof Result) {
return o;
}
return null;
}
}
3.5 测试以下几种不同情况
package com.yk.controller;
import com.yk.annotation.MyAnnotation;
import com.yk.annotation.MyLog;
import com.yk.res.ResponseResult;
import com.yk.res.Result;
import com.yk.res.ResultCode;
import org.aspectj.bridge.Message;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@MyLog
@RequestMapping("/add")
@ResponseResult
public Result add(){
return Result.success();
}
@ResponseResult
@RequestMapping("/del")
public Object del(){
return 201;
}
@ResponseResult
@RequestMapping("/upd")
public Object upd(){
return ResultCode.USER_ACCOUNT_NOT_FIND;
//用户名不存在
}
@ResponseResult
@RequestMapping("/list")
public Object list(){
return Result.success("yes");
}
}
OK!到这就结束了,希望能帮到你!!!