完成aop的步骤:
1.引入spring-aspect依赖
<!--切面依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.15.RELEASE</version>
</dependency>
2.创建切面类@Aspect
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component//此注解的作用是将该类交给spring容器管理
@Aspect//此注解的作用是将该类定义为切面类
public class LogAspect {
}
3.切面类中定义切点@Pointcut("execution(路径表达式)")也可以用@Pointcut("annotation(注解的路径)")
/**
* 第一个*:表示任意访问修饰符和返回类型
* 第二个*:表示该包下的任意类
* 第三个*:表示任意方法
* ..:表示任意参数任意类型
*/
//此注解的作用是在切面类中定义切点,切入点
@Pointcut("execution(* com.cjj.service.*.*(..))")
public void add(){}
4.切面类定义通知何时处理:@Before("切入点方法名()")
//此注解的作用是在切面类中定义该方法在方法执行前执行
@Before("add()")
public void beforeMethod(JoinPoint joinPoint){//JoinPoint:连接点,表示被切入的方法对象,封装了很多方法的内容
Signature signature = joinPoint.getSignature();//获取被切入的方法对象
String name = signature.getName(); //获取方法名称
Object[] args = joinPoint.getArgs(); //获取方法的参数
System.out.println(name+"方法被调用~~~~~~~~~~~~~"+"参数为"+ Arrays.asList(args));
}
//此注解的作用是在切面类中定义该方法在方法执行后执行,无论有没有异常都会执行
@After("add()")
public void afterMethod(JoinPoint joinPoint){
System.out.println("前置方法被调用~~~~~~~~~~~~~");
}
//此注解的作用是在切面类中定义该方法在触发返回值后执行
@AfterReturning(value = "add()",returning = "r")
public void afterReturning(Object r){
System.out.println(r);
System.out.println("前置方法被调用~~~~~~~~~~~~~");
}
@AfterThrowing("add()")
public void afterThrowing(){
System.out.println("前置方法被调用~~~~~~~~~~~~~");
}
5.在spring配置文件中,开启切面注解驱动,<aop:aspectj-autoproxy/>
<!--开启切面注解的驱动-->
<aop:aspectj-autoproxy/>
下面为实战案例:
因为工程已经引入过依赖,所以这里的实战案例直接从创建切面类开始,而且这个实战案例使用的是自定义注解的方式完成aop日志封装到数据库中。
1.在spring配置文件中开启切面注解驱动
<!--开启切面注解-->
<aop:aspectj-autoproxy/>
2.自定义注解类
package com.aaa.aspect;
import java.lang.annotation.*;
@Target({ElementType.METHOD,ElementType.TYPE}) //表示该注解只能使用在方法上
@Retention(RetentionPolicy.RUNTIME) //该注解在什么时候有效 Source源码时有效 ClASS:字节码时有效 RUNTIME运行时有效【这个】
@Documented
public @interface MyAnnotation {
String value() default ""; //
}
3.将自定义注解加在需要捕获日志的类上,并且设置aop的切入点为注解类型
@RequestMapping("/updateNameAndTel")
@MyAnnotation(value = "修改姓名和电话")
public Result updateNameAndTel(Integer id,@RequestBody LoginVo loginVo) {
int row = doctorService.updateNameAndTel(id,loginVo);
if (row == 0) {
return new Result(500, "失败", null);
}
return new Result(200, "修改姓名和电话成功", null);
}
4.使用环绕处理捕获日志
package com.aaa.aspect;
import com.aaa.dao.LogaopDao;
import com.aaa.dao.ManagerDao;
import com.aaa.pojo.Doctor;
import com.aaa.pojo.Logaop;
import com.aaa.utils.WebUtil;
import com.aaa.vo.Result;
import com.aliyun.oss.model.JsonFormat;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@Component
@Aspect
public class MyAspect {
@Autowired
private ManagerDao managerDao;
@Autowired
private LogaopDao logaopDao;
//定义切入点,使用注解切入点
@Pointcut("@annotation(com.aaa.aspect.MyAnnotation)")
private void log(){}
@Around("log()")//@Around里面包含了Before After AfterReturning @AfterThrowing所有的处理时机
public Object Mylog(ProceedingJoinPoint joinPoint){//ProceedingJoinPoint是joinPoint的子类,拥有更多的方法
//日志内容需要eid ,oname, mname,这三个都可以直接从session中获取
// time ,可以使用new Date() 获取当前时间
// addr , 可以使用request 的getRemoteAddr获取到ip地址
// result返回结果成功失败,
// name 进行的操作名称,也就是自定义注解的value
// data 操作传入的数据,也就是接口需要的数据
HttpServletRequest request = WebUtil.getRequest();
//ip地址
String remoteAddr = request.getRemoteAddr();
System.out.println("修改姓名和电话的ip地址为:"+remoteAddr);
//eid
HttpSession session = WebUtil.getSession();
Doctor doctor = (Doctor) session.getAttribute("user");
Integer o_id = doctor.getO_id();
System.out.println("o_id=======:"+o_id);
//oname
String o_name = doctor.getO_name();
System.out.println("oname=======:"+o_name);
//mname
Integer m_id = doctor.getM_id();
String mname = managerDao.selectNameById(m_id);
System.out.println("mname========"+mname);
//当前时间
Date date = new Date();
System.out.println("当前时间是:"+date);
try {
Result result = (Result) joinPoint.proceed();//调用切点的方法 todo 当代码运行到这里的时候,会跳转到切入点执行该方法
String msg = result.getMsg(); //拿到方法执行后的msg状态,todo 成功/失败
System.out.println("msg========="+msg);
//这个 Signature 对象被强制转换为 MethodSignature 类型。
// MethodSignature 是 Signature 的子接口,它提供了更多的方法来访问连接点方法的信息,例如获取方法的参数类型和返回类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//拿到方法上的注解
MyAnnotation annotation = signature.getMethod().getAnnotation(MyAnnotation.class);
//拿到注解的值
String value = annotation.value();
System.out.println("进行的操作:"+value);
//拿到方法执行时传入的参数
Object[] args = joinPoint.getArgs();
//将参数转化为list集合的格式,然后转化为string类型
String data = Arrays.asList(args).toString();
System.out.println("传入的参数为:"+data);
//将日志参数赋给Logaop类,之后添加到数据库中
Logaop logaop = new Logaop(null,o_id, o_name, mname, date, remoteAddr, msg, value, data);
int i = logaopDao.addLog(logaop);
System.out.println("日志添加之后执行,添加了"+i+"行日志");
return result;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
}
以上代码是在切面类运行完成并且没有异常的时候才会调用日志类dao层的添加功能。并且在添加日志之前,使用ProceedingJoinPoint的方法获取到需要的方法名,参数值等等日志数据库需要的参数。之后封装到logaop类中,调用日志表的添加功能。