在这里,我们将给大家分享关于SpringIntegration中的REST端点使消息传递通道成为多线程的知识,让您更了解springrestdoc的本质,同时也会涉及到如何更有效地JavaDSLfor
在这里,我们将给大家分享关于Spring Integration中的REST端点使消息传递通道成为多线程的知识,让您更了解spring restdoc的本质,同时也会涉及到如何更有效地Java DSL for Spring Integration 1.2 Milestone 2、Latest Jackson integration improvements in Spring、org.springframework.boot.actuate.metrics.integration.SpringIntegrationMetricReader的实例源码、org.springframework.boot.test.IntegrationTest的实例源码的内容。
本文目录一览:- Spring Integration中的REST端点使消息传递通道成为多线程(spring restdoc)
- Java DSL for Spring Integration 1.2 Milestone 2
- Latest Jackson integration improvements in Spring
- org.springframework.boot.actuate.metrics.integration.SpringIntegrationMetricReader的实例源码
- org.springframework.boot.test.IntegrationTest的实例源码
Spring Integration中的REST端点使消息传递通道成为多线程(spring restdoc)
我有一个非常简单的Spring
Boot应用程序,该应用程序提供了几个宁静的终结点,并且应该可以驱动将sftp文件上传到sftp服务器。我的要求是,如果有多个文件,则文件应排队。我希望通过sftp
spring集成工作流的默认行为来实现这一目标,因为我读到DirectChannel自动将文件排队。要测试该行为,请执行以下操作:
- 发送一个大文件,通过调用端点来阻塞通道一段时间。
- 通过调用端点来发送较小的文件。
预期结果:较小的文件将排队到通道上,并在较大文件的上传完成后进行处理。实际结果:打开与sftp服务器的新连接,较小的文件上传到那里而无需排队,而较大的文件继续传输。
我的应用程序中有两个文件:
DemoApplication.java
@SpringBootApplication@IntegrationComponentScan@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @Bean public SessionFactory<LsEntry> sftpSessionFactory() { DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true); factory.setHost("localhost"); factory.setPort(22); factory.setUser("tester"); factory.setPassword("password"); factory.setAllowUnknownKeys(true); return factory; } @Bean @ServiceActivator(inputChannel = "toSftpChannel") public MessageHandler handler() { SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory()); handler.setRemoteDirectoryExpression(new LiteralExpression("/")); return handler; } @MessagingGateway public interface MyGateway { @Gateway(requestChannel = "toSftpChannel") void sendToSftp(File file); }}
DemoController.java
@RestControllerpublic class DemoController { @Autowired MyGateway gateway; @RequestMapping("/sendFile") public void sendFile() { File file = new File("C:/smallFile.txt"); gateway.sendToSftp(file); } @RequestMapping("/sendBigFile") public void sendBigFile() { File file = new File("D:/bigFile.zip"); gateway.sendToSftp(file); }}
我是一个完全的新手,我不能完全确定我的sftp通道是否在这里正确创建,我的猜测是每次我执行sendToSftp调用时都会创建一个新的sftp通道。在这种情况下,如何实现队列行为的任何帮助将不胜感激。
答案1
小编典典您在这里没有队列,因为每个HTTP请求都是在其自己的线程中执行的。正确,http线程池用尽时,您可能仍然在那儿排队,但是在您只有两个请求的简单用例中,这似乎并不存在。
无论如何,您都可以在那里实现队列行为,但是您应该将自己声明toSftpChannel
为QueueChannel
Bean。
这样,下游进程将始终在同一线程上执行,并且下一个消息恰好在第一个消息之后从队列中拉出。
有关更多信息,请参见参考手册。
更新
由于您使用的FtpMessageHandler
是单向组件,但是您仍然需要对MVC控制器的方法进行一些答复,因此,唯一的方法就是拥有一个不返回的@Gateway
方法void
,当然,我们需要以某种方式发送答复。
为此,我建议使用PublishSubscribeChannel
:
@Bean@BridgeTopublic MessageChannel toSftpChannel() { return new PublishSubscribeChannel();}@Bean@ServiceActivator(inputChannel = "toSftpChannel")@Order(0)public MessageHandler handler() { SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory()); handler.setRemoteDirectoryExpression(new LiteralExpression("/")); return handler;}
这样,我们就有两个订阅者toSftpChannel
。使用,@Order(0)
我们确保@ServiceActivator
是第一个订户,因为我们需要首先执行SFTP传输。用,@BridgeTo
我们BridgeHandler
在相同的位置增加一秒钟PublishSubscribeChannel
。它的目的只是获取replyChannel
标头,然后在其中发送请求消息。由于我们不使用任何线程,因此BridgeHandler
将在完成向SFTP的传输后立即执行。
当然,BridgeHandler
除了您之外,您还可以拥有其他任何东西,@ServiceActivator
或者@Transfromer
返回其他信息而不是请求File
。例如:
@ServiceActivator(inputChannel = "toSftpChannel")@Order(1)public String transferComplete(File payload) { return "The SFTP transfer complete for file: " + payload;}
Java DSL for Spring Integration 1.2 Milestone 2
Java DSL for Spring Integration 1.2 Milestone 2 发布了,用于集成Spring的Java DSL。(至于什么是DSL,这个翻译成中文称作领域特定语言,具体的话请各位自行使用搜索引擎查找吧,我也爱莫能助了。)
目前迭代更新的一些亮点:
JPA support
@Autowired private EntityManagerFactory entityManagerFactory; @Bean public IntegrationFlow pollingAdapterFlow() { return IntegrationFlows .from(Jpa.inboundAdapter(this.entityManagerFactory) .entityClass(StudentDomain.class) .maxResults(1) .expectSingleResult(true), e -> e.poller(p -> p.trigger(new OnlyOnceTrigger()))) .channel(c -> c.queue("pollingResults")) .get(); } @Bean public IntegrationFlow updatingGatewayFlow() { return f -> f .handle(Jpa.updatingGateway(this.entityManagerFactory), e -> e.transactional(true)) .channel(c -> c.queue("persistResults")); } @Bean public IntegrationFlow retrievingGatewayFlow() { return f -> f .handle(Jpa.retrievingGateway(this.entityManagerFactory) .jpaQuery("from Student s where s.id = :id") .expectSingleResult(true) .parameterExpression("id", "payload")) .channel(c -> c.queue("retrieveResults")); }
Mid-flow transaction support
Scatter-Gather support
More routers improvements
点击查看提交记录以了解更多
发行说明:
https://spring.io/blog/2016/09/15/java-dsl-for-spring-integration-1-2-milestone-2-is-available
下载地址:
Source code (zip)
Source code (tar.gz)
Latest Jackson integration improvements in Spring
Spring Jackson support has been improved lately to be more flexible and powerful. This blog post gives you an update about the most useful Jackson related features available in Spring Framework 4.x and Spring Boot. All the code samples are coming from this spring-jackson-demo sample application, feel free to have a look at the code.
JSON Views
It can sometimes be useful to filter contextually objects serialized to the HTTP response body. In order to provide such capabilities, Spring MVC now has builtin support for Jackson’s Serialization Views.
The following example illustrates how to use@JsonViewto filter fields depending on the context of serialization - e.g. getting a "summary" view when dealing with collections, and getting a full representation when dealing with a single resource:
public class View { interface Summary {}
} public class User { @JsonView(View.Summary.class) private Long id; @JsonView(View.Summary.class) private String firstname; @JsonView(View.Summary.class) private String lastname; private String email; private String address; private String postalCode; private String city; private String country;
} public class Message { @JsonView(View.Summary.class) private Long id; @JsonView(View.Summary.class) private LocalDate created; @JsonView(View.Summary.class) private String title; @JsonView(View.Summary.class) private User author; private List<User> recipients; private String body;
}
Thanks to Spring MVC@JsonViewsupport, it is possible to choose, on a per handler method basis, which field should be serialized:
@RestController public class MessageController { @Autowired private MessageService messageService; @JsonView(View.Summary.class) @RequestMapping("/") public List<Message> getAllMessages() { return messageService.getAll();
} @RequestMapping("/{id}") public Message getMessage(@PathVariable Long id) { return messageService.get(id);
}
}
In this example, if all messages are retrieved, only the most important fields are serialized thanks to thegetAllMessages()method annotated with@JsonView(View.Summary.class):
[ { "id" : 1, "created" : "2014-11-14", "title" : "Info", "author" : { "id" : 1, "firstname" : "Brian", "lastname" : "Clozel" }
}, { "id" : 2, "created" : "2014-11-14", "title" : "Warning", "author" : { "id" : 2, "firstname" : "Stéphane", "lastname" : "Nicoll" }
}, { "id" : 3, "created" : "2014-11-14", "title" : "Alert", "author" : { "id" : 3, "firstname" : "Rossen", "lastname" : "Stoyanchev" }
} ]
In Spring MVC default configuration,MapperFeature.DEFAULT_VIEW_INCLUSIONis set tofalse. That means that when enabling a JSON View, non annotated fields or properties likebodyorrecipientsare not serialized.
When a specificMessageis retrieved using thegetMessage()handler method (no JSON View specified), all fields are serialized as expected:
{ "id" : 1, "created" : "2014-11-14", "title" : "Info", "body" : "This is an information message", "author" : { "id" : 1, "firstname" : "Brian", "lastname" : "Clozel", "email" : "bclozel@pivotal.io", "address" : "1 Jaures street", "postalCode" : "69003", "city" : "Lyon", "country" : "France" }, "recipients" : [ { "id" : 2, "firstname" : "Stéphane", "lastname" : "Nicoll", "email" : "snicoll@pivotal.io", "address" : "42 Obama street", "postalCode" : "1000", "city" : "Brussel", "country" : "Belgium" }, { "id" : 3, "firstname" : "Rossen", "lastname" : "Stoyanchev", "email" : "rstoyanchev@pivotal.io", "address" : "3 Warren street", "postalCode" : "10011", "city" : "New York", "country" : "USA" } ]
}
Only one class or interface can be specified with the@JsonViewannotation, but you can use inheritance to represent JSON View hierarchies (if a field is part of a JSON View, it will be also part of parent view). For example, this handler method will serialize fields annotated with@JsonView(View.Summary.class)and @JsonView(View.SummaryWithRecipients.class):
public class View { interface Summary {} interface SummaryWithRecipients extends Summary {}
} public class Message { @JsonView(View.Summary.class) private Long id; @JsonView(View.Summary.class) private LocalDate created; @JsonView(View.Summary.class) private String title; @JsonView(View.Summary.class) private User author; @JsonView(View.SummaryWithRecipients.class) private List<User> recipients; private String body;
}
@RestController public class MessageController { @Autowired private MessageService messageService; @JsonView(View.SummaryWithRecipients.class) @RequestMapping("/with-recipients") public List<Message> getAllMessagesWithRecipients() { return messageService.getAll();
}
}
JSON Views could also be specified when usingRestTemplateHTTP client orMappingJackson2JsonViewby wrapping the value to serialize in aMappingJacksonValueas shown in this code sample.
JSONP
As described in the reference documentation, you can enable JSONP for@ResponseBodyandResponseEntitymethods by declaring an@ControllerAdvicebean that extendsAbstractJsonpResponseBodyAdviceas shown below:
@ControllerAdvice public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { public JsonpAdvice() { super("callback");
}
}
With such@ControllerAdvicebean registered, it will be possible to request the JSON webservice from another domain using a<script />tag:
<script type="application/javascript" src="http://mydomain.com/1.json?jsonp=parseResponse"> </script>
In this example, the received payload would be:
parseResponse({ "id" : 1, "created" : "2014-11-14",
...
});
JSONP is also supported and automatically enabled when usingMappingJackson2JsonViewwith a request that has a query parameter named jsonp or callback. The JSONP query parameter name(s) could be customized through thejsonpParameterNamesproperty.
XML support
Since 2.0 release, Jackson provides first class support for some other data formats than JSON. Spring Framework and Spring Boot provide builtin support for Jackson based XML serialization/deserialization.
As soon as you include the jackson-dataformat-xml dependency to your project, it is automatically used instead of JAXB2.
Using Jackson XML extension has several advantages over JAXB2:
- Both Jackson and JAXB annotations are recognized
- JSON View are supported, allowing you to build easily REST Webservices with the same filtered output for both XML and JSON data formats
- No need to annotate your class with@XmlRootElement, each class serializable in JSON will serializable in XML
You usually also want to make sure that the XML library in use is Woodstox since:
- It is faster than Stax implementation provided with the JDK
- It avoids some known issues like adding unnecessary namespace prefixes
- Some features like pretty print don''t work without it
In order to use it, simply add the latest woodstox-core-asl dependency available to your project.
Customizing the Jackson ObjectMapper
Prior to Spring Framework 4.1.1, JacksonHttpMessageConverters were usingObjectMapperdefault configuration. In order to provide a better and easily customizable default configuration, a new Jackson2ObjectMapperBuilder has been introduced. It is the JavaConfig equivalent of the well known Jackson2ObjectMapperFactoryBean used in XML configuration.
Jackson2ObjectMapperBuilder provides a nice API to customize various Jackson settings while retaining Spring Framework provided default ones. It also allows to createObjectMapperandXmlMapperinstances based on the same configuration.
BothJackson2ObjectMapperBuilderandJackson2ObjectMapperFactoryBeandefine a better Jackson default configuration. For example, the DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES property set to false, in order to allow deserialization of JSON objects with unmapped properties.
Jackson support for Java 8 Date & Time API data types is automatically registered when Java 8 is used and jackson-datatype-jsr310 is on the classpath. Joda-Time support is registered as well when jackson-datatype-joda is part of your project dependencies.
These classes also allow you to register easily Jackson mixins, modules, serializers or even property naming strategy likePropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORESif you want to have youruserNamejava property translated touser_namein JSON.
With Spring Boot
As described in the Spring Boot reference documentation, there are various ways to customize the JacksonObjectMapper.
You can for example enable/disable Jackson features easily by adding properties likespring.jackson.serialization.indent_output=trueto application.properties.
As an alternative, in the upcoming 1.2 release Spring Boot also allows to customize the Jackson configuration (JSON and XML) used by Spring MVCHttpMessageConverters by declaring aJackson2ObjectMapperBuilder@Bean:
@Bean public Jackson2ObjectMapperBuilder jacksonBuilder() { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd")); return builder;
}
This is useful if you want to use advanced Jackson configuration not exposed through regular configuration keys.
Without Spring Boot
In a plain Spring Framework application, you can also useJackson2ObjectMapperBuilderto customize the XML and JSONHttpMessageConverters as shown bellow:
@Configuration @EnableWebMvc public class WebConfiguration extends WebMvcConfigurerAdapter { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
}
}
More to come
With the upcoming Spring Framework 4.1.3 release, thanks to the addition of a Spring context aware HandlerInstantiator (see SPR-10768 for more details), you will be able to autowire Jackson handlers (serializers, deserializers, type and type id resolvers).
This will allow you to build, for example, a custom deserializer that will replace a field containing only a reference in the JSON payload by the fullEntityretrieved from the database.
org.springframework.boot.actuate.metrics.integration.SpringIntegrationMetricReader的实例源码
@Bean @ConditionalOnMissingBean(name = "springIntegrationPublicmetrics") public MetricReaderPublicmetrics springIntegrationPublicmetrics( IntegrationMBeanExporter exporter) { return new MetricReaderPublicmetrics( new SpringIntegrationMetricReader(exporter)); }
@Bean @ConditionalOnMissingBean(name = "springIntegrationPublicmetrics") public MetricReaderPublicmetrics springIntegrationPublicmetrics( IntegrationMBeanExporter exporter) { return new MetricReaderPublicmetrics( new SpringIntegrationMetricReader(exporter)); }
@Bean @ConditionalOnMissingBean public MetricReaderPublicmetrics springIntegrationPublicmetrics( IntegrationMBeanExporter exporter) { return new MetricReaderPublicmetrics( new SpringIntegrationMetricReader(exporter)); }
org.springframework.boot.test.IntegrationTest的实例源码
@Test @Category(IntegrationTest.class) public void testGetPollableTask() throws Exception { String pollableTaskName = "testGetPollableTask"; PollableTask parentTask = pollableTaskService.createPollableTask(null,pollableTaskName,null,0); com.Box.l10n.mojito.rest.entity.PollableTask pollableTask = pollableTaskClient.getPollableTask(parentTask.getId()); assertEquals(pollableTaskName,pollableTask.getName()); assertNull(pollableTask.getFinishedDate()); assertFalse(pollableTask.isAllFinished()); }
/** * Configures the context in such a way that the service registy server has a * new unique port,and all provided application contexts are configured to * use it. It also copies properites provided in {@link RatelTest#value()} and * {@link IntegrationTest#value()} annotations. * * @param testContext * the TestContext of a test */ @Override public void prepareTestInstance(TestContext testContext) { applyPropertiesFromAnnotation(testContext,IntegrationTest.class.getName()); applyPropertiesFromAnnotation(testContext,RatelTest.class.getName()); int servicePort = RatelTestContext.getServicediscoveryPort(); // hardcoded,can // be changed to // finding free // tcp port applyServiceRegistryProperties(testContext,servicePort); }
private void addIntegrationTestProperty(Collection<String> propertySourceProperties) { propertySourceProperties.add(IntegrationTest.class.getName() + "=true"); }
今天的关于Spring Integration中的REST端点使消息传递通道成为多线程和spring restdoc的分享已经结束,谢谢您的关注,如果想了解更多关于Java DSL for Spring Integration 1.2 Milestone 2、Latest Jackson integration improvements in Spring、org.springframework.boot.actuate.metrics.integration.SpringIntegrationMetricReader的实例源码、org.springframework.boot.test.IntegrationTest的实例源码的相关知识,请在本站进行查询。
本文标签: