aop的应用使用aop完成日志的记录

 

前面已经介绍过aop的多种应用场景,今天研究了一下使用aop记录日志,使用的是后置通知。具体的实现如下:

自定义注解,用来记录用户的操作和一些基本信息

package com.soecode.lyf.log.annatation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Create by wws on 2019/6/3
 */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface LogRequire {

    /**
     * 操作模块
     */
     String operationModel() default "";
    /**
     * 操作功能
     */
     String operationFunction()default "";

    /**
     * 操作说明
     */
     String operationExplain ()default "";


}

增加一个获取参数里面的用户信息注解,注意这个值一般是从session里面获取的,从controller里面传过来,这里为了简单从页面传来一个假的参数。

package com.soecode.lyf.log.annatation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Description
 * @Author DJZ-WWS
 * @Date 2019/6/4 8:36
 */
@Target({ ElementType.TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public  @interface LogUser {

}

日志的切面实现

package com.soecode.lyf.log.aspect;

import com.mchange.v1.util.ArrayUtils;
import com.soecode.lyf.datasource.DataSourceAspect;
import com.soecode.lyf.log.annatation.LogRequire;
import com.soecode.lyf.log.annatation.LogUser;
import com.soecode.lyf.log.pojo.LogInfo;
import com.soecode.lyf.log.service.LogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.text.MessageFormat;
import java.util.Date;
import java.util.Enumeration;


/**
 * @Description 日志切面
 * @Author DJZ-WWS
 * @Date 2019/6/3 14:04
 */

@Aspect
@Component
public class SysLogAspect {
    static Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);
    @Autowired
    private LogService logService;

    @Pointcut("@annotation(com.soecode.lyf.log.annatation.LogRequire)")
    public void log() {
    }

    @After("log()")
    public void doAfter(JoinPoint joinPoint) throws UnknownHostException {
        System.out.println("=====SysLogAspect后置通知开始=====,开始记录日志信息");
        LogInfo logInfo = new LogInfo();
        //记录基本信息
        Class target = joinPoint.getTarget().getClass();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Parameter[] parameters = method.getParameters();
        //记录用户信息  获取参数里面加LogUser注解的值,表示用户信息
        //获取参数上所有的注解
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        //判断注解LogUser是否存在

        for (Annotation[] paraeterAnnotation : parameterAnnotations) {
            //获取注解的索引
            int paramIndex = ArrayUtils.indexOf(parameterAnnotations, paraeterAnnotation);
            //获取注解标记的值

            for (Annotation annotation : paraeterAnnotation) {
                if (annotation instanceof LogUser) {
                    Object[] args = joinPoint.getArgs();
                    Object value = args[paramIndex];
                    logInfo.setAccountInfo(value.toString());
                }
            }
        }
        //获取注解上的值
        LogRequire logRequire = null;
        logRequire = this.getLogRequire(target, method);
        //从接口初始化
        if (logRequire == null) {
            for (Class clazz : target.getInterfaces()) {
                logRequire = getLogRequire(clazz, method);
                if (logRequire != null) {
                    break;//从某个接口中一旦发现注解,不再循环
                }
            }
        }

        if (logRequire != null && (!StringUtils.isEmpty(logRequire.operationExplain())) && (!StringUtils.isEmpty(logRequire.operationFunction())) && (!StringUtils.isEmpty(logRequire.operationModel()))) {
            ////调用日志处理类去处理我们的日志
            logInfo.setOperationModel(logRequire.operationModel());
            logInfo.setOperationFunction(logRequire.operationFunction());
            logInfo.setOperationExplain(logRequire.operationExplain());
            logInfo.setIp(this.getIp());
            logInfo.setOperationTime(new Date());
            logService.saveLog(logInfo);
        }


    }

    /**
     * 获取方法或类的注解对象DataSource
     *
     * @param target
     * @param method
     * @return
     */
    private LogRequire getLogRequire(Class target, Method method) {
        try {
            //1.优先方法注解
            Class[] types = method.getParameterTypes();
            Method m = target.getMethod(method.getName(), types);
            if (m != null && m.isAnnotationPresent(LogRequire.class)) {
                return m.getAnnotation(LogRequire.class);
            }
            //2.其次类注解
            if (target.isAnnotationPresent(LogRequire.class)) {
                return target.getAnnotation(LogRequire.class);
            }

        } catch (Exception e) {
            e.printStackTrace();
            logger.error(MessageFormat.format("通过注解切换数据源时发生异常[class={0},method={1}]:"
                    , target.getName(), method.getName()), e);
        }
        return null;
    }


    private String getIp() throws UnknownHostException {
        try {
            InetAddress candidateAddress = null;
            // 遍历所有的网络接口
            for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); ) {
                NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
                // 在所有的接口下再遍历IP
                for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) {
                    InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();
                    if (!inetAddr.isLoopbackAddress()) {// 排除loopback类型地址
                        if (inetAddr.isSiteLocalAddress()) {
                            // 如果是site-local地址,就是它了
                            return inetAddr.getHostAddress();
                        } else if (candidateAddress == null) {
                            // site-local类型的地址未被发现,先记录候选地址
                            candidateAddress = inetAddr;
                        }
                    }
                }
            }
            if (candidateAddress != null) {
                return candidateAddress.getHostAddress();
            }
            // 如果没有发现 non-loopback地址.只能用最次选的方案
            InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
            if (jdkSuppliedAddress == null) {
                throw new UnknownHostException("The JDK InetAddress.getLocalHost() method unexpectedly returned null.");
            }
            return jdkSuppliedAddress.getHostAddress();
        } catch (Exception e) {
            UnknownHostException unknownHostException = new UnknownHostException(
                    "Failed to determine LAN address: " + e);
            unknownHostException.initCause(e);
            throw unknownHostException;
        }
    }

}

业务层的应用:

package com.soecode.lyf.bookuserservice.service.impl;

import com.soecode.lyf.bookuserservice.dao.BookDao;
import com.soecode.lyf.bookuserservice.pojo.Book;
import com.soecode.lyf.bookuserservice.service.BookService;
import com.soecode.lyf.datasource.DataSource;
import com.soecode.lyf.log.annatation.LogRequire;
import com.soecode.lyf.log.annatation.LogUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;


@Service
public class BookServiceImpl implements BookService {

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	// 注入Service依赖
	@Autowired
	private BookDao bookDao;



	@Override
	@DataSource("dataSource1")
	@LogRequire(operationModel = "book",operationFunction = "根据书的id查询book",operationExplain = "查询操作")
	public Book getById(long bookId,@LogUser String  userName) {
		return bookDao.queryById(bookId);
	}

	@Override
	@DataSource("dataSource1")
	@LogRequire(operationModel = "books",operationFunction = "查询所有的书",operationExplain = "查询所有的书")
	public List getList() {
		return bookDao.queryAll(0, 1000);
	}




}

日志基本信息bean

package com.soecode.lyf.log.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * @Description
 * @Author DJZ-WWS
 * @Date 2019/6/3 13:47
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LogInfo {

   
    /**
     * 操作模块
     */
    private String operationModel;
    /**
     * 操作功能
     */
    private String operationFunction;

    /**
     * 操作说明
     */
    private String operationExplain;
    /**
     * 账号信息
     */

    private String accountInfo;
    /**
     * ip归属地
     */
    private String ip;

    /**
     * 操作时间
     */

    private Date operationTime;

}

记录日志的服务

package com.soecode.lyf.log.service.impl;

import com.soecode.lyf.log.pojo.LogInfo;
import com.soecode.lyf.log.service.LogService;
import org.springframework.stereotype.Service;

/**
 * @Description
 * @Author DJZ-WWS
 * @Date 2019/6/3 14:09
 */

@Service
public class LogServiceImpl  implements LogService {
    @Override
    public void saveLog(LogInfo logInfo) {
        System.out.println("执行保存操作,保存的日志信息为"+logInfo.toString());
    }
}


业务层注解的应用

日志结构图

aop的应用使用aop完成日志的记录_第1张图片

这样就实现了aop对日志的操作。

结果如下:

 

   这里的没用真正的实现保存,伪保存。

   日志一般我们不会记录在数据库,数据量大的时候数据库压力大,可以采用mongodb记录日志。

   另外记录日志可以采用异步的方式实现日志的记录。

关于异步可以采用MQ或者使用线程的方式实现日志的记录。

你可能感兴趣的:(Spring)