一、适用场景
业务需求中可能会遇到需要记录用户对某个位置的点击次数,需要统计用户的操作频率,分析用户的操作习惯和规律,从而得出最热文章,最热商品等,然后再根据分析结果给用户推荐对应的文章、商品等。目前最火的当然是大数据分析技术,但是一般的小项目可能用不到,甚至只是想记录一下用户的操作日志,这时候就可以考虑下使用spring的AOP。
二、@Aspect注解
在使用之前我们先来了解下这个东东。AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
在spring AOP中业务逻辑仅仅只关注业务本身,将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
TM的好烦啊,一堆文字。总之一句话:一块豆腐分成好几块,做成不同的臭豆腐、卤豆腐、豆腐干,@Aspect就是这个分隔片。
好了,不装B了,来看看它的相关注解:
@Aspect:作用是把当前类标识为一个切面供容器读取
```java
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
@Around:环绕增强,相当于MethodInterceptor
@AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice
@After: final增强,不管是抛出异常或者正常退出都会执行
三、准备工作
老套路:引依赖、改配置
<!--spring切面aop依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
在application.properties文件里加这样的配置
#切面启用
spring.aop.proxy-target-class=true
spring.aop.auto=true
四、简单应用一
4.1 创建实体类
import java.io.Serializable;
import java.util.Date;
/**
* @ClassName SysLog
* @Description TODO
* @Author chenb2
* @Date 2020/6/12 17:25
* @Version 1.1
*/
public class SysLog implements Serializable {
private Long id;
private String username; //用户名
private String operation; //操作
private String method; //方法名
private String params; //参数
private String ip; //ip地址
private Date createDate; //操作时间
//创建getter和setter方法
}
4.2 自定义注解
/**
* @ClassName MyLog
* @Description TODO
* @Author chenb2
* @Date 2020/6/12 17:23
* @Version 1.1
*/
import java.lang.annotation.*;
/**
* 自定义注解类
*/
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //生成文档
public @interface MyLog {
String value() default "";
}
4.3 创建切片
import com.alibaba.fastjson.JSON;
import com.gisquest.delivery.platform.model.user.User;
import com.gisquest.delivery.platform.utils.common.IpUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
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 javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.Date;
/**
* 系统日志:切面处理类
*/
@Aspect
@Component
public class SysLogAspect {
/* @Autowired
private SysLogService sysLogService;*/
//定义切点 @Pointcut
//在注解的位置切入代码
@Pointcut("@annotation(com.gisquest.delivery.platform.annotation.MyLog )")
public void logPoinCut() {
}
//切面 配置通知
@AfterReturning("logPoinCut()")
public void saveSysLog(JoinPoint joinPoint) {
System.out.println("切面。。。。。");
//保存日志
SysLog sysLog = new SysLog();
//从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取切入点所在的方法
Method method = signature.getMethod();
//获取操作
MyLog myLog = method.getAnnotation(MyLog.class);
if (myLog != null) {
String value = myLog.value();
sysLog.setOperation(value);//保存获取的操作
}
//获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
//获取请求的方法名
String methodName = method.getName();
sysLog.setMethod(className + "." + methodName);
//请求的参数
Object[] args = joinPoint.getArgs();
//将参数所在的数组转换成json
String params = JSON.toJSONString(args);
sysLog.