paulwong

自定义注释与操作行为记录

自定义注释就是一个标记,一个信息收集器,如果配合SPRING的AOP使用,可以记录用户的操作行为。

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

/**
 * 新增的用法:@Audit(behaviour="新增了专题", 
 *            value="#{args[0].colSubject}")
 *
 * 修改的用法:@Audit(behaviour="修改了专题", id="#{args[0].colSubject.id}", 
 *            className="com.paul.program.colsubject.valueobject.ColSubject",
 *            value="#{args[0].colSubject}")
 *
 * 删除的用法:@Audit(behaviour="删除了专题", id="#{args[0].colSubject.id}"
 *             className="com.paul.program.colsubject.valueobject.ColSubject")
 *             
 * 
@author PAUL
 *
 
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Audit {
    
    String id() default "";
    String className() default "";
    String collectionName() default "";
    String value() default "";
    String behaviour();

}


值对象
AuditData.java
package com.paul.common.audit;

import java.io.Serializable;
import java.util.Date;

import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

import com.paul.common.util.jackson.CustomJsonDateSerializer;

@Document(collection = "auditdata")
public class AuditData implements Serializable{

    private static final long serialVersionUID = -4011585863836336249L;
    
    @Id
    private String id;
    
    @CreatedBy
    @Field("userid")
    @JsonProperty("userid")
    private String userId;
    
    @CreatedDate
    @Field("createdate")
    @JsonProperty("createdate")
    @JsonSerialize(using = CustomJsonDateSerializer.class)
    private Date createDate;
    
    private String behaviour;
    
    @Field("newvalue")
    @JsonProperty("newvalue")
    private String newValue;
    
    @Field("oldvalue")
    @JsonProperty("oldvalue")
    private String oldValue;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public String getBehaviour() {
        return behaviour;
    }

    public void setBehaviour(String behaviour) {
        this.behaviour = behaviour;
    }


    public String getNewValue() {
        return newValue;
    }

    public void setNewValue(String newValue) {
        this.newValue = newValue;
    }

    public String getOldValue() {
        return oldValue;
    }

    public void setOldValue(String oldValue) {
        this.oldValue = oldValue;
    }

    public String toString() {
        return "AuditData [id=" + id + ", userId=" + userId + ", createDate="
                + createDate + ", behaviour=" + behaviour + ", newValue="
                + newValue + ", oldValue=" + oldValue + "]";
    }
    

}


RootObject.java
package com.paul.common.audit;

public class RootObject {

    private final Object[] args;

    private final Object invokedObject;

    private final Object returned;

    private final Throwable throwned;

    public RootObject(Object invokedObject, Object[] args, Object returned, Throwable throwned) {
        super();
        this.invokedObject = invokedObject;
        this.args = args;
        this.returned = returned;
        this.throwned = throwned;
    }

    public Object[] getArgs() {
        return args;
    }

    public Object getInvokedObject() {
        return invokedObject;
    }

    public Object getReturned() {
        return returned;
    }

    public Throwable getThrowned() {
        return throwned;
    }

}


TemplateParserContext.java
package com.paul.common.audit;

import org.springframework.expression.ParserContext;

public class TemplateParserContext implements ParserContext {

    public String getExpressionPrefix() {
        return "#{";
    }

    public String getExpressionSuffix() {
        return "}";
    }

    public boolean isTemplate() {
        return true;
    }
}


获取用户ID
package com.paul.common.audit.aware;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.AuditorAware;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.paul.common.constant.Constants;
import com.paul.program.account.valueobject.Account;

@Component
public class MyAppAuditor implements AuditorAware<String> {
    
    private static Logger logger = LoggerFactory.getLogger(MyAppAuditor.class);

    // get your user name here
    public String getCurrentAuditor() {
        
        String result = "N/A";
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                    .getRequestAttributes()).getRequest();
            Account account = (Account)request.getSession().getAttribute(Constants.USER_INFO);
            result = account.getLoginName();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return result;
    }
}


切面
package com.paul.common.audit.interceptor;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParseException;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;

import com.paul.common.audit.AuditData;
import com.paul.common.audit.AuditDataRequest;
import com.paul.common.audit.RootObject;
import com.paul.common.audit.TemplateParserContext;
import com.paul.common.audit.annotation.Audit;
import com.paul.common.audit.service.AuditDataService;
import com.paul.common.util.StringUtils;

@Component
@Aspect
public class AuditAspect {
    
    private static Logger logger = LoggerFactory.getLogger(AuditAspect.class);
    
    @Autowired
    private AuditDataService auditDataService;
    
    @Autowired
    private MongoTemplate mongoTemplate;
    
    private SimpleDateFormat dateFormatPrototype = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private Map<String, Expression> expressionCache = new ConcurrentHashMap<String, Expression>();

    private ExpressionParser expressionParser = new SpelExpressionParser();

    private ParserContext parserContext = new TemplateParserContext();
    

    protected static void appendThrowableCauses(Throwable throwable, String separator, StringBuilder toAppendTo) {
        List<Throwable> alreadyAppendedThrowables = new ArrayList<Throwable>();

        while (throwable != null) {
            // append
            toAppendTo.append(throwable.toString());
            alreadyAppendedThrowables.add(throwable);

            // cause
            Throwable cause = throwable.getCause();
            if (cause == null || alreadyAppendedThrowables.contains(cause)) {
                break;
            } else {
                throwable = cause;
                toAppendTo.append(separator);
            }
        }
    }
    
    private String getValueByEl(String template, Object invokedObject, Object[] args, Object returned, Throwable throwned)
    {
        String result = "N/A";
        if(StringUtils.isBlank(template))
            return result;
        try {
            Expression expression = expressionCache.get(template);
            if (expression == null) {
                expression = expressionParser.parseExpression(template, parserContext);
                expressionCache.put(template, expression);
            }

            Object evaluatedMessage = expression.getValue(new RootObject(invokedObject, args, returned, throwned), Object.class);
            result = evaluatedMessage.toString();
        } catch (ParseException e) {
            logger.error(e.getMessage(), e);
        } catch (EvaluationException e) {
            logger.error(e.getMessage(), e);
        }
        return result;
    }


    protected AuditData buildAuditData(Audit auditAnnotation, Object invokedObject, Object[] args, Object returned, Throwable throwned, long durationInNanos) {
        
        AuditData auditData = new AuditData();
        auditData.setBehaviour(auditAnnotation.behaviour());
        String id = this.getValueByEl(auditAnnotation.id(), invokedObject, args, returned, throwned);
        auditData.setOldValue(this.getOldValueToString(id, auditAnnotation.className()));
        try {
            String newValue = this.getValueByEl(auditAnnotation.value(), invokedObject, args, returned, throwned);
            auditData.setNewValue(newValue);

            StringBuilder msg = new StringBuilder();

            SimpleDateFormat simpleDateFormat = (SimpleDateFormat) dateFormatPrototype.clone();
            msg.append(simpleDateFormat.format(new Date()));
//            auditData.setCreateDate(simpleDateFormat.format(new Date()));

            msg.append(" ").append(newValue);

            if (throwned != null) {
                msg.append(" threw '");
                appendThrowableCauses(throwned, ", ", msg);
                msg.append("'");
            }
            msg.append(" by ");
            String user = this.getUser();
            if (user == null) {
                user = "anonymous";
            } 
            msg.append(" in ") .append(TimeUnit.MILLISECONDS.convert(durationInNanos, TimeUnit.NANOSECONDS)).append(" ms");
            return auditData;
        } catch (Exception e) {
            /*StringBuilder msg = new StringBuilder("Exception evaluating template '" + template + "': ");
            appendThrowableCauses(e, ", ", msg);
*/
            return auditData;
        }
    }
    
    private String getOldValueToString(/*ProceedingJoinPoint proceedingJoinPoint,*/ String id, String className) 
    {
        String result = "N/A";
        /*String id = "5385be613d2a47eec07c53d4";
        try {
            MethodSignature methodSig = (MethodSignature) proceedingJoinPoint.getSignature();
            Object target = proceedingJoinPoint.getTarget();
            Object newValue = proceedingJoinPoint.getArgs()[0];
            String findMethodName = "findOne";
            Object oldValue=null;
            Method findMethod = target.getClass().getDeclaredMethod(findMethodName, (Class<?>[])null);
            oldValue = findMethod.invoke(target, (Object[]) null);

        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
*/
        if(StringUtils.isBlank(id) || StringUtils.isBlank(className))
            return result;
        
        try {
            Object object = mongoTemplate.findById(id, Class.forName(className));
            result = ToStringBuilder.reflectionToString(object,  ToStringStyle.SHORT_PREFIX_STYLE); 
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return result;
    }
    
//    @Around("execution(* com.paul..**.repository..*(..))")
    @Around("@annotation(auditAnnotation)")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint, Audit auditAnnotation) throws Throwable {
        
        boolean ok = false;
        Object o = null;
        AuditData auditData = null;
        try 
        {
            auditData = buildAuditData(auditAnnotation, proceedingJoinPoint.getThis(), 
                    proceedingJoinPoint.getArgs(), 
                    o, null, 10);
            o = proceedingJoinPoint.proceed();
            ok = true;
            return o;
        } 
        finally 
        {
            if (ok)
            {
//                String value = (MessageFormat.format(auditAnnotation.value(), proceedingJoinPoint.getArgs()));
                try 
                {
                    AuditDataRequest auditDataRequest = new AuditDataRequest();
                    auditDataRequest.setAuditData(auditData);
                    auditDataService.addAuditData(auditDataRequest);
                    logger.info(auditData + "");
                } 
                catch (Exception e) 
                {
                    logger.error(e.getMessage(), e);
                }
            }    
        }
    }
    
    private String getUser()
    {
        return "Paul";
    }
    

}


保存至数据库
package com.paul.common.audit.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import com.paul.common.audit.AuditData;
import com.paul.common.audit.AuditDataRequest;
import com.paul.common.audit.annotation.Audit;
import com.paul.common.audit.repository.AuditDataRepository;
import com.paul.program.colsubject.valueobject.ColSubjectRequest;

@Service
public class AuditDataService {
    
    @Autowired
    private AuditDataRepository auditDataRepository;
    
//    @Audited(message = "save(#{args[0].name}, #{args[0].email}): #{returned?.id}")
    @Audit(behaviour="修改了审计", id="#{args[0].colSubject.id}", 
            className="com.paul.program.colsubject.valueobject.ColSubject",
            value="#{args[0].colSubject}")
    public void saveObject(ColSubjectRequest colSubjectRequest)
    {
        
    }
    
    @Audit(behaviour="删除了专题", id="#{args[0]}",
             className="com.paul.program.subject.valueobject.Subject")
    public void delete(String id) {
    }
    
    @Audit(behaviour="新增了专题", value="#{args[0].colSubject}")
    public void add(ColSubjectRequest colSubjectRequest) {
    }
    
    public AuditData addAuditData(AuditDataRequest auditDataRequest)
    {
        return auditDataRepository.save(auditDataRequest.getAuditData());
    }
    
    public Page<AuditData> findAll(AuditDataRequest auditDataRequest)
    {
        Page<AuditData> page = auditDataRepository.findAll(auditDataRequest.getPageable());
        return page;
    }

}


DAO
package com.paul.common.audit.repository;

import org.springframework.data.repository.PagingAndSortingRepository;

import com.paul.common.audit.AuditData;

/**
 * 审计
 * 
 
*/
public interface AuditDataRepository extends PagingAndSortingRepository<AuditData, String>{
    
}

posted on 2014-07-25 14:56 paulwong 阅读(1358) 评论(0)  编辑  收藏 所属分类: SPRING


只有注册用户登录后才能发表评论。


网站导航: