【问题标题】:AspectJ with Gradle Pointcut or Advice not working - NOT using spring or android带有 Gradle Pointcut 或 Advice 的 AspectJ 不起作用 - 不使用 spring 或 android
【发布时间】:2018-07-31 21:27:14
【问题描述】:

您好,我正在构建一个用于在 AWS 组件之间进行消息传递的库,因此我想要一个轻量级的解决方案。解决方案的一部分要求我在调用带注释的方法时进行监听,所以我想我会使用一个切入点并实现一个关于在到达该切入点时要做什么的建议。

gradle.build 脚本如下所示:

buildscript {
    ext {
        // some company-specific config here
        nexus = {
            credentials {
                username nexusBuildUserToken
                password nexusBuildPassToken
            }
            url nexusRepoURL
        }
    }

    repositories {
        mavenCentral()
        maven(nexus)
    }


    dependencies {
        classpath("net.researchgate:gradle-release:$gradleReleasePluginVersion")
        classpath("gradle.plugin.aspectj:gradle-aspectj:$gradleAspectJPluginVersion")

    }
}

apply plugin: 'java'

// IDE
apply plugin: 'idea'
apply plugin: 'eclipse-wtp'

apply plugin: "aspectj.gradle"

jar {
    enabled = true
}

// project artifact info
group = groupId
archivesBaseName = artifactId

repositories {
    mavenCentral()
    maven(nexus)
    maven {
        url "https://dl.bintray.com/findify/maven"
    }

}

dependencies {

    compile("org.aspectj:aspectjtools:$aspectjVersion")

    compile("org.apache.commons:commons-lang3:${commonsLang3Version}")
    compile("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${jacksonDataformatYamlVersion}")

    compile("org.elasticmq:elasticmq-rest-sqs_2.11:0.14.1")
    compile("com.amazonaws:aws-java-sdk-sqs:${awsMessagingVersion}")
    compile("com.amazonaws:aws-java-sdk-sns:${awsMessagingVersion}")
    compile("au.com.auspost:json-encryption:${jsonEncryptionVersion}")

    compile("org.apache.commons:commons-lang3:${commonsLang3Version}")
    compile("org.reflections:reflections:${reflectionsVersion}")
    compile("redis.clients:jedis:${jedisVersion}")
    compile("org.aspectj:aspectjweaver:$aspectjVersion")
    compile("org.aspectj:aspectjrt:$aspectjVersion")

    testCompile("junit:junit:${jUnitVersion}")
    testCompile("org.mockito:mockito-core:${mockitoCoreVersion}")
    testCompile("org.assertj:assertj-core:${assertjVersion}")
    testCompile("ai.grakn:redis-mock:${embeddedRedisVersion}")
    testCompile("org.slf4j:slf4j-simple:1.7.25")
    testCompile("ch.qos.logback:logback-core:1.2.3")
    testCompile(group: 'io.findify', name: 'sqsmock_2.11', version: '0.3.2')
}

如您所见,我包含了所有 aspectj 库,以确保我没有遗漏任何我需要的东西(请随时告诉我我不需要什么)。

我希望编织的课程是这样的:

@Aspect
public class TopicSenderManager {

    private ThreadPoolFactory threadPoolFactory;

    private CorrelationService correlationService = new CorrelationService.Default();

    private Map<String, TopicSenderProcessor> topicSenderProcessors = new HashMap<>();


    private ExecutorService executor;

    public TopicSenderManager(Map<String, TopicSenderProcessor> topicSenderProcessors) {
        this.threadPoolFactory = new ThreadPoolFactory.Default();
        this.topicSenderProcessors = topicSenderProcessors;
        executor = threadPoolFactory.create(topicSenderProcessors.size(), "TopicSender");
    }

    @Around("@annotation(topicSender) && execution(* *(..))")
    public Object sendMessageToTopic(ProceedingJoinPoint pjp, TopicSender topicSender) throws Throwable {

        TopicSenderProcessor topicSenderProcessor = getProcessor(topicSender.topicAlias(), topicSender.eventType());
        topicSenderProcessor.setCorrelationId(correlationService.read());
        Object[] args = pjp.getArgs();
        if (args == null || args.length != 1) {
            throw new Exception("naughty, naughty");
        } else {
            topicSenderProcessor.setEventMessage((EventMessage) args[0]);
            executor.execute(topicSenderProcessor);
        }    
        return pjp.proceed();
    }

    public Map<String, TopicSenderProcessor> getTopicSenderProcessors() {
        return topicSenderProcessors;
    }

    public TopicSenderProcessor getProcessor(String topic, String eventType) {
        String senderKey = topic + "," + eventType;
        return topicSenderProcessors.get(senderKey);
    }
}

我希望这能做的是提取带有@TopicSender 注释的方法的每次执行(调用),并在线程池中的线程中执行关联的处理器。

我查看了 TopicSenderManager 的反编译类,如下所示:

@Aspect
public class TopicSenderManager {
    private ThreadPoolFactory threadPoolFactory = new Default();
    private CorrelationService correlationService = new au.com.auspost.messaging.CorrelationService.Default();
    private Map<String, TopicSenderProcessor> topicSenderProcessors = new HashMap();
    private ExecutorService executor;

    public TopicSenderManager(Map<String, TopicSenderProcessor> topicSenderProcessors) {
        this.topicSenderProcessors = topicSenderProcessors;
        this.executor = this.threadPoolFactory.create(topicSenderProcessors.size(), "TopicSender");
    }

    @Around("@annotation(topicSender) && execution(* *(..))")
    public Object sendMessageToTopic(ProceedingJoinPoint pjp, TopicSender topicSender) throws Throwable {
        TopicSenderProcessor topicSenderProcessor = this.getProcessor(topicSender.topicAlias(), topicSender.eventType());
        topicSenderProcessor.setCorrelationId(ajc$inlineAccessFieldGet$au_com_auspost_messaging_send_topic_TopicSenderManager$au_com_auspost_messaging_send_topic_TopicSenderManager$correlationService(this).read());
        Object[] args = pjp.getArgs();
        if (args != null && args.length == 1) {
            topicSenderProcessor.setEventMessage((EventMessage)args[0]);
            ajc$inlineAccessFieldGet$au_com_auspost_messaging_send_topic_TopicSenderManager$au_com_auspost_messaging_send_topic_TopicSenderManager$executor(this).execute(topicSenderProcessor);
            return pjp.proceed();
        } else {
            throw new Exception("naughty, naughty");
        }
    }

    public Map<String, TopicSenderProcessor> getTopicSenderProcessors() {
        return this.topicSenderProcessors;
    }

    public TopicSenderProcessor getProcessor(String topic, String eventType) {
        String senderKey = topic + "," + eventType;
        return (TopicSenderProcessor)this.topicSenderProcessors.get(senderKey);
    }

    public static TopicSenderManager aspectOf() {
        if (ajc$perSingletonInstance == null) {
            throw new NoAspectBoundException("au.com.auspost.messaging.send.topic.TopicSenderManager", ajc$initFailureCause);
        } else {
            return ajc$perSingletonInstance;
        }
    }

    public static boolean hasAspect() {
        return ajc$perSingletonInstance != null;
    }

    static {
        try {
            ajc$postClinit();
        } catch (Throwable var1) {
            ajc$initFailureCause = var1;
        }

    }
}

我认为这是应该发生的编织。

接下来要做的是设置一个(某种)单元测试来演示我想要的行为:

首先,我定义了一个我想要拦截的方法的简单示例:

public class SenderT1E1 {

    @TopicSender(topicAlias = "t1", eventType = "a.c.a.e1")
    public void aSendingMethod(EventMessage<TestMessage> eventMessage) {
        // do what you like before sending the message
    }
}

然后是看它的测试:

public class SenderE1T1Test {

    static TopicSenderManager manager;

    @BeforeClass
    public static void setUp() throws Exception {

        MockAmazonClient snsClient = new MockAmazonClient();

        TopicSenderFactory senderFactory = new TopicSenderFactory();

        PublishResult publishResult = new PublishResult().withMessageId("E1T1");

        manager = senderFactory.make(snsClient.amazonSNS(publishResult), "aws-send-test.properties");
    }

    @Test
    public void whenSenderIsCalledMessageIsSent() {
        SenderT1E1 target = new SenderT1E1();

        EventMessage message = new EventMessage<>();
        message.setEventType("a.c.a.e1");
        message.setPayload(new TestMessage());

        target.aSendingMethod(message);

        TopicSenderProcessor processor = manager.getProcessor("http://localhost:8001/topic/t1", "a.c.a.e1");

        assertThat(manager.getTopicSenderProcessors().entrySet().size(), is(2));
        manager.getTopicSenderProcessors().forEach((k,v) -> System.out.println(k + ",  " + v));

        assertThat(processor, is(notNullValue()));

        // now, did it execute...
        assertThat(processor.getEventMessage(), is(notNullValue()));
        assertThat(processor.getLastPublishRequest(), is(notNullValue()));
        assertThat(processor.getLastPublishResult(), is(notNullValue()));
    }
}

基本上,// now did it execute... 行之前的所有内容都可以正常工作,但在那之后所有内容都为空。所以看起来永远无法到达切入点。

那么,编织是否工作不正常? @Around 规范中的切入点和建议是否不正确?是否有某种运行时触发器,我忘记了?还是别的什么?

我想我应该在这里声明一下:我不使用的原因 spring 是我发现依赖的数量导致了 真的很臃肿的图书馆,我正试图让它尽可能地精简。 我想要 aspectJ 的速度优势,我更喜欢编译时间 编织,因为这是一个要在应用程序中使用的库,我将 万不得已就去春天,但确实是不得已而为之。

【问题讨论】:

  • 这是很多代码,但是您的 Gradle 脚本中缺少依赖版本,因此无法构建。顺便说一句,你反编译方面的目的是什么?如果您想查看是否发生了编织,请反编译目标类,如SenderT1E1。但是配置 AspectJ 编译器以通过-showWeaveInfo 显示它在哪里编织会更容易,即使该插件完全没有记录并且我不知道如何配置它。顺便说一句,我以前也从未使用过 Gradle,我将 AspectJ 与 Maven 一起使用。您也可以查看this plugin
  • (续) 是的,当然你在使用 AspectJ 时不需要 Spring,两者完全不相关。您可以发布一个MCVE 而不依赖于您的内部 Nexus 并且在 GitHub 上重现该问题的最少代码吗?如果可以的话,我会看看并帮助您解决它。我还为你找到了this,也许对你有帮助。

标签: java aspectj gradle-plugin pointcut


【解决方案1】:

问题出在其他类别中。没有触发切入点的原因是因为我使用的是编译时编织,而编织的目标类在运行时不在类路径中。 编织的目标类在 src/test/java 中,而切入点在 src/main/java 中,并且在编译应用程序时,src/test/java 是看不见的。我需要的是运行时编织,以便可以发现和编织测试类,然后切入点起作用。 不幸的是,运行时编织是一项昂贵的操作,所以不是我的选择,所以我转而使用侦听器模式。从实现者的角度来看,它并不那么干净,但在性能方面要好得多

【讨论】:

    猜你喜欢
    • 2015-09-30
    • 1970-01-01
    • 1970-01-01
    • 2015-12-17
    • 1970-01-01
    • 1970-01-01
    • 2021-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多