我正在尝试在spring-integration
中实现RSS / Atom提要聚合器,我主要使用Java DSL来编写IntegrationFlow
。此聚合器的要求是可以在运行时添加/删除提要。也就是说,在设计时,饲料未知。
我发现将基本Feed.inboundAdapter()
与测试网址一起使用很简单,然后使用变换器从Feed中提取链接,然后将其传递给outbound-file-adapter
以保存指向文件的链接。但是,当我尝试通过inbound-file-adapter
从FileSplitter
运行文件中读取(数千)Feed网址时,我已经陷入困境,然后将包含Feed网址的每个结果Message<String>
传递给然后注册一个新的Feed.inboundAdapter()
。这对Java DSL来说不可能吗?
理想情况下,如果我能做到以下情况,我会喜欢它:
@Bean
public IntegrationFlow getFeedsFromFile() throws MalformedURLException {
return IntegrationFlows.from(inboundFileChannel(), e -> e.poller(Pollers.fixedDelay(10000)))
.handle(new FileSplitter())
//register new Feed.inboundAdapter(payload.toString()) foreach Message<String> containing feed url coming from FileSplitter
.transform(extractLinkFromFeedEntry())
.handle(appendLinkToFile())
.get();
}
虽然经过多次阅读弹簧集成java DSL代码(并沿途学习了大量的东西)后,我却看不出这样做是可行的。那么...... A)是吗? B)应该是吗? C)建议?
几乎感觉我应该能够获取.handle(new FileSplitter())
的输出并将其传递给.handleWithAdapter(Feed.inboundAdapter(/*stuff here*/))
,但DSL仅在那里引用outbound-adapter
。入站适配器实际上只是AbstractMessageSource
的子类,似乎唯一位置可以指定其中一个作为IntegrationFlows.from(/*stuff here*/)
方法的参数。
我原本以为可以从文件中获取输入,逐行拆分,使用该输出注册入站Feed适配器,轮询这些Feed,从Feed中提取新链接并附加它们到一个文件。它看起来好像不是。
是否有一些聪明的子类我可以做这个工作?
失败了...我怀疑这将是答案,我发现Spring集成Dynamic Ftp Channel Resolver Example和this回答如何适应它动态注册入站案例的东西..
这是要走的路吗?任何帮助/指导表示赞赏。在倾听了DSL代码和阅读文档几天之后,我想我将实现动态ftp示例并使其适应FeedEntryMessageSource ......在这种情况下我的问题是......动态ftp示例有效使用XML配置,但是可以使用Java配置或Java DSL吗?
更新
我已经按如下方式实施了解决方案:
@SpringBootApplication
class MonsterFeedApplication {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext parent = SpringApplication.run(MonsterFeedApplication.class, args);
parent.setId("parent");
String[] feedUrls = {
"https://1nichi.wordpress.com/feed/",
"http://jcmuofficialblog.com/feed/"};
List<ConfigurableApplicationContext> children = new ArrayList<>();
int n = 0;
for(String feedUrl : feedUrls) {
AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
child.setId("child" + ++n);
children.add(child);
child.setParent(parent);
child.register(DynamicFeedAdapter.class);
StandardEnvironment env = new StandardEnvironment();
Properties props = new Properties();
props.setProperty("feed.url", feedUrl);
PropertiesPropertySource pps = new PropertiesPropertySource("feed", props);
env.getPropertySources().addLast(pps);
child.setEnvironment(env);
child.refresh();
}
System.out.println("Press any key to exit...");
System.in.read();
for (ConfigurableApplicationContext child : children) {
child.close();
}
parent.close();
}
@Bean
public IntegrationFlow aggregateFeeds() {
return IntegrationFlows.from("feedChannel")
.transform(extractLinkFromFeed())
.handle(System.out::println)
.get();
}
@Bean
public MessageChannel feedChannel() {
return new DirectChannel();
}
@Bean
public AbstractPayloadTransformer<SyndEntry, String> extractLinkFromFeed() {
return new AbstractPayloadTransformer<SyndEntry, String>() {
@Override
protected String transformPayload(SyndEntry payload) throws Exception {
return payload.getLink();
}
};
}
}
DynamicFeedAdapter.java
@Configuration
@EnableIntegration
public class DynamicFeedAdapter {
@Value("${feed.url}")
public String feedUrl;
@Bean
public static PropertySourcesPlaceholderConfigurer pspc() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public IntegrationFlow feedAdapter() throws MalformedURLException {
URL url = new URL(feedUrl);
return IntegrationFlows
.from(s -> s.feed(url, "feedTest"),
e -> e.poller(p -> p.fixedDelay(10000)))
.channel("feedChannel")
.get();
}
}
这适用于 IF ,只有 IF 我在application.properties
中定义的一个作为feed.url=[insert url here]
。否则它无法告诉我'无法解析财产{feed.url}'。我怀疑正在发生的事情是@Bean
中定义的DynamicFeedAdapter.java
都得到了热切初始化的单例,所以除了在main方法中我们的for循环中手动创建的bean之外(因为它们工作得很好)注入了feed.url属性)我们有一个已经急切初始化的流浪单例,如果在application.properties中没有定义feed.url那么它就无法解析属性而且一切都响了。现在根据我对Spring的了解,我知道应该可以@Lazy
初始化DynamicFeedAdapter.java
中的bean,这样我们就不会遇到这个不需要的流浪单身问题 - 孩子了。问题是现在......如果我只标记feedAdapter()
@Lazy
那么bean永远不会被初始化。我如何自己初始化它们?
更新 - 问题已解决
没有经过测试,我认为问题在于启动正在寻找 组件扫描期间的DynamicFeedAdapter。一个简单的解决方案 把它移到一个兄弟包。如果MonsterFeedApplication在 com.acme.foo,然后将适配器配置类放在com.acme.bar中。那 方式,启动不会将其视为应用程序的“部分”
这确实是个问题。在实施加里的建议后,一切都很完美。
答案 0 :(得分:1)
有关入站邮件适配器的类似问题,请参阅the answer to this question和its follow up。
实质上,每个Feed适配器都是在参数化的子上下文中创建的。
在这种情况下,子上下文是使用main()
方法创建的,但没有理由在.handle()
调用的服务中无法完成。