当前位置: 首页 > 知识库问答 >
问题:

如何在外呼中使用AOP

萧懿轩
2023-03-14

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

应用程序编程接口:

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方法调用。我哪里出错了?

更新:

我的Spring课注解:

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

    String serviceName();
}

共有1个答案

张亦
2023-03-14

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

  • 您使用Spring AOP,这是一个基于动态代理(接口的 JDK 代理,类的 CGLIB 代理)的“AOP 精简版”框架。它仅适用于Spring豆/组件,但从我所看到的,您的贷款客户不是Spring@Component
  • 即使它是Spring组件,Feign也会通过反射创建自己的JDK动态代理。它们不受Spring的控制。可能有一种方法可以手动将它们连接到Spring中,无论是以编程方式还是通过XML配置。但是在那里我无法帮助你,因为我不使用Spring。
  • Spring AOP 仅支持 AspectJ 切入点的子集。具体来说,它不支持 call(),而只支持执行 ()。也就是说,它只编织到执行方法的地方,而不是调用它的地方。
  • 但是,执行发生在实现接口的方法中,并且接口方法(如@MeteredRemoteCall)上的注释永远不会被其实现类继承。事实上,方法注释在Java中永远不会继承,只有从类(不是接口!)到相应子类的类级注释。也就是说,即使你的注释类有一个@Inherited元注释,它对@Target({元素类型.METHOD})没有帮助,而只对@Target({元素类型.TYPE})有帮助。更新:由于我之前已经多次回答了这个问题,所以我刚刚记录了这个问题,以及使用AspectJ模拟接口和方法的注释继承中的解决方法。

那么你能做什么呢?最好的选择是在Spring应用程序中通过LTW(加载时编织)使用完整的AspectJ。这使您能够使用call()切入点,而不是由Spring AOP隐式使用的execution()。如果您在AspectJ中的方法上使用@annotation()切入点,它将同时匹配调用和执行,正如我将在一个单独的示例中展示的那样(没有Spring,但效果与Spring中的LTW AspectJ相同):

标记批注:

package de.scrum_master.app;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

虚假客户端:

这个示例客户端获取完整的StackOverflow问题页面(HTML源代码)作为字符串。

package de.scrum_master.app;

import feign.Param;
import feign.RequestLine;

public interface StackOverflowClient {
    @RequestLine("GET /questions/{questionId}")
    @MeteredRemoteCall
    String getQuestionPage(@Param("questionId") Long questionId);
}

驱动程序应用:

出于演示目的,此应用程序以三种不同的方式使用Flygn客户端界面:

    < li >不使用Feign,通过匿名子类手动实例化 < li >与#1类似,但这次在实现方法中添加了额外的标记注释 < li >通过Feign规范使用
package de.scrum_master.app;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import feign.Feign;
import feign.codec.StringDecoder;

public class Application {
    public static void main(String[] args) {
        StackOverflowClient soClient;
        long questionId = 41856687L;

        soClient = new StackOverflowClient() {
            @Override
            public String getQuestionPage(Long loanId) {
                return "StackOverflowClient without Feign";
            }
        };
        System.out.println("  " + soClient.getQuestionPage(questionId));

        soClient = new StackOverflowClient() {
            @Override
            @MeteredRemoteCall
            public String getQuestionPage(Long loanId) {
                return "StackOverflowClient without Feign + extra annotation";
            }
        };
        System.out.println("  " + soClient.getQuestionPage(questionId));

        // Create StackOverflowClient via Feign
        String baseUrl = "http://stackoverflow.com";
        soClient = Feign
            .builder()
            .decoder(new StringDecoder())
            .target(StackOverflowClient.class, baseUrl);
        Matcher titleMatcher = Pattern
            .compile("<title>([^<]+)</title>", Pattern.CASE_INSENSITIVE)
            .matcher(soClient.getQuestionPage(questionId));
        titleMatcher.find();
        System.out.println("  " + titleMatcher.group(1));
    }
}

没有方面的控制台日志:

  StackOverflowClient without Feign
  StackOverflowClient without Feign + extra annotation
  java - How to use AOP with Feign calls - Stack Overflow

如您所见,在案例#3中,它只打印了这个StackOverflow问题的标题。;-)我使用正则表达式匹配器是为了从HTML代码中提取它,因为我不想打印整个web页面。

方面:

这基本上是您的方面,带有额外的连接点日志记录。

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import de.scrum_master.app.MeteredRemoteCall;

@Aspect
public class MetricAspect {
    @Around(value = "@annotation(annotation)", argNames = "joinPoint, annotation")
    public Object meterRemoteCall(ProceedingJoinPoint joinPoint, MeteredRemoteCall annotation)
        throws Throwable
    {
        System.out.println(joinPoint);
        return joinPoint.proceed();
    }
}

控制台日志和方面:

call(String de.scrum_master.app.StackOverflowClient.getQuestionPage(Long))
  StackOverflowClient without Feign
call(String de.scrum_master.app.StackOverflowClient.getQuestionPage(Long))
execution(String de.scrum_master.app.Application.2.getQuestionPage(Long))
  StackOverflowClient without Feign + extra annotation
call(String de.scrum_master.app.StackOverflowClient.getQuestionPage(Long))
  java - How to use AOP with Feign calls - Stack Overflow

如您所见,以下连接点会为三种情况中的每一种拦截:

    < li >仅< code>call(),因为即使手动实例化,实现类也没有接口方法的注释。所以< code>execution()无法匹配。 < Li > < code > call()和< code>execution()两者,因为我们手动将标记注释添加到实现类中。 < li >仅< code>call(),因为Feign创建的动态代理没有接口方法的注释。所以< code>execution()无法匹配。

我希望这能帮助你理解发生了什么以及为什么。

底线:使用完整的AspectJ,以便让你的切入点与调用()连接点匹配。然后你的问题就解决了。

 类似资料:
  • 价值万元具备小型呼叫中心能力,适用于电话销售、客户回访、渠道管理等场景。 外呼中心开通条件 【温馨提示】 外呼中心费用与钉钉智能电话保持一致。 外呼中心价值 开通外呼中心入口 外呼中心入口 外呼中心入口 简单三步完成呼叫任务 1.创建呼叫任务 创建呼叫任务,可从外部联系人批量导入,客户跟进更有计划性。 注: 外呼时间有限制,仅限早上7:00~20:00。 可以创建10个任务,单个任务导入上限200

  • 假设我们有一段无聊的代码,我们都必须使用: 切换到Java 8后,我的IDE告诉我,我可以将此代码替换为对方付费呼叫,它会自动生成: 然而,这给了我一个错误: Steam中的collect(java.util.stream.Collector)不能应用于:(java.util.stream.Collector,capture,java.util.List) 我尝试转换参数,但它给了我未定义的和,I

  • 但是上行和下行都不起作用 当我使用上行链路或下行链路时,我的应用程序行为不端,而当我使用其他来源时,我的应用程序工作正常

  • 问题内容: 我需要从被叫方获取呼叫者信息(什么文件/什么行)。我了解到可以为此目的使用inpect模块,但不能完全使用它。 如何使用inspect获取那些信息?还是有其他方法来获取信息? 问题答案: 呼叫者的帧比当前帧高一帧。您可以用来查找呼叫者的框架。然后使用inspect.getframeinfo获取调用者的文件名和行号。

  • 1、接口声明 业务流程图: 2、接口调用 2.1、校验账号 接口说明: 接口类型:主动调用接口 接口作用:校验登录账号是否与appid符合。 请求方式: GET 请求地址: https://icall.sobot.com/api/icall/5/task/check_login_name 请求参数: 参数 类型 必填 名称 备注 appid 字符串 是 智齿分配 signature 字符串 是 智

  • 语境 我们正在Twilio中构建一个预约确认和连接应用程序,目前正在使用Studio。 它通过WhatsApp与用户通话,然后在他们准备好预约时通过正常的语音运营商服务拨打他们的电话,然后将他们的电话与呼叫中心代理连接。 null 非常感谢您的任何建议!