Java实现AOP切面记录日志实例

注:一个可以直接拿去用的aop切面保存系统操作日志的实例,应用框架为SpringMVC。

需求描述:
管理员要在系统中能看到每个账户的操作记录、时间、账户名称、ip。

实现步骤:
1、实现自定义注解MethodLog
2、建日志实体Bean
3、实现监听注解,调用切面实现类
4、配置启动对@AspectJ注解的支持及监听类
5、业务Controller引用

具体实现:
1、建一个接口类,内容如下

package com.xx.xxx.utils;

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

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodLog {
    String remark() default "";  // 自定义的操作描述属性
    String operType() default "0";  //  自定义的操作类型
}

2、建日志实体Bean

package com.xx.xxx.entity;

public class Syslog {

    private String ipAddress;
    private String loginName;
    private String methodName;
    private String methodRemark;
    private String operation;
    private String operationDate;

    public String getIpAddress() {
        return ipAddress;
    }

    public void setIpAddress(String ipAddress) {
        this.ipAddress = ipAddress;
    }

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public String getMethodRemark() {
        return methodRemark;
    }

    public void setMethodRemark(String methodRemark) {
        this.methodRemark = methodRemark;
    }

    public String getOperation() {
        return operation;
    }

    public void setOperation(String operation) {
        this.operation = operation;
    }

    public String getOperationDate() {
        return operationDate;
    }

    public void setOperationDate(String operationDate) {
        this.operationDate = operationDate;
    }
}

3、实现监听注解,调用切面实现类

package com.xx.xxx.utils;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import javax.servlet.http.HttpServletRequest;

import com.xx.xxx.dao.ManageDao;
import com.xx.xxx.entity.Syslog;
import com.xx.xxx.entity.UsersT;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Component
@Aspect
public class LogService {

    @Autowired
    private ManageDao manageDao;  // 保存日志表DAO

    public LogService() {
        System.out.println("Aop");
    }

    /**
     * 自定义注解装饰
     * @throws Throwable
     */
    @Pointcut("@annotation(com.xx.xxx.utils.MethodLog)")
    public void methodCachePointcut() {}

    /**
     * 方法执行的前后调用
     * @param point
     * @return
     * @throws Throwable
     */
     // 此注解为:调用前和后都执行,如果只调用前或后,用@Before或@After
    @Around("methodCachePointcut()")  
    public Object around(ProceedingJoinPoint point) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes()).getRequest();
                // 获取日期,格式为:2020-01-01 23:23:23 星期四
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E"); 
        Calendar ca = Calendar.getInstance();
        String operDate = df.format(ca.getTime());
        String ip = getIpAddr(request);
        ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
        // 获取存在缓存中的用户Bean
        UsersT user = (UsersT) SecurityUtils.getSubject().getSession().getAttribute("userVO");
        String loginName;
        // 初次登录没有用户Bean,则获取用户登录账号作为账号名称,如果业务中必须要名称就拿登录账号去库里查一遍
        if (user != null) {
            loginName = user.getUserName();
        } else {
            loginName = request.getParameter("userAccount");
        }
		// 获取自定义注解描述
        String monthRemark = getMthodRemark(point);
        // 获取方法名
        String monthName = point.getSignature().getName();
        // 获取类包
        String packages = point.getThis().getClass().getName();
        if (packages.indexOf("$$EnhancerBySpringCGLIB$$") > -1) {
            try {
                // 去掉CGLIB动态生成的类
                packages = packages.substring(0, packages.indexOf("$$"));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        Object[] method_param = null;
        Object object;
        try {
            method_param = point.getArgs(); //获取方法参数
            //String param=(String) point.proceed(point.getArgs());
			// 保存日志到数据库日志表
            Syslog sysLog = new Syslog();
            sysLog.setIpAddress(ip);
            sysLog.setLoginName(loginName);
            sysLog.setMethodName(packages + "." + monthName);
            sysLog.setMethodRemark(monthRemark);
            if(StringUtils.isNotBlank(monthRemark)){
                sysLog.setOperation(monthRemark);
            }
            //sysLog.setOperationDate(operDate);
			// 保存日志表(调用Mybatis和DAO不做细解了)
            manageDao.saveOptionLog(sysLog);
			// 请求对象属性及值
            object = point.proceed();
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        return object;
    }

    /**
     * 方法运行出现异常时调用(暂时用不到)
     * @param ex
     */
    public void afterThrowing(Exception ex) {
        System.out.println("afterThrowing");
        System.out.println(ex);
    }

    /**
     * 获取自定义注解描述
     * @param joinPoint
     * @return
     * @throws Exception
     */
    public static String getMthodRemark(ProceedingJoinPoint joinPoint)
            throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();

        Class targetClass = Class.forName(targetName);
        Method[] method = targetClass.getMethods();
        String methode = "";
        for (Method m : method) {
            if (m.getName().equals(methodName)) {
                Class[] tmpCs = m.getParameterTypes();
                if (tmpCs.length == arguments.length) {
                    MethodLog methodCache = m.getAnnotation(MethodLog.class);
                    if (methodCache != null) {
                        methode = methodCache.remark();
                    }
                    break;
                }
            }
        }
        return methode;
    }

    /**
     * 获取请求IP
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        if (request.getHeader("x-forwarded-for") == null) {
            return request.getRemoteAddr();
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}

4、配置启动对@AspectJ注解的支持及监听类,在Spring的配置文件中,添加如下配置即可


    
    
    

5、业务Controller引用,在@RequestMapping下添加自定义注解杰即可

    @RequestMapping(value="/addUser",method=RequestMethod.POST)
    @MethodLog(remark = "添加用户")
    public void addUser(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ////  具体的业务
    }

效果(图中隐去了信息,具体自己实现更为现实哈):
Java实现AOP切面记录日志实例_第1张图片

至此就结束AOP切面保存系统操作日志,其中原理及AOP更多功能没做讲解,可百度或官网,如有不解欢迎留言,献出你知的,帮助不知的。

你可能感兴趣的:(Java)