項目當中需要對用戶操作菜單的行為記錄日志,用SpringAOP寫了個大概實現,切點是采用注解的方式,用包名的方式雖然也可以達到相同的效果,但是不如注解方式靈活方便。
不多說,直接上代碼,此處只是簡單寫寫實現原理。
工程目錄:
pom.xml引入以下依賴:
<!-- 熱部署模塊 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> <!-- 這個需要為 true 熱部署才有效 --> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- spring-boot aop依賴配置引入 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
注解:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface Action { String description() default "no description"; }
Controller類:
/** * @auther: gaopeng */ @RestController public class AspectController { /** * 走切面 * @return */ @GetMapping("/test") @Action(description = "執行了test操作菜單") public String test(){ return "method return"; } /** * 不走切面 */ @GetMapping("/test1") private void test1(){ } /** * 走切面,拋異常 */ @GetMapping("/throws") @Action(description = "執行了throws菜單但是拋了異常") public void throwsException(){ throw new RuntimeException(); } }
切面類:
/** * @auther: gaopeng */ @Aspect @Component public class TestAspect { /** * 切入點 */ // 此處的切點是注解的方式,也可以用包名的方式達到相同的效果 //@Pointcut("execution(public * com.gaopeng.springboot.mytest.controller.*.*(..))") @Pointcut("@annotation(com.gaopeng.springboot.mytest.annotation.Action)") public void execute(){ } /** * 前置通知 * @param joinPoint */ @Before(value ="execute()") public void Before(JoinPoint joinPoint) { System.out.println("執行方法之前"); } /** * 環繞通知 * @param proceedingJoinPoint * @return */ @Around(value ="execute()") public Object around(ProceedingJoinPoint proceedingJoinPoint) { System.out.println("環繞通知開始"); try { System.out.println("執行方法:" + proceedingJoinPoint.getSignature().getName()); MethodSignature signature =(MethodSignature) proceedingJoinPoint.getSignature(); Action action = signature.getMethod().getAnnotation(Action.class); System.out.println("菜單="+action.description()); Object object = proceedingJoinPoint.proceed(); System.out.println("環繞通知結束,方法返回:" + object); return object; } catch (Throwable e) { System.out.println("執行方法異常:" + e.getClass().getName()); return null; } } /** * 后置通知 * @param joinPoint */ @After(value ="execute()") public void After(JoinPoint joinPoint) { System.out.println("執行方法之后"); } /** * 后置通知,帶返回值 * @param obj */ @AfterReturning(pointcut = "execute()",returning = "obj") public void AfterReturning(Object obj) { System.out.println("執行方法之后獲取返回值:"+obj); } /** * 后置通知,異常時執行 * @param e */ @AfterThrowing(throwing = "e",pointcut = "execute()") public void doAfterThrowing(Exception e) { System.out.println("執行方法異常:"+e.getClass().getName()); } }
運行結果: