我一直在看一些Spring AOP的教程,对相关的概念已经有了一定的了解。
现在来到我的需求,我需要创建一个活动日志的实现,它将保存一个登录用户在DB中的活动,这些活动的范围可以从申请服务或创建新用户的情况。Admin
用户等。在调用任何有注解的方法时(比如说 @ActivityLog
),这些信息将以以下形式存在。actorId
, actionComment
, actionTime
, actedUponId
,......等等。
现在,如果我创建一个POJO类(它映射到一个 ActivityLog
表),并希望将这些数据从数据库中的 Advice
(最好使用与方法相同的交易,方法使用的是 @Transactional
注解),我如何实际填充这个POJO中的变量?我大概可以在这个POJO中得到 actorId
的会话对象&。actionTime
可以简单地将 new Date()
的动态值呢?actionComment
actedUponId
?
任何帮助将是辉煌的! 顺便说一下,我有一个要求,就是不能使用Hibernate拦截器)。
这里是一个完整的例子。
@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);
}
}
}
}
}
}
如果你正在寻找得到 actionComment
和 actedUponId
从参数到注解方法(假设它们都是字符串),你可以将绑定项添加到你的 @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();
}
The args
点切中的绑定可以在部分指定模式下使用,以防有其他关于你不感兴趣的参数,由于方面本身就是一个bean,所以它可以以正常的方式被连接到其他一切正在进行的事情中。
需要注意的是,如果你在同一个方法调用上混合了声明式事务管理,你就必须把方面的顺序搞对。这在一定程度上是通过让方面Bean也实现Spring的 Ordered
接口,并通过控制交易的优先级,通过 order
归于 <tx:annotation-driven/>
. (如果这是不可能的,你将被迫用直接交易处理来做聪明的事情;那是一个更痛苦的选择,要搞好...)
你将得到一个参考 org.aspectj.lang.JoinPoint
在你的建议中,你可以通过以下方法获得正在执行的目标方法的名称 toShortString()
.你可以有一个循环属性文件,其中包含了 method-name=comments
这些注释可以被填入到 "我的名字 "中。POJO.actionComment
.method-name可以设置为 POJO.actedUponId
.
我希望建议应该在同一个事务内运行,如果数据访问方法被建议,服务方法使用@Transactional。