解释
AOP通常意思是: 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
常用的业务环境: 配置事务、日志、权限验证、用户请求时做一些特殊处理
注解解释:
-
@Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)(元注解);
取值(ElementType)有:名 作用 CONSTRUCTOR 用于描述构造器 FIELD 用于描述域 LOCAL_VARIABLE 用于描述局部变量 METHOD 用于描述方法 PACKAGE 用于描述包 PARAMETER 用于描述参数 TYPE 用于描述类、接口(包括注解类型) 或enum声明 -
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)(元注解)
取值(RetentionPoicy)有:键名 作用 SOURCE 在源文件中有效(即源文件保留) CLASS 在class文件中有效(即class保留) RUNTIME 在运行时有效(即运行时保留) **注意:**RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理
-
@Aspect:作用是把当前类标识为一个切面供容器读取
-
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
-
@Around:环绕增强,相当于MethodInterceptor
-
@AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
-
@Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
-
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice
-
@Component 注解 把切面类加入到IOC容器中
-
@Aspect 注解 使之成为切面类
代码实现切面用户操作Log日志
1. 日志实体类和service
实体:采用lombok的方式
@Data
public class SysLog implements Serializable {
private static final long serialVersionUID = -6309732882044872298L;
@TableId(value="id", type = IdType.ID_WORKER)
private Integer id;
private String userName;
private String operation;
private Integer time;
private String method;
private String params;
private String ip;
private Date createTime;
}
service接口和实现类(dao层再次就不贴出来了,该项目使用了mybatisplus非常简单):
//接口
public interface ISysLogService extends IService<SysLog> {
}
//实现类
@Service
public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> implements ISysLogService {
}
2. 定义一个方法级别的Log
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "";
}
3. 声名切面,完成日志保存
@Aspect
@Component
public class LogAspect {
@Autowired
ISysLogService iSysLogService;
@Pointcut("@annotation(com.zhongsy.study.common.Log)")
public void pointcut() { }
@Around("pointcut()")
public Object around(ProceedingJoinPoint point) {
Object result = null;
long beginTime = System.currentTimeMillis();
try {
// 执行方法
result = point.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
// 执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
// 保存日志
saveLog(point, time);
return result;
}
private void saveLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLog sysLog = new SysLog();
Log logAnnotation = method.getAnnotation(Log.class);
if (logAnnotation != null) {
// 注解上的描述
sysLog.setOperation(logAnnotation.value());
}
// 请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
// 请求的方法参数值
Object[] args = joinPoint.getArgs();
// 请求的方法参数名称
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] paramNames = u.getParameterNames(method);
if (args != null && paramNames != null) {
String params = "";
for (int i = 0; i < args.length; i++) {
params += " " + paramNames[i] + ": " + args[i];
}
sysLog.setParams(params);
}
// 获取request
// HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
// 设置IP地址
// sysLog.setIp(IPUtils.getIpAddr(request));
sysLog.setIp("127.0.0.1");
// 模拟一个用户名
sysLog.setUserName("mrbird");
sysLog.setTime((int) time);
sysLog.setCreateTime(new Date());
// 保存系统日志
iSysLogService.save(sysLog);
}
}
注意: 因为我这里是测试,因此获取ip的方法我没有编写,具体采用的时候需要具体的编写。