3

I have been going through some Spring / AOP tutorials and have somewhat familiarized myself with the related concepts.

Now coming to my requirements, I need to create an Activities Log implementation which will save the activities of a logged-in user in the DB which can range from applying for a service or creating new users in case of Admin users, etc. On invocation of any method having an annotation (say @ActivityLog), this information is to be persisted in the form of actorId, actionComment, actionTime, actedUponId, ... etc.

Now, if I create a POJO class (that maps to a ActivityLog table in the DB) and want to save this data from inside the Advice (preferably using the same transaction as the method, method uses @Transactional annotation), how do I actually populate the variables in this POJO?? I can probably get the actorId from the session object & actionTime can simply be new Date() but how about the dynamic values for actionComment / actedUponId?

Any help will be brilliant! (BTW, I have a requirement to not use Hibernate Interceptors.)

4

3 回答 3

3

这是一个完整的例子:

@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);
                }
            }
        }
    }
}

}
于 2012-05-20T15:25:33.107 回答
2

如果您希望从带注释的方法的参数中获取actionCommentand actedUponIdfrom 参数(假设它们都是字符串),您可以将绑定术语添加到您的@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,它可以连接到正在发生的所有其他事情中正常的方式。

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

于 2012-05-20T16:04:01.350 回答
0

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

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

于 2012-05-18T09:46:15.190 回答