spring--aop---->添加日志

265 阅读4分钟

image.png

完成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类中,调用日志表的添加功能。