`

Spring3.0 + 自定义注解实现操作日志记录功能

阅读更多



Xml代码
<aop:aspectj-autoproxy/>  

<aop:aspectj-autoproxy/> 这一步就完成了@AspectJ的支持,从而可以实现通过注解方式将通知编织到非公共方法中。



第二步:编写自定义注解。实现对方法所实现的功能进行描述,以便在通知中获取描述信息,代码非常简单,如下:


Java代码
package com.abchina.rmpf.logmng.ann;   
  
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})   
@Retention(RetentionPolicy.RUNTIME)   
@Documented  
public @interface rmpfLog {   
    String desc() default "无描述信息";   
}  

package com.abchina.rmpf.logmng.ann;

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})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface rmpfLog {
        String desc() default "无描述信息";
}


第三步:编写操作日志切面通知实现类。

在编写切面通知实现类之前,我们需要搞清楚我们需要哪些通知类型,是前置通知、后置通知、环绕通知或异常通知?根据我的需求,我们知道我们记录的操作日志 有两种情况,一种是操作成功,一种是操作失败。操作成功时则方法肯定已经执行完成,顾我们需要实现一个后置通知;操作失败时则说明方法出现异常无法正常执 行完成,顾还需要一个异常通知。因此我们就需要实现这两个通知即可。代码如下:

Java代码
package com.abchina.rmpf.logmng.aop;   
  
import java.io.File;   
import java.lang.reflect.Array;   
import java.util.Collection;   
import java.util.Iterator;   
  
import net.sf.json.JSONArray;   
import net.sf.json.JSONObject;   
  
import org.aspectj.lang.JoinPoint;   
import org.aspectj.lang.annotation.After;   
import org.aspectj.lang.annotation.AfterReturning;   
import org.aspectj.lang.annotation.AfterThrowing;   
import org.aspectj.lang.annotation.Aspect;   
import org.dom4j.Document;   
import org.dom4j.Element;   
import org.dom4j.io.SAXReader;   
import org.springframework.util.ResourceUtils;   
  
import plantix.core.business.exception.BusinessException;   
import plantix.core.context.ThreadContext;   
  
import com.abchina.rmpf.privmng.web.vo.ActorVO;   
import com.abchina.rmpf.common.Constant;   
import com.abchina.rmpf.common.DateTool;   
import com.abchina.rmpf.logmng.ann.rmpfLog;   
import com.abchina.rmpf.logmng.service.ILogService;   
import com.abchina.rmpf.logmng.web.vo.LogVO;   
import com.opensymphony.xwork2.ActionContext;   
  
@Aspect //该注解标示该类为切面类   
public class LogAspect {   
      
    /**  
     * LogService  
     * @generated  
     */  
    private ILogService logService;   
      
    //标注该方法体为后置通知,当目标方法执行成功后执行该方法体   
    @AfterReturning("within(com.abchina.irms..*) && @annotation(rl)")   
    public void addLogSuccess(JoinPoint jp, rmpfLog rl){   
        Object[] parames = jp.getArgs();//获取目标方法体参数   
        String params = parseParames(parames); //解析 目标方法体的参数   
        String className = jp.getTarget().getClass().toString();//获取目标类名   
        className = className.substring(className.indexOf("com"));   
        String signature = jp.getSignature().toString();//获取目标方法签名   
        String methodName = signature.substring(signature.lastIndexOf(".")+1, signature.indexOf("("));   
        String modelName = getModelName(className); //根据类名获取所属的模块   
           
        String ip = (String)ActionContext.getContext().getSession().get(Constant.THREAD_APP_IP_KEY); //用户IP   
        ActorVO actor = ((ActorVO)ActionContext.getContext().getSession().get(Constant.SESSION_ACTOR_KEY));   
        LogVO logvo = new LogVO();   
        logvo.setId(java.util.UUID.randomUUID().toString());   
        logvo.setClassname(className);   
        logvo.setMethodname(methodName);   
        logvo.setArgument(params);   
        logvo.setMemo(rl.desc());   
        logvo.setModelname(modelName);   
        logvo.setIp(ip);   
        logvo.setOperationtime(DateTool.getDateTime4());   
//      logvo.setErr("");   
        logvo.setFlag("1");   
           
        if(actor!=null){   
            logvo.setOrgid(actor.getOrgcode());   
            logvo.setUserid(actor.getUserid());   
            logvo.setUsername(actor.getUsername());   
        }   
           
        logService.insertLog(logvo);   
    }   
  
    //标注该方法体为异常通知,当目标方法出现异常时,执行该方法体   
    @AfterThrowing(pointcut="within(com.abchina.irms..*) && @annotation(rl)", throwing="ex")   
    public void addLog(JoinPoint jp, rmpfLog rl, BusinessException ex){   
        Object[] parames = jp.getArgs();   
        String params = parseParames(parames);   
        String className = jp.getTarget().getClass().toString();   
        className = className.substring(className.indexOf("com"));   
        String signature = jp.getSignature().toString();   
        String methodName = signature.substring(signature.lastIndexOf(".")+1, signature.indexOf("("));   
        String modelName = getModelName(className);   
           
        String ip = (String)ActionContext.getContext().getSession().get(Constant.THREAD_APP_IP_KEY);   
        ActorVO actor = ((ActorVO)ActionContext.getContext().getSession().get(Constant.SESSION_ACTOR_KEY));   
        LogVO logvo = new LogVO();   
        logvo.setId(java.util.UUID.randomUUID().toString());   
        logvo.setClassname(className);   
        logvo.setMethodname(methodName);   
        logvo.setArgument(params);   
        logvo.setMemo(rl.desc());   
        logvo.setModelname(modelName);   
        logvo.setIp(ip);   
        logvo.setOperationtime(DateTool.getDateTime4());   
        logvo.setErr(ex.toString());//记录异常信息   
        logvo.setFlag("0");   
           
        if(actor!=null){   
            logvo.setOrgid(actor.getOrgcode());   
            logvo.setUserid(actor.getUserid());   
            logvo.setUsername(actor.getUsername());   
        }   
           
        logService.insertLog(logvo);   
    }   
      
    /**  
     * 根据包名查询检索其所属的模块  
     * @param packageName 包名  
     * @return 模块名称  
     */  
    private String getModelName(String packageName){   
        String modelName = "";   
        SAXReader reader = new SAXReader();   
        try {   
            //读取project.xml 模块信息描述xml文档   
            File proj = ResourceUtils.getFile("classpath:project.xml");   
            Document doc = reader.read(proj);   
            //获取文档根节点   
            Element root = doc.getRootElement();   
            //查询模块名称   
            modelName = searchModelName(root, packageName);   
        } catch (Exception e) {   
            e.printStackTrace();   
        }   
        return modelName;   
    }   
      
    /**  
     * 采用递归方式根据包名逐级检索所属模块  
     * @param element 元素节点  
     * @param packageName 包名  
     * @return 模块名称  
     */  
    private String searchModelName(Element element, String packageName){   
        String modelName = searchModelNodes(element, packageName);   
        //若将包名解析到最后的根目录后仍未检索到模块名称,则返回空   
        if(packageName.lastIndexOf(".")==-1){   
            return modelName;   
        }   
        //逐级检索模块名称   
        if(modelName.equals("")){   
            packageName = packageName.substring(0, packageName.lastIndexOf("."));   
            modelName = searchModelName(element, packageName);   
        }   
        return modelName;   
    }   
      
      
    /**  
     * 根据xml文档逐个节点检索模块名称  
     * @param element 节点元素  
     * @param packageName 包名  
     * @return 模块名称  
     */  
    @SuppressWarnings("unchecked")   
    private String searchModelNodes(Element element, String packageName){   
           
        String modelName = "";   
        Element modules = element.element("modules");   
        Iterator it = modules.elementIterator();   
        if(!it.hasNext()){   
            return modelName;   
        }   
        while (it.hasNext()) {   
            Element model = (Element) it.next();   
            String pack = model.attributeValue("packageName");   
            String name = model.elementText("moduleCHPath");   
            if(packageName.equals(pack)){   
                modelName = name;   
                return modelName;   
            }   
            if(modelName!=null && !modelName.equals("")){   
                break;   
            }   
            modelName = searchModelNodes(model, packageName);   
        }   
           
        return modelName;   
    }   
      
    /**  
     * 解析方法参数  
     * @param parames 方法参数  
     * @return 解析后的方法参数  
     */  
    private String parseParames(Object[] parames) {   
        StringBuffer sb = new StringBuffer();   
        for(int i=0; i<parames.length; i++){   
            if(parames[i] instanceof Object[] || parames[i] instanceof Collection){   
                JSONArray json = JSONArray.fromObject(parames[i]);   
                if(i==parames.length-1){   
                    sb.append(json.toString());   
                }else{   
                    sb.append(json.toString() + ",");   
                }   
            }else{   
                JSONObject json = JSONObject.fromObject(parames[i]);   
                if(i==parames.length-1){   
                    sb.append(json.toString());   
                }else{   
                    sb.append(json.toString() + ",");   
                }   
            }   
        }   
        String params = sb.toString();   
        params = params.replaceAll("(\"\\w+\":\"\",)", "");   
        params = params.replaceAll("(,\"\\w+\":\"\")", "");   
        return params;   
    }   
  
    public ILogService getLogService() {   
        return logService;   
    }   
  
    public void setLogService(ILogService logService) {   
        this.logService = logService;   
    }   
      
      
}  

package com.abchina.rmpf.logmng.aop;

import java.io.File;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.util.ResourceUtils;

import plantix.core.business.exception.BusinessException;
import plantix.core.context.ThreadContext;

import com.abchina.rmpf.privmng.web.vo.ActorVO;
import com.abchina.rmpf.common.Constant;
import com.abchina.rmpf.common.DateTool;
import com.abchina.rmpf.logmng.ann.rmpfLog;
import com.abchina.rmpf.logmng.service.ILogService;
import com.abchina.rmpf.logmng.web.vo.LogVO;
import com.opensymphony.xwork2.ActionContext;

@Aspect //该注解标示该类为切面类
public class LogAspect {
       
        /**
         * LogService
         * @generated
         */
        private ILogService logService;
       
        //标注该方法体为后置通知,当目标方法执行成功后执行该方法体
        @AfterReturning("within(com.abchina.irms..*) && @annotation(rl)")
        public void addLogSuccess(JoinPoint jp, rmpfLog rl){
                Object[] parames = jp.getArgs();//获取目标方法体参数
                String params = parseParames(parames); //解析目标方法体的参数
                String className = jp.getTarget().getClass().toString();//获取目标类名
                className = className.substring(className.indexOf("com"));
                String signature = jp.getSignature().toString();//获取目标方法签名
                String methodName = signature.substring(signature.lastIndexOf(".")+1, signature.indexOf("("));
                String modelName = getModelName(className); //根据类名获取所属的模块
               
                String ip = (String)ActionContext.getContext().getSession().get(Constant.THREAD_APP_IP_KEY); //用户IP
                ActorVO actor = ((ActorVO)ActionContext.getContext().getSession().get(Constant.SESSION_ACTOR_KEY));
                LogVO logvo = new LogVO();
                logvo.setId(java.util.UUID.randomUUID().toString());
                logvo.setClassname(className);
                logvo.setMethodname(methodName);
                logvo.setArgument(params);
                logvo.setMemo(rl.desc());
                logvo.setModelname(modelName);
                logvo.setIp(ip);
                logvo.setOperationtime(DateTool.getDateTime4());
//                logvo.setErr("");
                logvo.setFlag("1");
               
                if(actor!=null){
                        logvo.setOrgid(actor.getOrgcode());
                        logvo.setUserid(actor.getUserid());
                        logvo.setUsername(actor.getUsername());
                }
               
                logService.insertLog(logvo);
        }

        //标注该方法体为异常通知,当目标方法出现异常时,执行该方法体
        @AfterThrowing(pointcut="within(com.abchina.irms..*) && @annotation(rl)", throwing="ex")
        public void addLog(JoinPoint jp, rmpfLog rl, BusinessException ex){
                Object[] parames = jp.getArgs();
                String params = parseParames(parames);
                String className = jp.getTarget().getClass().toString();
                className = className.substring(className.indexOf("com"));
                String signature = jp.getSignature().toString();
                String methodName = signature.substring(signature.lastIndexOf(".")+1, signature.indexOf("("));
                String modelName = getModelName(className);
               
                String ip = (String)ActionContext.getContext().getSession().get(Constant.THREAD_APP_IP_KEY);
                ActorVO actor = ((ActorVO)ActionContext.getContext().getSession().get(Constant.SESSION_ACTOR_KEY));
                LogVO logvo = new LogVO();
                logvo.setId(java.util.UUID.randomUUID().toString());
                logvo.setClassname(className);
                logvo.setMethodname(methodName);
                logvo.setArgument(params);
                logvo.setMemo(rl.desc());
                logvo.setModelname(modelName);
                logvo.setIp(ip);
                logvo.setOperationtime(DateTool.getDateTime4());
                logvo.setErr(ex.toString());//记录异常信息
                logvo.setFlag("0");
               
                if(actor!=null){
                        logvo.setOrgid(actor.getOrgcode());
                        logvo.setUserid(actor.getUserid());
                        logvo.setUsername(actor.getUsername());
                }
               
                logService.insertLog(logvo);
        }
       
        /**
         * 根据包名查询检索其所属的模块
         * @param packageName 包名
         * @return 模块名称
         */
        private String getModelName(String packageName){
                String modelName = "";
                SAXReader reader = new SAXReader();
                try {
                        //读取project.xml模块信息描述xml文档
                        File proj = ResourceUtils.getFile("classpath:project.xml");
                        Document doc = reader.read(proj);
                        //获取文档根节点
                        Element root = doc.getRootElement();
                        //查询模块名称
                        modelName = searchModelName(root, packageName);
                } catch (Exception e) {
                        e.printStackTrace();
                }
                return modelName;
        }
       
        /**
         * 采用递归方式根据包名逐级检索所属模块
         * @param element 元素节点
         * @param packageName 包名
         * @return 模块名称
         */
        private String searchModelName(Element element, String packageName){
                String modelName = searchModelNodes(element, packageName);
                //若将包名解析到最后的根目录后仍未检索到模块名称,则返回空
                if(packageName.lastIndexOf(".")==-1){
                        return modelName;
                }
                //逐级检索模块名称
                if(modelName.equals("")){
                        packageName = packageName.substring(0, packageName.lastIndexOf("."));
                        modelName = searchModelName(element, packageName);
                }
                return modelName;
        }
       
       
        /**
         * 根据xml文档逐个节点检索模块名称
         * @param element 节点元素
         * @param packageName 包名
         * @return 模块名称
         */
        @SuppressWarnings("unchecked")
        private String searchModelNodes(Element element, String packageName){
               
                String modelName = "";
                Element modules = element.element("modules");
                Iterator it = modules.elementIterator();
                if(!it.hasNext()){
                        return modelName;
                }
                while (it.hasNext()) {
                        Element model = (Element) it.next();
                        String pack = model.attributeValue("packageName");
                        String name = model.elementText("moduleCHPath");
                        if(packageName.equals(pack)){
                                modelName = name;
                                return modelName;
                        }
                        if(modelName!=null && !modelName.equals("")){
                                break;
                        }
                        modelName = searchModelNodes(model, packageName);
                }
               
                return modelName;
        }
       
        /**
         * 解析方法参数
         * @param parames 方法参数
         * @return 解析后的方法参数
         */
        private String parseParames(Object[] parames) {
                StringBuffer sb = new StringBuffer();
                for(int i=0; i<parames.length; i++){
                        if(parames[i] instanceof Object[] || parames[i] instanceof Collection){
                                JSONArray json = JSONArray.fromObject(parames[i]);
                                if(i==parames.length-1){
                                        sb.append(json.toString());
                                }else{
                                        sb.append(json.toString() + ",");
                                }
                        }else{
                                JSONObject json = JSONObject.fromObject(parames[i]);
                                if(i==parames.length-1){
                                        sb.append(json.toString());
                                }else{
                                        sb.append(json.toString() + ",");
                                }
                        }
                }
                String params = sb.toString();
                params = params.replaceAll("(\"\\w+\":\"\",)", "");
                params = params.replaceAll("(,\"\\w+\":\"\")", "");
                return params;
        }

        public ILogService getLogService() {
                return logService;
        }

        public void setLogService(ILogService logService) {
                this.logService = logService;
        }
       
       
}


大家看上面的代码会发现这两个方法体:




Java代码
@AfterReturning("within(com.abchina.irms..*) && @annotation(rl)")   
public void addLogSuccess(JoinPoint jp, rmpfLog rl){…}  

@AfterReturning("within(com.abchina.irms..*) && @annotation(rl)")
public void addLogSuccess(JoinPoint jp, rmpfLog rl){…}


Java代码
@AfterThrowing(pointcut="within(com.abchina.irms..*) && @annotation(rl)", throwing="ex")   
public void addLog(JoinPoint jp, rmpfLog rl, BusinessException ex){…}  

@AfterThrowing(pointcut="within(com.abchina.irms..*) && @annotation(rl)", throwing="ex")
public void addLog(JoinPoint jp, rmpfLog rl, BusinessException ex){…}


这两个方法体分别是后置通知和异常通知的实现。它们有两个相同的参数jp和rl,jp是切点对象,通过该对象可以获取切点所切入方法所在的类,方法名、参 数等信息,具体方法可以看方法体的实现;rl则是我们的自定义注解的对象,通过该对象我们可以获取注解中参数值,从而获取方法的描述信息。在异常通知中多 出了一个ex参数,该参数是方法执行时所抛出的异常,从而可以获取相应的异常信息。此处为我写的自定义异常。注意:如果指定异常参数,则异常对象必须与通 知所切入的方法体抛出的异常保持一致,否则该通知不会执行。



addLogSuccess方法签名上的@AfterReturning("within(com.abchina.irms..*) && @annotation(rl)")注解,是指定该方法体为后置通知,其有一个表达式参数,用来检索符合条件的切点。该表达式指定com/abchina /irms目录下及其所有子目录下的所有带有@rmpfLog注解的方法体为切点。


addLog方法签名上的@AfterThrowing(pointcut="within(com.abchina.irms..*) && @annotation(rl)", throwing="ex")注解,是指定方法体为异常通知,其有一个表达式参数和一个抛出异常参数。表达式参数与后置通知的表达式参数含义相同,而抛出 异常参数,则表示如果com/abchina/irms目录下及其所有子目录下的所有带有@rmpfLog注解的方法体在执行时抛出 BusinessException异常时该通知便会执行。



注意:切面通知实现类是一个普通的pojo对象,如果要想指定其为通知对象,则需在其类名上添加@Aspect注解



第四步:在spring配置文件中创建通知bean对象。


Xml代码
<bean id="logAspect" class="com.abchina.rmpf.logmng.aop.LogAspect">  
    <property name="logService">  
      <ref local="com.abchina.rmpf.logmng.service.impl.LogServiceImpl"/>  
    </property>  
  </bean>  

<bean id="logAspect" class="com.abchina.rmpf.logmng.aop.LogAspect">
        <property name="logService">
      <ref local="com.abchina.rmpf.logmng.service.impl.LogServiceImpl"/>
    </property>
  </bean>



第五步:使用操作日志记录注解。

通过以上四步操作后,操作日志的记录功能就算完成了,那我们该如何使用呢?很简单!在com/abchina/irms目录下及其所有子目录下任意找到一个service层的某个类的方法,在其方法体上添加@rmpfLog(desc=”描述信息”)即可。代码如下:


Java代码
               @rmpfLog(desc="创建关联交易合同")   
@Transactional  
public void insertRtcont(RtcontVO rtcontVO) throws BusinessException {   
    rtcontAL.insertRtcont(toRtcontDomain(rtcontVO));   
}

分享到:
评论
2 楼 bkk854762363 2015-09-12  
好文章,赞
1 楼 怪黍叔 2015-03-02  
学习了

相关推荐

Global site tag (gtag.js) - Google Analytics