spring Aop以及自定义注解记录操作日志

日志管理

1.自定义日志输出

在resources目录下新建log4j.properties文件

自定义配置控制台日志输出格式,以及配置日志输出到本地文件

# 定义输出DEBUG级别以上的日志,自定义console控制台输出,file输出到文件
log4j.rootLogger=debug,console,file

# 指定输出目的地console为输出到控制台
log4j.appender.console=org.apache.log4j.ConsoleAppender
# 指定console的日志消息的输出最低层次
log4j.appender.console.Threshold=DEBUG
# 指定console输出控制台,默认情况下是:System.out
log4j.appender.console.Target=System.out
# 指定console的布局
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# 指定console的布局的输出格式
log4j.appender.console.layout.ConversionPattern=[%5p] %d{yyyy-MM-dd HH:mm:ss} - %m%n

# 指定输出file的方式为输出到文件
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
# 指定INFO的输出日志路径
log4j.appender.file.File=E:/log/info.log
# 设置每天生成日志文件
log4j.appender.file.DatePattern='_'yyyy-MM-dd'.log '
# 追加 设置true则在原来的log文件中追加, false覆盖原来的log文件
log4j.appender.file.Append = true
log4j.appender.file.Threshold = INFO
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%5p]  %d{yyyy-MM-dd HH:mm:ss} - %m%n
log4j.appender.file.encoding=UTF-8

2.使用spring Aop记录日志

简介:面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足。同时AOP框架也是Spring的一个关键的组件。

2.1 关于Spring AOP的术语

切面(Aspect):在Spring AOP中,切面可以使用通用类或者在普通类中以@Aspect 注解(@AspectJ风格)来实现

连接点(Joinpoint):在Spring AOP中一个连接点代表一个方法的执行

通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括"around"、"before”和"after"等通知。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链

切入点(Pointcut):定义出一个或一组方法,当执行这些方法时可产生通知,Spring缺省使用AspectJ切入点语法。

2.2 通知类型

前置通知(@Before):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)

返回后通知(@AfterReturning):在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回

抛出异常后通知(@AfterThrowing):方法抛出异常退出时执行的通知

后通知(@After):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)

环绕通知(@Around):包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型,环绕通知可以在方法调用前后完成自定义的行为,它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行

2.3 开启AOP拦截

在Springs的配置文件中添加:

<!-- 开启AOP拦截 -->
    <aop:aspectj-autoproxy/>
2.4 包路径作为切点记录日志

首先新建一个LogAspect.class文件

定义切点为

@Pointcut("execution(* cn.edu.zzti.cs.controller...(..))")

拦截 cn.edu.zzti.cs.controlle包以及子包路径下的所有方法

package cn.edu.zzti.cs.log;

import cn.edu.zzti.cs.entity.student.Student;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;


@Aspect
@Component
public class LogAspect {
    private Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Autowired
    private HttpServletRequest request;

    private String userNum;

    private String userName;

    private String startTime;

    private Date beginTime;

    private String durations;

    private String methodName;

    private String param;

    private String returnValues;

    private String remoteAddr;

    private String requestUri;

    private String exceptions;


    /**
     * @Description: 定义一个切点
     */
    @Pointcut("execution(* cn.edu.zzti.cs.controller..*.*(..))")
    public void SystemLog(){}

    @Before("SystemLog()")
    public void SysBefore(JoinPoint joinPoint)throws Exception{

        //获取请求的方法名
        methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();

        //获取请求的参数
        param= Arrays.toString(joinPoint.getArgs());

        //格式化开始时间
        beginTime=new Date();
        startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(beginTime);

        //获取请求来源的IP
        String ip=request.getRemoteAddr();
        remoteAddr = ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;

        //获取请求的Url地址
        requestUri=request.getRequestURI();

    }

    @Around("SystemLog()")
    public Object aroundExec(ProceedingJoinPoint joinPoint) throws Throwable {
        Object ret = joinPoint.proceed();
        //从session中获取用户账号和姓名
        Student student=(Student) request.getSession().getAttribute("user");
        if(student!=null){
            userNum=student.getStuNum();
            userName = student.getStuName();
        }
        return ret;
    }

    /**
     *@description 方法抛出异常后执行 记录异常
     **/
    @AfterThrowing(pointcut = "SystemLog()", throwing = "e")
    public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        //记录方法抛出的异常
        exceptions=e.toString();
        //记录用时
        long endTime= System.currentTimeMillis();
        long number=endTime - beginTime.getTime();
        durations=String.valueOf(number)+"ms";

        //控制台打印
        errorPrint();
    }

    /**
     * @Description: 方法正常调用后触发   记录方法返回值
     */
    @AfterReturning(returning = "ret", pointcut = "SystemLog()")
    public void SysAfterReturning(Object ret){
        //记录用时
        long endTime= System.currentTimeMillis();
        long number=endTime - beginTime.getTime();
        durations=String.valueOf(number)+"ms";

        //获取方法返回值
        if(ret !=null){
            returnValues=ret.toString();
        }

        //控制台打印
        infoPrint();
    }

    private void infoPrint(){
        // 记录请求内容
        logger.info("\n"+
                "-------------------------------------------------------------------------------------------------------------------"+"\n"+
                "BEGIN_TIME(开始时间) : "+startTime+"\n"+
                "DURATIONS(用时) : "+durations+"\n"+
                "USER_NUM(用户账号) : "+userNum+"\n"+
                "USER_NAME(用户姓名) : "+userName+"\n"+
                "URL(请求地址) : " + requestUri+"\n"+
                "HTTP_METHOD(请求方式): " + request.getMethod()+"\n"+
                "IP(IP地址) : " + remoteAddr+"\n"+
                "CLASS_METHOD(处理请求的方法) : " + methodName +"\n"+
                "ARGS(参数) : " + param+"\n"+
                "RESPONSE(返回值) : "+returnValues+"\n"+
                "-------------------------------------------------------------------------------------------------------------------");
    }

    private void errorPrint(){
        // 记录请求内容
        logger.error("\n"+
                "-------------------------------------------------------------------------------------------------------------------"+"\n"+
                "BEGIN_TIME(开始时间) : "+startTime+"\n"+
                "DURATIONS(用时) : "+durations+"\n"+
                "USER_NUM(用户账号) : "+userNum+"\n"+
                "USER_NAME(用户姓名) : "+userName+"\n"+
                "URL(请求地址) : " + requestUri+"\n"+
                "HTTP_METHOD(请求方式): " + request.getMethod()+"\n"+
                "IP(IP地址) : " + remoteAddr+"\n"+
                "CLASS_METHOD(处理请求的方法) : " + methodName +"\n"+
                "ARGS(参数) : " + param+"\n"+
                "RESPONSE(返回值) : "+returnValues+"\n"+
                "EXCEPTIONS(异常) : "+exceptions+"\n"+
                "-------------------------------------------------------------------------------------------------------------------");
    }
}

在spring配置文件中定义一个实例,将bean注入到spring容器中

<bean id = "logAspect" class="cn.edu.zzti.cs.log.LogAspect"/>

在这里插入图片描述
运行项目发起登录请求即可看到控制台打印出日志:

在这里插入图片描述
本地文件中记录日志内容如下:

在这里插入图片描述

2.5 自定注解作为切点记录日志

创建自定义注解@LogRecord

package cn.edu.zzti.cs.log;

import java.lang.annotation.*;

/**
 *@description 自定义注解记录日志
 **/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogRecord {
    String description() default "";//记录方法描述
}

新建一个AnnoAspect.java文件

定义切点为

@Pointcut("@annotation(cn.edu.zzti.cs.log.LogRecord)")

拦截所有标注@LogRecord注解的方法

package cn.edu.zzti.cs.log;

import cn.edu.zzti.cs.entity.student.Student;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
 *@description: 使用自定义注解方式记录日志
 **/
@Aspect
@Component
public class AnnoAspect {
    private Logger logger = LoggerFactory.getLogger(AnnoAspect.class);

    @Autowired
    private HttpServletRequest request;

    private String userNum;

    private String userName;

    private String startTime;

    private Date beginTime;

    private String durations;

    private String methodName;

    private String param;

    private String returnValues;

    private String remoteAddr;

    private String requestUri;

    private String exceptions = "无";

    private String logTitle;

    /**
     * @Description: 定义一个切点
     */
    @Pointcut("@annotation(cn.edu.zzti.cs.log.LogRecord)")
    public void AnnoLog(){}

    /**
     * @Description: 方法调用前触发
     */
    @Before("AnnoLog()")
    public void doBefore(JoinPoint joinPoint){
        //获取请求的方法名
        methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();

        //获取请求的参数
        param= Arrays.toString(joinPoint.getArgs());

        //格式化开始时间
        beginTime=new Date();
        startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(beginTime);

        //获取请求的Url地址
        requestUri=request.getRequestURI();

        //获取请求来源的IP
        String ip=request.getRemoteAddr();
        remoteAddr = ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
    }


    @Around("AnnoLog()")
    public Object aroundExec(ProceedingJoinPoint joinPoint) throws Throwable {
        Object ret = joinPoint.proceed();
        Student student=(Student) request.getSession().getAttribute("user");
        if(student!=null){
            userNum=student.getStuNum();
            userName = student.getStuName();
        }
        return ret;
    }

    /**
     *@description 方法执行后调用(不区分成功或异常)
     **/
    @After(value = "@annotation(logRecord)",argNames ="logRecord")
    public void after(LogRecord logRecord){
        //获取方法的自定义描述
        logTitle=logRecord.description();
        //记录用时
        long endTime= System.currentTimeMillis();
        long number=endTime - beginTime.getTime();
        durations=String.valueOf(number)+"ms";
    }

    /**
     *@description 方法抛出异常后执行 记录异常
     **/
    @AfterThrowing(pointcut = "AnnoLog()", throwing = "e")
    public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        e.printStackTrace();
        exceptions=e.toString();
        errorPrint();
    }

    /**
     * @Description: 方法正常调用后触发   记录方法返回值
     */
    @AfterReturning(returning = "ret", pointcut = "AnnoLog()")
    public void doAfterReturning(JoinPoint joinPoint,Object ret){
        //获取方法返回值
        if(ret !=null){
            returnValues=ret.toString();
        }
        infoPrint();
    }


    private void infoPrint(){
        // 记录下请求内容
        logger.info("\n"+
                "-------------------------------------------------------------------------------------------------------------------"+"\n"+
                "LOG_TITLE(日志类型) :"+logTitle+"\n"+
                "BEGIN_TIME(开始时间) : "+startTime+"\n"+
                "DURATIONS(用时) : "+durations+"\n"+
                "USER_NUM(用户账号) : "+userNum+"\n"+
                "USER_NAME(用户姓名) : "+userName+"\n"+
                "URL(请求地址) : " + requestUri+"\n"+
                "HTTP_METHOD(请求方式): " + request.getMethod()+"\n"+
                "IP(IP地址) : " + remoteAddr+"\n"+
                "CLASS_METHOD(处理请求的方法) : " + methodName +"\n"+
                "ARGS(参数) : " + param+"\n"+
                "RESPONSE(返回值) : "+returnValues+"\n"+
                "-------------------------------------------------------------------------------------------------------------------");
    }

    private void errorPrint(){
        // 记录下请求内容
        logger.error("\n"+
                "-------------------------------------------------------------------------------------------------------------------"+"\n"+
                "LOG_TITLE(日志类型) :"+logTitle+"\n"+
                "BEGIN_TIME(开始时间) : "+startTime+"\n"+
                "DURATIONS(用时) : "+durations+"\n"+
                "USER_NUM(用户账号) : "+userNum+"\n"+
                "USER_NAME(用户姓名) : "+userName+"\n"+
                "URL(请求地址) : " + requestUri+"\n"+
                "HTTP_METHOD(请求方式): " + request.getMethod()+"\n"+
                "IP(IP地址) : " + remoteAddr+"\n"+
                "CLASS_METHOD(处理请求的方法) : " + methodName +"\n"+
                "ARGS(参数) : " + param+"\n"+
                "RESPONSE(返回值) : "+returnValues+"\n"+
                "EXCEPTIONS(异常) : "+exceptions+"\n"+
                "-------------------------------------------------------------------------------------------------------------------");
    }
}

在处理登录请求的方法上添加@LogRecord注解

在这里插入图片描述
在spring配置文件中在定义一个实例,将bean注入到spring容器中

<bean id = "annoAspect" class="cn.edu.zzti.cs.log.AnnoAspect"/>

在这里插入图片描述
运行项目发起登录请求即可看到控制台打印出日志:

在这里插入图片描述
本地文件中

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用Spring AOP来通过自定义注解来实现切入点。首先,定义一个自定义注解,并在需要切入的方法上使用该注解,然后定义一个切面,并在切面中使用@Before 或 @Around 注解来拦截被标记的方法,最后,在Spring的配置文件中声明所需的切面,此时,所有被标记的方法都会被拦截。 ### 回答2: 使用Spring AOP切入自定义注解有以下几个步骤: 1. 在项目中引入Spring AOP的依赖,一般为spring-aopspring-aspects。 2. 在配置文件中开启Spring AOP的自动代理功能。可以通过在XML配置文件中添加<aop:aspectj-autoproxy />或在Java配置文件中添加@EnableAspectJAutoProxy注解实现。 3. 创建一个切面类,用于定义切入的逻辑。这个类需要使用@Component或者其他Spring注解进行标识,以便Spring能够扫描到。 4. 在切面类的方法中,使用@Before、@After等注解定义切入点和具体的切入操作。例如,使用@Before注解定义在某个注解标记的方法执行之前切入的逻辑。 5. 在注解中定义自定义的切点。可以使用@Retention和@Target等元注解来配置注解的生命周期和使用范围。 6. 在目标类或方法上添加自定义的注解。例如,在一个Service类的某个方法上添加自定义注解。 7. 运行项目,Spring会根据配置自动代理目标类,当目标类或方法被调用时,切面类中定义的切入逻辑就会自动被执行。 8. 可以通过配置切入的顺序、通知的类型等来进一步细化切入的逻辑。 通过以上步骤,我们可以使用Spring AOP方便地切入自定义注解,实现对目标类或方法的增强、日志记录、权限控制等功能。 ### 回答3: 使用Spring AOP切入自定义注解需要以下几个步骤: 1. 定义一个自定义的注解。可以使用Java提供的`@interface`关键字创建一个注解,例如: ```java @Target(ElementType.METHOD) // 定义注解的作用范围为方法 @Retention(RetentionPolicy.RUNTIME) // 注解在运行时可见 public @interface CustomAnnotation { // 自定义注解的属性 } ``` 2. 创建一个切面类来处理注解。可以使用Spring提供的`@Aspect`注解来标记切面类,并在方法上使用`@Before`、`@After`等注解来定义切入点和增强逻辑。例如: ```java @Aspect @Component public class CustomAspect { @Before("@annotation(customAnnotation)") // 拦截带有CustomAnnotation注解的方法 public void beforeMethod(CustomAnnotation customAnnotation) { // 在方法执行前执行的逻辑 } } ``` 3. 配置Spring AOP。在Spring配置文件中添加AOP的配置,例如使用`<aop:aspectj-autoproxy>`标签开启自动代理,并指定切面类的包名,让Spring能够自动扫描并应用切面逻辑。 4. 在目标方法上使用自定义注解。在需要切入的方法上标记使用自定义注解,例如: ```java @CustomAnnotation public void doSomething() { // 方法的实际逻辑 } ``` 这样,在调用`doSomething()`方法时,Spring AOP会拦截到带有`@CustomAnnotation`注解的方法,并执行切面逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值