3

我爱春天。但是当涉及到在方法执行前后插入代码时,我不得不承认,Spring/AOP 搞错了。

在我看来,硬编码方法名称或类似于方法名称的正则表达式应该是我们最不应该经历的事情。我们都从 2000 年代初期的 XML 地狱中艰难地学会了它。

相比之下,EJB3 的拦截器是基于注释的、简单的、更易于阅读的,并且它能够解决我们的大部分问题(如果不是全部的话)。如果我们可以在 Spring 中编写 EJB3 样式的拦截器,那不是很好吗?

我知道的最佳解决方案是@annotation在切入点表达式中使用,正如我在以下代码中所做的那样:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AroundExecution {

}

@Component
public class SimpleBean {
    @AroundExecution
    public void printGreetings(){
        logger.info("Hello World 2");
    }
}

@Aspect
@Component
public class SimpleAdvice {
    @Around("@annotation(com.myinterceptors.springaop.AroundExecution)")
    public Object adviceAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object retVal = null;
        try {
            logger.info("Before executing");
            retVal = joinPoint.proceed();
            logger.info("After executing");
        } 
        catch(Throwable e) {
            logger.error("Execution error");
            throw e;
        }
        return retVal;
    }
}

似乎不可能消除注释类名称的最后一点硬编码。

如果我使用 Guice,我可以这样做:

    public class SimpleModule extends AbstractModule {
        protected void configure() {
        AroundInterceptor interceptor = new AroundInterceptor();
        bindInterceptor(
            Matchers.any(), 
            Matchers.annotatedWith(BeforeExecution.class).or(Matchers.annotatedWith(AfterExecution.class)), 
            interceptor
        );
        }
    }

public class AroundInterceptor implements MethodInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(AroundInterceptor.class);

    public Object invoke(MethodInvocation invocation) throws Throwable {

        try{
            if(invocation.getMethod().getAnnotation(BeforeExecution.class)!=null){
                invokeBefore(invocation);
            }
            return invocation.proceed();
        }
        finally{
            if(invocation.getMethod().getAnnotation(AfterExecution.class)!=null){
                invokeAfter(invocation);
            }
        }
    }

    protected void invokeBefore(MethodInvocation invocation) throws Throwable {
        logger.info("Intercepted before executing: "+invocation.getMethod().getName());
    }


    protected void invokeAfter(MethodInvocation invocation) throws Throwable {
        logger.info("Intercepted after executing: "+invocation.getThis().getClass().getName()+"."+
                invocation.getMethod().getName());
    }
}

不完全是最漂亮的,但它完成了工作。

装饰器模式是一种替代方案,但它在维护方面增加了很多开销:

用于 Spring bean 的 JSR-299 CDI 装饰器

如果 Spring 可以允许对切入点表达式进行扩充以支持类定义作为参数,那就容易多了,例如@Around("@annotatedWith", MyAnnotation.class)

我想知道是否有人曾经在 Spring 中实现过不需要在元数据或应用程序上下文中进行硬编码的拦截器?

4

1 回答 1

0

有点晚了,但是看看 Spring 官方文档中的这段代码:

@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)")
public void audit(Auditable auditable) {
    AuditCode code = auditable.value();
    // ...
}

注意切入点中引用的可审计参数名称。

如果您想使用 @Around 建议,只需像这样修改您的代码:

@Around("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)")
public void audit(ProceedingJoinPoint joinPoint, Auditable auditable) {
    AuditCode code = auditable.value();
    // ...
}
于 2016-01-07T10:08:25.367 回答