在一个项目即将完成的时候,提出要增加日志记录的功能,并且点名要求利用AOP的方式来做(领导是业务通,技术上可能只知道这种方式。)。没有办法,人在屋檐下,只能按照这种方式来做。下面切入正题:

1.首先,导入jar包:

aspectjrt.jar

aspectjweaver.jar

2.第二步,编写日志管理工具类,如下:

package com.myframework.aop;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.annotation.AfterReturning;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Pointcut;  
import org.springframework.beans.factory.annotation.Autowired;

import com.myframework.system.service.LogService;

@Aspect  
public class LogAspect {  
      
    @Autowired  
    LogService logService;  
    
    /** 
     * save日志切入点
     */  
    @Pointcut("execution(* com.myframework.*..*..service.*.save*(..))")  
    public void saveOperation(){  
    }
    
    /** 
     * create日志切入点
     */  
    @Pointcut("execution(* com.myframework.*..*..service.*.create*(..))")  
    public void createOperation(){  
    }
    
    /** 
     * update日志切入点
     */  
    @Pointcut("execution(* com.myframework.*..*..service.*.update*(..))")  
    public void updateOperation(){  
    }
    
    /** 
     * delete日志切入点
     */  
    @Pointcut("execution(* com.myframework.*..*..service.*.delete*(..))")  
    public void deleteOperation(){  
    }
    
    /** 
     * save操作(后置通知) 
     * @param joinPoint 
     * @param object 
     * @throws Throwable 
     */
    @AfterReturning(value = "saveOperation()", argNames = "object", returning = "object")  
    public void logSaveOperation(JoinPoint joinPoint, Object object) throws Throwable {  
    	// 获取方法名  
        String methodName = joinPoint.getSignature().getName();
        // 获取操作内容  
        String opContent = optionContent(joinPoint.getArgs(), methodName); 
        
    	logService.operationLog("数据增加", "方法名:" + methodName + ";操作内容:" + opContent);
    }
    
    /** 
     * create操作(后置通知) 
     * @param joinPoint 
     * @param object 
     * @throws Throwable 
     */
    @AfterReturning(value = "createOperation()", argNames = "object", returning = "object")  
    public void logCreateOperation(JoinPoint joinPoint, Object object) throws Throwable {  
    	// 获取方法名  
        String methodName = joinPoint.getSignature().getName();  
        // 获取操作内容  
        String opContent = optionContent(joinPoint.getArgs(), methodName); 
    	logService.operationLog("数据增加", "方法名:" + methodName + ";操作内容:" + opContent);
    }
    
    /** 
     * update操作(后置通知) 
     * @param joinPoint 
     * @param object 
     * @throws Throwable 
     */
    @AfterReturning(value = "updateOperation()", argNames = "object", returning = "object")  
    public void logUpdateOperation(JoinPoint joinPoint, Object object) throws Throwable {  
    	// 获取方法名  
        String methodName = joinPoint.getSignature().getName();  
        // 获取操作内容  
        String opContent = optionContent(joinPoint.getArgs(), methodName); 
    	logService.operationLog("数据修改", "方法名:" + methodName + ";操作内容:" + opContent);
    }

    /** 
     * delete操作(后置通知) 
     * @param joinPoint 
     * @param object 
     * @throws Throwable 
     */
    @AfterReturning(value = "deleteOperation()", argNames = "object", returning = "object")  
    public void logDeleteOperation(JoinPoint joinPoint, Object object) throws Throwable {  
    	// 获取方法名  
        String methodName = joinPoint.getSignature().getName(); 
        // 获取操作内容  
        String opContent = optionContent(joinPoint.getArgs(), methodName);         
    	logService.operationLog("数据删除", "方法名:" + methodName + ";操作内容:" + opContent);
    }
    
    /** 
     * 使用Java反射来获取被拦截方法(insert、update)的参数值, 将参数值拼接为操作内容 
     *  
     * @param args 
     * @param mName 
     * @return 
     */  
    public String optionContent(Object[] args, String mName) {  
        if (args == null) {  
            return null;  
        }  
        StringBuffer rs = new StringBuffer();  
        rs.append(mName);  
        String className = null;  
        int index = 1;  
        // 遍历参数对象  
        for (Object info : args) {  
            // 获取对象类型  
            className = info.getClass().getName();  
            className = className.substring(className.lastIndexOf(".") + 1);  
            rs.append("[参数" + index + ",类型:" + className + ",值:");  
            // 获取对象的所有方法  
            Method[] methods = info.getClass().getDeclaredMethods();  
            // 遍历方法,判断get方法  
            for (Method method : methods) {
                String methodName = method.getName();  
                // 判断是不是get方法  
                if (methodName.indexOf("get") == -1) {// 不是get方法  
                    continue;// 不处理  
                }  
                Object rsValue = null;  
                try {  
                    // 调用get方法,获取返回值  
                    rsValue = method.invoke(info);  
                } catch (Exception e) {  
                    continue;  
                }  
                // 将值加入内容中  
                rs.append("(" + methodName + ":" + rsValue + ")");  
            }  
            rs.append("]");  
            index++;  
        }  
        return rs.toString();  
    }  
  
}


两个重点,一个是切面@Pointcut写法:


1 ) execution(* *(..))-- 表示匹配所有方法


2

) execution(public * com. savage.service.UserService.*(..))-- 表示匹配 com.savage.server.UserService 中所有的公有方法


3)execution(* com.savage.server..*.*(..))--表示匹配com.savage.server包及其子包下的所有方法

另一个是通过反射机制来获取方法中的参数值。

3.ApplicationContext.xml文件配置

<aop:aspectj-autoproxy />
<bean id="logBean" class="com.myframework.aop.LogAspect"></bean>

4.第三步,编写测试类。这个就不写了,自由发挥吧。


到这儿功能就完成了。吐槽一下领导的这种做法,并不是说这日志管理的实现方式不好,这种方式简单方便效果很好,但是这种方式最好是项目搭建的时候就直接搭建好日志管理,毕竟切面截取是有一定规则的。他们这个项目快结束了再来制定这样的规则,对原来的代码需要大面积重构,带来的负面效果杠杠的。