Java 如何在外呼中使用AOP

Java 如何在外呼中使用AOP,java,aop,aspectj,spring-aop,feign,Java,Aop,Aspectj,Spring Aop,Feign,我对如何在AOP中使用假客户端感兴趣。例如: API: public interface LoanClient { @RequestLine("GET /loans/{loanId}") @MeteredRemoteCall("loans") Loan getLoan(@Param("loanId") Long loanId); } @Aspect @Component // Spring Component annotation public class Metric

我对如何在AOP中使用假客户端感兴趣。例如:

API:

public interface LoanClient {
    @RequestLine("GET /loans/{loanId}")
    @MeteredRemoteCall("loans")
    Loan getLoan(@Param("loanId") Long loanId);
}
@Aspect
@Component // Spring Component annotation
public class MetricAspect {

    @Around(value = "@annotation(annotation)", argNames = "joinPoint, annotation")
    public Object meterRemoteCall(ProceedingJoinPoint joinPoint, 
                        MeteredRemoteCall annotation) throws Throwable {
    // do something
  }
}
配置:

public interface LoanClient {
    @RequestLine("GET /loans/{loanId}")
    @MeteredRemoteCall("loans")
    Loan getLoan(@Param("loanId") Long loanId);
}
@Aspect
@Component // Spring Component annotation
public class MetricAspect {

    @Around(value = "@annotation(annotation)", argNames = "joinPoint, annotation")
    public Object meterRemoteCall(ProceedingJoinPoint joinPoint, 
                        MeteredRemoteCall annotation) throws Throwable {
    // do something
  }
}
但我不知道如何“拦截”api方法调用。我哪里出错了

更新:

public interface LoanClient {
    @RequestLine("GET /loans/{loanId}")
    @MeteredRemoteCall("loans")
    Loan getLoan(@Param("loanId") Long loanId);
}
@Aspect
@Component // Spring Component annotation
public class MetricAspect {

    @Around(value = "@annotation(annotation)", argNames = "joinPoint, annotation")
    public Object meterRemoteCall(ProceedingJoinPoint joinPoint, 
                        MeteredRemoteCall annotation) throws Throwable {
    // do something
  }
}
我的Spring类注释:

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

    String serviceName();
}

您的情况有些复杂,因为您有几个问题:

  • 您可以使用SpringAOP,这是一个基于动态代理(接口为JDK代理,类为CGLIB代理)的“AOPLite”框架。它只适用于SpringBean/组件,但从我所看到的
    LoanClient
    不是Spring
    @Component
  • 即使它是Spring组件,Feign也会通过反射创建自己的JDK动态代理。它们不受春天的控制。可能有一种方法可以手动将它们连接到Spring中,既可以通过编程方式,也可以通过XML配置。但在那个里我帮不了你们,因为我不用弹簧
  • SpringAOP只支持AspectJ切入点的子集。具体来说,它不支持
    call()
    ,只支持
    execution()
    。也就是说,它只在执行方法的地方编织,而不是调用方法的地方
  • 但是执行发生在实现接口的方法中,接口方法上的注释(如
    @meteredmotecall
    )永远不会被它们的实现类继承。事实上,在Java中永远不会继承方法注释,只继承类(而不是接口!)到相应子类的类级注释。也就是说,即使您的注释类具有继承的
    @元注释,它对
    @Target({ElementType.METHOD})
    也没有帮助,只对
    @Target({ElementType.TYPE})
    有帮助更新:因为我以前已经回答过好几次这个问题,所以我刚刚记录了这个问题,并在中介绍了一个解决方法
那你能做什么呢?最好的选择是从Spring应用程序中加载(加载时编织)。这使您能够使用
call()
切入点,而不是SpringAOP隐式使用的
execution()
。如果在AspectJ中的方法上使用
@annotation()
切入点,它将匹配调用和执行,我将在一个独立的示例中向您展示(没有Spring,但效果与在Spring中使用LTW的AspectJ相同):

标记注释:

public interface LoanClient {
    @RequestLine("GET /loans/{loanId}")
    @MeteredRemoteCall("loans")
    Loan getLoan(@Param("loanId") Long loanId);
}
@Aspect
@Component // Spring Component annotation
public class MetricAspect {

    @Around(value = "@annotation(annotation)", argNames = "joinPoint, annotation")
    public Object meterRemoteCall(ProceedingJoinPoint joinPoint, 
                        MeteredRemoteCall annotation) throws Throwable {
    // do something
  }
}
package de.scrum\u master.app;
导入java.lang.annotation.ElementType;
导入java.lang.annotation.Retention;
导入java.lang.annotation.RetentionPolicy;
导入java.lang.annotation.Target;
@保留(RetentionPolicy.RUNTIME)
@目标(ElementType.METHOD)
public@interface meteredmotecall{}
外国客户:

public interface LoanClient {
    @RequestLine("GET /loans/{loanId}")
    @MeteredRemoteCall("loans")
    Loan getLoan(@Param("loanId") Long loanId);
}
@Aspect
@Component // Spring Component annotation
public class MetricAspect {

    @Around(value = "@annotation(annotation)", argNames = "joinPoint, annotation")
    public Object meterRemoteCall(ProceedingJoinPoint joinPoint, 
                        MeteredRemoteCall annotation) throws Throwable {
    // do something
  }
}
此示例客户端获取完整的StackOverflow问题页面(HTML源代码)作为字符串

package de.scrum\u master.app;
进口外国参数;
进口外国请求行;
公共接口StackOverflowClient{
@请求行(“GET/questions/{questionId}”)
@计量远程呼叫
字符串getQuestionPage(@Param(“questionId”)长questionId);
}
驱动程序应用程序:

public interface LoanClient {
    @RequestLine("GET /loans/{loanId}")
    @MeteredRemoteCall("loans")
    Loan getLoan(@Param("loanId") Long loanId);
}
@Aspect
@Component // Spring Component annotation
public class MetricAspect {

    @Around(value = "@annotation(annotation)", argNames = "joinPoint, annotation")
    public Object meterRemoteCall(ProceedingJoinPoint joinPoint, 
                        MeteredRemoteCall annotation) throws Throwable {
    // do something
  }
}
此应用程序以三种不同的方式使用外部客户端接口进行演示:

  • 无需伪装,通过匿名子类手动实例化
  • 类似于#1,但这次在实现方法中添加了一个额外的标记注释
  • 通过伪装的规范用法
  • package de.scrum\u master.app;
    导入java.util.regex.Matcher;
    导入java.util.regex.Pattern;
    进口外国;
    导入feign.codec.StringDecoder;
    公共类应用程序{
    公共静态void main(字符串[]args){
    StackOverflowClient soClient;
    长问题ID=41856687L;
    soClient=新的StackOverflowClient(){
    @凌驾
    公共字符串getQuestionPage(长loanId){
    返回“StackOverflowClient无伪”;
    }
    };
    System.out.println(“+soClient.getQuestionPage(questionId));
    soClient=新的StackOverflowClient(){
    @凌驾
    @计量远程呼叫
    公共字符串getQuestionPage(长loanId){
    返回“StackOverflowClient无伪+额外注释”;
    }
    };
    System.out.println(“+soClient.getQuestionPage(questionId));
    //通过外挂创建StackOverflowClient
    字符串baseUrl=”http://stackoverflow.com";
    soClient=假装
    .builder()
    .decoder(新的StringDecoder())
    .target(StackOverflowClient.class,baseUrl);
    匹配器标题匹配器=模式
    
    .compile(([^您的情况有些复杂,因为您有几个问题:

    • 您使用SpringAOP,这是一个基于动态代理(JDK代理用于接口,CGLIB代理用于类)的“AOP-lite”框架。它只适用于SpringBean/组件,但从我看来,您的
      LoanClient
      不是Spring
      @组件
    • 即使它是一个Spring组件,Feign也会通过反射创建自己的JDK动态代理。它们不在Spring的控制范围内。可能有一种方法可以通过编程或XML配置手动将它们连接到Spring中。但我无法帮助您,因为我不使用Spring
    • Spring AOP只支持AspectJ切入点的一个子集。具体来说,它不支持
      call()
      ,只支持
      execution()
      。也就是说,它只在方法执行的地方进行编织,而不是在方法被调用的地方
    • 但执行发生在实现接口的方法中,接口方法(如
      @meteredmotecall
      )上的注释永远不会被它们的实现类继承。事实上,方法注释永远不会在Java中继承,只会从类(而不是接口)继承类级注释到相应的子类,即即使y