一、定义一个Annotation类
Annotation的中文意思就是注解,注解的具体意思可以查询菜鸟教程。在这个注解类上需要加上其他三个注解,分别是 @Target,@Retention,@Documented,可以随便点击Springboot中的其他注解(如@Controller,@Service等)可以发现,他们三个都是一直出现的,所有注解都需要加。
import java.lang.annotation.*;
/**
* TYPE代表可以放在类上边,MTTHOD代表可以放在方法上
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
String module() default "";
String operator() default "";
}
复制代码
二、定义切面类Aspect
- 声明了注解之后,就需要定义一个切面类,来实现我们所需要的所有操作。在pt()函数上添加 @Pointcut注解表示切入点,内容为自己刚刚声明的@Annotation的包地址。
- 定义log函数,添加 @Around 注解,用于环绕pt()函数。
import com.alibaba.fastjson.JSON;
import com.jyuxuan.blog.utils.HttpContextUtils;
import com.jyuxuan.blog.utils.IPUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Component
@Aspect // 切面,定义了通知和切点的关系
@Slf4j
public class LogAspect {
@Pointcut("@annotation(com.***.blog.common.aop.LogAnnotation)")
public void pt() {
}
/**
* 环绕通知
*/
@Around("pt()")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = System.currentTimeMillis();
// 执行方法
Object result = joinPoint.proceed();
// 执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
// 保存日志
recordLog(joinPoint, time);
return result;
}
private void recordLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
log.info("=====================log start================================");
log.info("module:{}", logAnnotation.module());
log.info("operation:{}", logAnnotation.operator());
// 请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
log.info("request method:{}", className + "." + methodName + "()");
// 请求的参数
Object[] args = joinPoint.getArgs();
String params = JSON.toJSONString(args[0]);
log.info("params:{}", params);
//获取 request 设置IP地址
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
log.info("ip:{}", IPUtils.getIpAddr(request));
log.info("excute time : {} ms", time);
log.info("=====================log end================================");
}
}
复制代码
三、在Controller方法中使用注解
在自己的随便的一个方法上,添加注解,即可在工作台上查看日志信息。
@PostMapping
@LogAnnotation(module = "名称", operator = "操作名称")
public Result test(@RequestBody Work work) {
return Result.success(null);
}
复制代码
四、补充内容
1、@Target
随便在一个注解上点击进入,即可看到@Target注解,然后点进去@Target注解,发现以下内容。再次点击ElementType
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
复制代码
在这里可以看到@Target可以注解的全部内容信息,全部写在注释中了。
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE, // 类、接口或者枚举
/** Field declaration (includes enum constants) */
FIELD, // 域
/** Method declaration */
METHOD, // 方法
/** Formal parameter declaration */
PARAMETER, // 参数
/** Constructor declaration */
CONSTRUCTOR, // 构造方法
/** Local variable declaration */
LOCAL_VARIABLE, // 局部变量
/** Annotation type declaration */
ANNOTATION_TYPE, // 注解类型
/** Package declaration */
PACKAGE, // 包
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER, // 参数类型
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE // 能标注任何类型
}
复制代码
2、@Retention
指明修饰的注解的生存周期,即会保留到哪个阶段,点击进入。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
复制代码
再次点击RetentionPolicy。
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE, // 表示源码级别保留,编译后即丢失
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS, // 编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,(默认值)
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME // 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用
}
复制代码
3、@Documented
表明这个注释是由 javadoc记录的,表明该注解下的类会出现在java doc html文档中。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
复制代码