我们再搭建项目后,时常需要进行对程序的一些监控,比如日志,运行时间等,所以需要用到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的地址,就能看到我们单独的切入点:
