【转】 springboot aop+@interface实现日志记录
一、基本概念
1、自定义注解
自定义注解我们必须了解四个元注解,什么是元注解?元注解指作用于注解之上的元数据或者元信息,简单通俗的讲,元注解就是注解的注解 .
Documented与Inherited是典型的标识性注解,也就是说在注解内部并没有成员变量,没有成员变量的注解称为标识注解
Documented
指明拥有这个注解的元素可以被javadoc此类的工具文档化。这种类型应该用于注解那些影响客户使用带注释的元素声明的类型。如果一种声明使用Documented进行注解,这种类型的注解被作为被标注的程序成员的公共API 。
Inherited
指明该注解类型被自动继承。如果用户在当前类中查询这个元注解类型并且当前类的声明中不包含这个元注解类型,那么也将自动查询当前类的父类是否存在Inherited元注解,这个动作将被重复执行知道这个标注类型被找到,或者是查询到顶层的父类。
具体请查看:https://blog.csdn.net/ab411919134/article/details/81252516
Retention
指明在什么级别显示此注解
Retention主要的参数类型包括以下几种:
RetentionPolicy.SOURCE 注解存在于源代码中,编译时会被抛弃
RetentionPolicy.CLASS 注解会被编译到class文件中,但是JVM会忽略
RetentionPolicy.RUNTIME JVM会读取注解,同时会保存到class文件中
Target
指明该类型的注解可以注解的程序元素的范围
Target主要的参数类型包括以下几种:
ElementType.TYPE 用于类,接口,枚举但不能是注解
ElementType.FIELD 作用于字段,包含枚举值
ElementType.METHOD 作用于方法,不包含构造方法
ElementType.PARAMETER 作用于方法的参数
ElementType.CONSTRUCTOR 作用于构造方法
ElementType.LOCAL_VERIABLE 作用于本地变量或者catch语句
ElementType.ANNOTATION_TYPE 作用于注解
ElementType.PACKAGE 作用于包
2、@ControllerAdvice 注解
在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中
二、完整示例
1、工程结构
2、pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.aop.demo</groupId> <artifactId>aop-demo</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.1.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--spring切面aop依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies> </project>
3、新建启动类
package com.aop.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @author Administrator * @date 2018/08/18 */ @SpringBootApplication public class Application { public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); } }
4、自定义异常类
package com.aop.demo.exception; /** * @author Administrator * @date 2018/08/18 */ public class OperateException extends RuntimeException{ public Integer errorCode; public String errorMessage; public OperateException(Integer errorCode, String errorMessage){ this.errorCode = errorCode; this.errorMessage = errorMessage; } public Integer getErrorCode() { return errorCode; } public void setErrorCode(Integer errorCode) { this.errorCode = errorCode; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } }
5、自定义异常处理类,使用了@ControllerAdvice,来捕捉全局异常
package com.aop.demo.exception; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import java.util.HashMap; import java.util.Map; /** * @author Administrator * @date 2018/08/18 */ @ControllerAdvice public class MyExceptionHandler { @ExceptionHandler(value = Exception.class) @ResponseBody public Map<String,Object> exceptionProcess(Exception e){ Map<String,Object> map = new HashMap<String, Object>(); if(e instanceof OperateException){ OperateException myException = (OperateException) e; System.out.println(myException.getErrorCode()+": "+ myException.getErrorMessage()); map.put(myException.getErrorCode().toString(),myException.getErrorMessage()); return map; }else { System.out.println(""); map.put("505","系统级错误"); return map; } } }
6、引入aop来处理方法,执行前,执行中,执行后的动作。这里只是在控制台打印信息
1)定义了一个切入点:logwrite(),对com.aop.demo.controller里面的所有方法有效
举例说明execution表达式:
任意公共方法的执行:
execution(public * *(..))
任何一个以“set”开始的方法的执行:
execution(* set*(..))
AccountService 接口的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
定义在service包里的任意方法的执行:
execution(* com.xyz.service.*.*(..))
定义在service包和所有子包里的任意类的任意方法的执行:
execution(* com.xyz.service..*.*(..))
定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:
execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))")
package com.aop.demo.aspect; import com.aop.demo.controller.MyController; import com.aop.demo.exception.MyExceptionHandler; import com.aop.demo.model.LogWrite; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; 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.lang.reflect.Method; /** * @author Administrator * @date 2018/08/20 */ @Aspect @Component public class HttpAspect { @Autowired private MyExceptionHandler myExceptionHandler; @Pointcut("execution(public * com.aop.demo.controller.*.*(..))") public void logwrite(){ } @Before("logwrite()") public void doBefore(JoinPoint joinPoint){ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); System.out.println("===========================以下是Aspect的Before方法的执行结果=========================="); //url System.out.println("url="+request.getRequestURL()); //method System.out.println("method="+request.getMethod()); //ip System.out.println("id="+request.getRemoteAddr()); //获取类名和方法名 System.out.println("class_method="+joinPoint.getSignature().getDeclaringTypeName() + "," + joinPoint.getSignature().getName()); //args[] System.out.println("args="+joinPoint.getArgs()); for (Method method: MyController.class.getDeclaredMethods()) { if(method.getName().equals(joinPoint.getSignature().getName())){ LogWrite logWrite = method.getAnnotation(LogWrite.class); if(logWrite!=null){ System.out.println("Found LogWrite:"+logWrite.user()+" "+logWrite.action() +"处理中"); } } } } @Around("logwrite()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object result = null; try { result = proceedingJoinPoint.proceed(); return result; } catch (Exception e) { System.out.println("===========================以下是Aspect的doAround方法捕捉到的异常=========================="); for (Method method: MyController.class.getDeclaredMethods()) { if(method.getName().equals(proceedingJoinPoint.getSignature().getName())){ LogWrite logWrite = method.getAnnotation(LogWrite.class); if(logWrite!=null){ System.out.println("Found LogWrite:"+logWrite.user()+" "+logWrite.action() +"处理失败"); } } } return myExceptionHandler.exceptionProcess(e); } } @AfterReturning(pointcut = "logwrite()",returning = "object")//打印输出结果 public void doAfterReturing(JoinPoint joinPoint,Object object){ System.out.println("===========================以下是Aspect的doAfterReturing方法的执行结果=========================="); for (Method method: MyController.class.getDeclaredMethods()) { if(method.getName().equals(joinPoint.getSignature().getName())){ LogWrite logWrite = method.getAnnotation(LogWrite.class); if(logWrite!=null){ System.out.println("Found LogWrite:"+logWrite.user()+" "+logWrite.action() +"处理完成"); } } } System.out.println("response="+object.toString()); } }
7、自定义注解
package com.aop.demo.model; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Administrator * @date 2018/08/18 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LogWrite { public String user() default "adming"; public String action() default "create"; }
8、controller
package com.aop.demo.controller; import com.aop.demo.exception.OperateException; import com.aop.demo.model.LogWrite; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; /** * @author Administrator * @date 2018/08/18 */ @RestController @RequestMapping("/test") public class MyController { @RequestMapping(value = "/aopsuccess",method = RequestMethod.GET) @LogWrite(user = "boshen",action = "excute aopsuccess") public String testSucces(){ return "aopsuccess excute success"; } @RequestMapping(value = "/aoperror/{flag}",method = RequestMethod.GET) @LogWrite(user = "boshen",action = "excute aoperror") public String testError(@PathVariable String flag){ if(flag.equals("A")){ throw new OperateException(1001,"发生业务错误"); } return "testError excute success"; } }
说明:MyController类里面使用了自定义注解:@LogWrite,在HttpAspect 类里面,获取注解的内容得到user和action,打印到控制台上
9、用postman执行
控制台如下:
===========================以下是Aspect的Before方法的执行结果========================== url=http://10.111.131.11:8084/aop-test/test/aopsuccess method=GET id=10.111.131.11 class_method=com.aop.demo.controller.MyController,testSucces args=[Ljava.lang.Object;@3b3ffba0 Found LogWrite:boshen excute aopsuccess处理中 ===========================以下是Aspect的doAfterReturing方法的执行结果========================== Found LogWrite:boshen excute aopsuccess处理完成 response=aopsuccess excute success
控制台如下:
===========================以下是Aspect的Before方法的执行结果========================== url=http://10.111.131.11:8084/aop-test/test/aoperror/A method=GET id=10.111.131.11 class_method=com.aop.demo.controller.MyController,testError args=[Ljava.lang.Object;@67ca9239 Found LogWrite:boshen excute aoperror处理中 ===========================以下是Aspect的doAround方法捕捉到的异常========================== Found LogWrite:boshen excute aoperror处理失败 1001: 发生业务错误 ===========================以下是Aspect的doAfterReturing方法的执行结果========================== Found LogWrite:boshen excute aoperror处理完成 response={1001=发生业务错误}