Short-circuit methods for lambdas from annotation (#2823)

* Short-circuit methods for lambdas from annotation

When we have a `@ServiceActivator` or any other messaging annotations
on `Function` or `Consumer` `@Bean`s, there is a restriction when we
can't use lambdas because of target method argument type erasure in Java.

* Use a `@Bean` method return to determine the target function argument
type and wrap the call into the `LambdaMessageProcessor`.

In this case we call the target method directly after possible payload
conversion according expected generic type for `Function` or `Consumer`.
There is just no reason to go a `MessagingMethodInvokerHelper` route
for this lambda variants

* Apply the short-circuit algorithm for Kotlin lambdas as well
* Make some refactoring and improvements to `ClassUtils`
if favor or similar API in the SF `ClassUtils`
* Fix `No beanFactory` warning for the `ExpressionCommandMessageProcessor`

* * Resolve Checkstyle violations

* * Fix `resolveAttributeToBoolean()` argument name to be generic
* Fix typo in the exception message for `IntegrationFlowDefinition.get()`

* * Revert `ClassUtils.resolvePrimitiveType()` logic: an existing in SF
does exactly opposite one
This commit is contained in:
Artem Bilan
2019-03-27 21:18:33 -04:00
committed by Gary Russell
parent a599881a8e
commit 572dc0ec14
14 changed files with 270 additions and 169 deletions

View File

@@ -349,8 +349,7 @@ public class MessagingAnnotationsWithBeanAnnotationTests {
@Filter(inputChannel = "skippedChannel5")
@Profile("foo")
public MessageHandler skippedMessageHandler() {
return m -> {
};
return m -> { };
}
@Bean
@@ -384,14 +383,7 @@ public class MessagingAnnotationsWithBeanAnnotationTests {
@Bean
@ServiceActivator(inputChannel = "functionMessageServiceChannel")
public Function<Message<String>, String> messageFunctionAsService() {
return new Function<Message<String>, String>() { // Has to be interface for proper type inferring
@Override
public String apply(Message<String> m) {
return m.getPayload().toLowerCase();
}
};
return (message) -> message.getPayload().toLowerCase();
}
@Bean
@@ -408,14 +400,7 @@ public class MessagingAnnotationsWithBeanAnnotationTests {
@Bean
@ServiceActivator(inputChannel = "messageConsumerServiceChannel")
public Consumer<Message<?>> messageConsumerAsService() {
return new Consumer<Message<?>>() { // Has to be interface for proper type inferring
@Override
public void accept(Message<?> e) {
collector().add(e);
}
};
return collector()::add;
}
}

View File

@@ -16,7 +16,6 @@
package org.springframework.integration.function
import assertk.all
import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isNotNull
@@ -24,8 +23,10 @@ import assertk.assertions.isTrue
import assertk.assertions.size
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.integration.annotation.EndpointId
import org.springframework.integration.annotation.InboundChannelAdapter
import org.springframework.integration.annotation.Poller
import org.springframework.integration.annotation.ServiceActivator
@@ -37,6 +38,7 @@ import org.springframework.integration.dsl.IntegrationFlows
import org.springframework.integration.endpoint.SourcePollingChannelAdapter
import org.springframework.messaging.Message
import org.springframework.messaging.MessageChannel
import org.springframework.messaging.PollableChannel
import org.springframework.messaging.SubscribableChannel
import org.springframework.messaging.support.GenericMessage
import org.springframework.messaging.support.MessageBuilder
@@ -44,9 +46,7 @@ import org.springframework.test.annotation.DirtiesContext
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig
import java.util.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.function.Supplier
/**
* @author Artem Bilan
@@ -70,8 +70,12 @@ class FunctionsTests {
private lateinit var counterChannel: SubscribableChannel
@Autowired
@Qualifier("kotlinSupplierChannelAdapter")
private lateinit var kotlinSupplierInboundChannelAdapter: SourcePollingChannelAdapter
@Autowired
private lateinit var fromSupplierQueue: PollableChannel
@Test
fun `invoke function via transformer`() {
val replyChannel = QueueChannel()
@@ -111,6 +115,11 @@ class FunctionsTests {
assertThat(countDownLatch.await(10, TimeUnit.SECONDS)).isTrue()
}
@Test
fun `verify supplier flow`() {
assertThat(this.fromSupplierQueue.receive(10_000)).isNotNull()
}
@Configuration
@EnableIntegration
class Config {
@@ -136,10 +145,23 @@ class FunctionsTests {
@Bean
@InboundChannelAdapter(value = "counterChannel", autoStartup = "false",
poller = [Poller(fixedRate = "10", maxMessagesPerPoll = "1")])
@EndpointId("kotlinSupplierChannelAdapter")
fun kotlinSupplier(): () -> String {
return { "baz" }
}
@Bean
fun flowFromSupplier() =
IntegrationFlows.from<String>({ "bar" },
{ e ->
e.poller { p ->
p.fixedDelay(10)
.maxMessagesPerPoll(1)
}
})
.channel { c -> c.queue("fromSupplierQueue") }
.get()
}
}