前言
学习了spring中的切面Aspect,因为平时使用的比较多的时springboot,写了一个springboot中Aspect实现切面的小demo。文章底部会附带源码。
简介
在写之前我们先来看一下aop的基本知识。
AOP是Aspect Oriented Programming的缩写,即面向切面编程。是面向对象编程(oop)的一种补充,在开发中常用于记录日志,方法跟踪等。
AspectJ的注解及描述
注解名称 | 描述 |
---|---|
@AspectJ | 用于定义一个切面 |
@Pointcut | 用于定义切入点表达式。在使用时还需要定义一个包含名字和任意参数的方法签名来表示切入点名称。实际上就是一个返回值为void,且具体方法体为空的普通方法。 |
@Before | 用于定义前置通知,使用时,通常需要指定一个value的属性值,该值用于指定一个切入点表达式。 |
@AfterReturning | 定义后置通知,使用时指定可以point/value和returning属性。 |
@Around | 用于定义环绕通知.使用时也需要一个value属性。 |
@AfterThrowing | 用于定义异常通知来处理程序中未处理的异常。 |
@After | 用于定义final通知,不管是否异常,该通知都会执行。 |
1.使用idea创建一个springboot项目
2.导入依赖包
AOP必须
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
还有一些数据处理需要的依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.57</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
3日志实体类
package com.example.demo2.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description
* @Author
* @Date 2019/10/18 10:51
* @Version
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LogEntity {
//路径
private String className;
//方法名
private String methodName;
//参数
private String params;
//value
private String value;
//创建时间
private String date;
}
4service层
package com.example.demo2.aspect;
import com.example.demo2.entity.LogEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @Description
* @Author
* @Date 2019/10/18 11:02
* @Version
*/
@Slf4j
@Service
public class SysLogService {
/**
* 不做具体实现,在控制台打印出LogEntity类
* @param logEntity
* @return
*/
public boolean sysLog(LogEntity logEntity){
System.out.println(logEntity);
return true;
}
}
5自定义一个注解
package com.example.demo2.aspect;
import java.lang.annotation.*;
/**
* @Description
* @Author
* @Date 2019/10/18 10:55
* @Version
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value() default "";
}
需要说明一下的是@Target(ElementType.METHOD),@Retention(RetentionPolicy.RUNTIME),@Documented这三个注解意思分别是:@Target(ElementType.METHOD)表示该注解的使用范围为方法,除此之外还有ElementType.TYPE,ElementType.ANNOTATION_TYPE等表示作用于类、注解上。@Retention定义它所注解的注解保留多久,RetentionPolicy.RUNTIME表示保留在运行时,我们可以通过反射去获取注解信息。@Documented表示会被包含在javadoc中。
6定义切面
package com.example.demo2.aspect;
import com.alibaba.fastjson.JSON;
import com.example.demo2.entity.LogEntity;
import com.google.common.collect.Lists;
import org.aspectj.lang.JoinPoint;
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.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpSession;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* @Description
* @Author
* @Date 2019/10/18 10:59
* @Version
*/
@Aspect
@Component
public class SysAspect {
@Autowired
private SysLogService sysLogService;
@Pointcut(value = "@annotation(com.example.demo2.aspect.SysLog)")
public void logPointCut() {}
@Before("logPointCut() && @annotation(sysLog)")
public void saveLog(JoinPoint joinPoint,SysLog sysLog) {
LogEntity logEntity = new LogEntity();
//路径
String className =joinPoint.getTarget().getClass().getName();
//方法名
String methodName = joinPoint.getSignature().getName();
logEntity.setClassName(className);
logEntity.setMethodName(methodName);
//时间
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
logEntity.setDate(dateFormat.format(new Date()));
//注解备注
if (sysLog !=null){
logEntity.setValue(sysLog.value());
}
//参数列表
Object[] arguments = joinPoint.getArgs();
List<Object> argumentList = Lists.newArrayList();
if (arguments!=null && arguments.length>0){
for (int i=0;i<arguments.length;i++){
Object item = arguments[i];
if (!(item instanceof ServletRequest) && !(item instanceof ServletResponse)
&& !(item instanceof HttpSession) && !(item instanceof BindingResult)
&& !(item instanceof MultipartFile)){
argumentList.add(arguments[i]);
}
}
}
//json转字符串
String params = JSON.toJSONString(argumentList);
logEntity.setParams(params);
sysLogService.sysLog(logEntity);
}
}
7测试
我们之前已经写好了一个简单的获取信息的代码,我们来测试一下
package com.example.demo2.controller;
import com.example.demo2.aspect.SysLog;
import com.example.demo2.entity.User;
import com.example.demo2.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description
* @Author
* @Date 2019/10/18 10:43
* @Version
*/
@RestController
@RequestMapping("/demo")
public class UserController {
@Autowired
private UserService userService;
@SysLog(value = "测试")
@GetMapping(value = "get")
public User getInformation(String userName,String passWord){
return userService.getInformation(userName,passWord);
}
}
这个接口具体的service层和dao层代码我就不贴在这里了,大家可以在源码里查看。
启动项目,启动postman进行调接口
大家可以看到我们的logEntity已经打印在控制台上面了。
这只是我写的一个很简单的demo,切面service层我们也没有具体实现。如果觉得有什么好的建议,欢迎提出来。
源码地址:https://github.com/yjzxianyu/aop