java - 使用 Spring AOP 创建拦截器

标签 java spring aop spring-aop

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

在我看来,硬编码方法名称或类似于方法名称的正则表达式应该是我们让自己经历的最后一件事。我们都在 2000 年代初从 XML hell 中艰难地学到了它。

相比之下,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());
    }
}

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

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

JSR-299 CDI Decorators for Spring beans

如果 Spring 允许扩充切入点表达式以支持类定义作为参数,例如@Around("@annotatedWith", MyAnnotation.class)

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

最佳答案

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

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

注意切入点中引用的auditable 参数名称。

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

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

关于java - 使用 Spring AOP 创建拦截器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14293892/

相关文章:

java - 使用 Spring 4 WebSocket 从 Java 推送消息

java - Spring MVC : formal unbound in pointcut

java - 如何检索以逗号作为分隔符的空格行?

java - 将 PDF 文件附加到来自应用程序的电子邮件

java - 如何在 Spring Data JPA 中执行嵌套连接获取

java - 将 Spring Boot jar 部署到 Azure,并使 Azure 重新启动

java - 找不到aspectj-maven-plugin的依赖项

java - 构造函数调用不同类的 AspectJ 切入点 - 识别创建的对象类型

java - SocketChannel.write() 在单个线程中处理多个客户端

Java 扫描器和行延续