Spring / AOP:在数据库中实现活动日志的最佳方式

时间:2012-05-18 09:32:55

标签: spring aop aspectj

我一直在阅读一些Spring / AOP教程,并对相关概念有所了解。

现在满足我的要求,我需要创建一个活动日志实现,它将保存登录用户在数据库中的活动,其中包括申请服务或创建新用户{{1}在调用具有注释的任何方法(例如Admin)时,此信息将以@ActivityLogactorIdactionComment的形式保留, actionTime,......等。

现在,如果我创建一个POJO类(映射到数据库中的actedUponId表)并希望从ActivityLog内部保存这些数据(最好使用与方法相同的事务,方法使用Advice注释),我如何实际填充此POJO中的变量?我可以从会话对象&中获得@TransactionalactorId可以只是actionTime,但new Date() / actionComment的动态值怎么样?

任何帮助都将是辉煌的! (顺便说一句,我要求不使用Hibernate拦截器。)

3 个答案:

答案 0 :(得分:2)

这是一个完整的例子:

@Aspect
@Component
public class WebMethodAuditor {

protected final Log logger = LogFactory.getLog(getClass());

public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss";

@Autowired
AuditRecordDAO auditRecordDAO; 

@Before("execution(* com.mycontrollers.*.*(..))")
public void beforeWebMethodExecution(JoinPoint joinPoint) {
    Object[] args = joinPoint.getArgs();
    String methodName = joinPoint.getSignature().getName();
    User principal = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    Timestamp timestamp = new Timestamp(new java.util.Date().getTime());
    // only log those methods called by an end user
    if(principal.getUsername() != null) {
        for(Object o : args) {
            Boolean doInspect = true;
            if(o instanceof ServletRequestDataBinder) doInspect = false;
            if(o instanceof ExtendedModelMap) doInspect = false;
            if(doInspect) {
                if(o instanceof BaseForm ) {
                    // only show form objects
                    AuditRecord ar = new AuditRecord();
                    ar.setUsername(principal.getUsername());
                    ar.setClazz(o.getClass().getCanonicalName());
                    ar.setMethod(methodName);
                    ar.setAsString(o.toString());
                    ar.setAudit_timestamp(timestamp);
                    auditRecordDAO.save(ar);
                }
            }
        }
    }
}

}

答案 1 :(得分:2)

如果您希望从带注释方法的参数中获取actionCommentactedUponId(假设它们都是字符串),您可以将绑定项添加到@Around切入点中这样:

@Around("@annotation(ActivityLog) && args(actionComment,actedUponId)")
public Object logActivity(ProceedingJoinPoint pjp,
        String actionComment, String actedUponId) throws Throwable {
    // ... get other values from context, etc. ...
    // ... write to log ...
    pjp.proceed();
}

切入点中的args绑定可以在部分指定的模式下使用,如果有其他关于你不感兴趣的参数,并且因为方面本身就是一个bean,它可以被连线以正常方式进行的其他事情。

请注意,如果您在相同的方法调用上混合声明式事务管理,则必须使方面的顺序正确。这部分是通过使aspect bean实现Spring Ordered接口,并通过order属性控制事务的优先级到<tx:annotation-driven/>来完成的。 (如果这是不可能的,你将被迫通过直接交易处理做出聪明的事情;这是一个更加痛苦的选择,以便做对......)

答案 2 :(得分:0)

您将在您的建议中获得对org.aspectj.lang.JoinPoint的引用。您可以使用toShortString()获取正在执行的目标方法的名称。您可以拥有带{的循环/属性文件{1}}条目。此评论可以填充到method-name=comments。方法名称可以设置为POJO.actionComment

我希望建议应该在同一个事务中运行,如果建议使用数据访问方法并且服务方法使用@Transactional。