我们再搭建项目后,时常需要进行对程序的一些监控,比如日志,运行时间等,所以需要用到AOP,而spring boot也提供了很好的AOP。
首先,我们进行引入Spring boot的AOP,再web模块的pom.xml中:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
接着创建aop目录:
而LogAspect.java中,代码如下:
package com.demo.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; 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 public class LogAspect { @Pointcut("execution(public * com.demo.controller.*.*(..))") public void log(){} @Before("log()") public void deBefore(JoinPoint joinPoint) throws Throwable { // 接收到请求,记录请求内容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 记录下请求内容 System.out.println("URL : " + request.getRequestURL().toString()); System.out.println("HTTP_METHOD : " + request.getMethod()); System.out.println("IP : " + request.getRemoteAddr()); System.out.println("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(returning = "ret", pointcut = "log()") public void doAfterReturning(Object ret) throws Throwable { // 处理完请求,返回内容 System.out.println("方法的返回值 : " + ret); } //后置异常通知 @AfterThrowing("log()") public void throwss(JoinPoint jp){ System.out.println("方法异常时执行....."); } //后置最终通知,final增强,不管是抛出异常或者正常退出都会执行 @After("log()") public void after(JoinPoint jp){ System.out.println("方法最后执行....."); } //环绕通知,环绕增强,相当于MethodInterceptor @Around("log()") public Object arround(ProceedingJoinPoint pjp) { System.out.println("方法环绕start....."); try { Object o = pjp.proceed(); System.out.println("方法环绕proceed,结果是 :" + o); return o; } catch (Throwable e) { e.printStackTrace(); return null; } } }
这样,整个AOP已经完成,我们可以访问之前写的hello world地址,可以发现控制器输出:
但是,倘若我们并不是需要每个方法都进行记录,而是对个别的访问进行记录呢?比如我们对数据库的查询进行记录时间,那么应该怎么做呢?
这里,就需要用到单个注解的方式来实现。
首先,创建注解目录和文件:
再RuntimeActuator.java中,代码如下:
package com.demo.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义注解 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RuntimeActuator { String note() default "";//传参用,如果不需要可以不写。 }
这样,注解就创建完毕,然后我们定义注解对应的AOP:
再AOP目录下创建RuntimeAspect.java,代码如下:
package com.demo.aop; import com.demo.annotation.RuntimeActuator; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect @Component public class RuntimeAspect { //这里相当于是定义个全局量 @Pointcut("@annotation(runtimeActuator)") public void serviceStatistics(RuntimeActuator runtimeActuator) { } @Before("serviceStatistics(runtimeActuator)") public void doBefore(JoinPoint joinPoint, RuntimeActuator runtimeActuator) { //runtimeActuator.note()是我们自己设置的 System.out.println("before:"+System.currentTimeMillis() +':'+ runtimeActuator.note()); } @After("serviceStatistics(runtimeActuator)") public void doAfter(RuntimeActuator runtimeActuator) { System.out.println("after:"+System.currentTimeMillis() +':'+ runtimeActuator.note()); } }
至此,整个单个注解的功能就完成了,我们可以给需要的目标加入注解了:
这样,我们运行程序,访问userInfo的地址,就能看到我们单独的切入点: