简体   繁体   English

带有Gradle Pointcut或Advice的AspectJ无法正常工作-不使用spring或android

[英]AspectJ with Gradle Pointcut or Advice not working - NOT using spring or android

Hi I am building a library for messaging between AWS components and so I want a lightweight solution. 嗨,我正在建立一个用于AWS组件之间消息传递的库,因此我想要一个轻量级的解决方案。 One part of the solution requires me to listen in on when an annotated method is invoked, so I thought I'd use a pointcut and implement an advice on what to do when that pointcut was reached. 解决方案的一部分要求我在调用带注释的方法时进行侦听,因此我认为我将使用切入点,并针对到达该切入点时执行的操作提供建议。

The gradle.build script looks like this: 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')
}

As you can see I included all the aspectj libs in an effort to make sure I'm not missing anything I need (feel free to tell me what I don't need). 如您所见,我包括了所有aspectj库,以确保我没有遗漏任何我需要的内容(随时告诉我我不需要的内容)。

The class I expect to get weaved is this: 我希望学习的课程是这样的:

@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);
    }
}

What I'm hoping this will do is pick up every execution (invocation) of a method annotated with @TopicSender and execute the associated processor in a thread from the threadpool. 我希望这将是采用@TopicSender注释的方法的每次执行(调用)并在线程@TopicSender的线程中执行关联的处理器。

I had a look at the decompiled class of the TopicSenderManager which is shown below: 我看了一下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;
        }

    }
}

I assume this is the weaving that is supposed to have happened. 我认为这是应该发生的编织。

The next thing is to set up a (sort of) unit test that demonstrates the behaviour that I want: 接下来的事情是设置一个(某种)单元测试,以演示我想要的行为:

Firstly I define a simple example of the sort of method that I want to intercept: 首先,我定义一个我想拦截的方法的简单示例:

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
    }
}

Then the test that looks at it: 然后查看它的测试:

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()));
    }
}

Basically, everything up to the // now did it execute... line works, but everything is null after that. 基本上, // now did it execute...所有内容都// now did it execute...行,但是此后所有内容都为null。 So it looks like the pointcut is never reached. 因此,似乎切入点从未达到。

So, is the weaving not working correctly? 那么,编织是否正常工作? is the pointcut and advice in the @Around spec not correct? @Around规范中的切入点和建议不正确? is there some kind of runtime trigger, I have forgotten about? 我已经忘记了某种运行时触发器吗? or is it something else? 或者是别的什么?

I thought I should make a statement here: the reason I'm not using spring is that I have found that the number of dependencies leads to a really bloated library and I'm trying to keep it as trim as possible. 我认为我应该在这里声明:我不使用spring的原因是,我发现依赖项的数量导致了一个真正肿的库,并且我试图尽可能地减少它。 I want the speed benefits of aspectJ and i would prefer compile time weaving because this is a library to be used in applications I will resort to spring if I have to, but it really is a last resort. 我想获得AspectJ的速度优势,并且我更喜欢编译时间编织,因为这是要在我必须使用spring的应用程序中使用的库,但这确实是不得已的方法。

The problem was in the something else category. 问题出在其他类别。 The reason the Pointcut was not being triggered was because I was using compile-time weaving, and the weaved target class was not in the classpath at runtime. 没有触发Pointcut的原因是因为我使用的是编译时编织,并且运行时编织的目标类不在类路径中。 The weaved target class was in src/test/java, while the pointcut was in src/main/java and when the application is compiled, src/test/java is no where in sight. 编织的目标类位于src / test / java中,而切入点位于src / main / java中,并且在编译应用程序时,看不到src / test / java。 What I needed was runtime weaving, so that the test class could be discovered and weaved, and then the pointcut would work. 我需要的是运行时编织,以便可以发现并编织测试类,然后切入点将起作用。 Unfortunately, runtime weaving is an expensive operation, so not an option for my purposes, so I have gone over to using listener pattern. 不幸的是,运行时编织是一项昂贵的操作,因此对于我而言不是一个选择,因此我转而使用侦听器模式。 Its not as clean from an implementors perspective, but much better performance-wise 从实现者的角度来看,它不是那么干净,但是在性能方面要好得多

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM