领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多如果您错过了今年的 SpringOne 2GX 大会,那么主题演讲中的一大亮点就是 Spring Boot 的发布。Dave Syer 展示了如何快速创建一个 Spring MVC 应用程序,其代码可以 压缩到一条推文中。在本篇博文中,我将揭开 Spring Boot 的面纱,并通过创建一个 拉取请求 来展示其工作原理。
Spring Boot 拥有强大的自动配置功能。当它在类路径上检测到某些内容时,就会自动创建 Bean。但它目前还没有一个功能是支持 Spring JMS。我需要这个功能!
第一步是编写一个自动配置类
package org.springframework.boot.autoconfigure.jms;
. . .some import statements. . .
@Configuration
@ConditionalOnClass(JmsTemplate.class)
public class JmsTemplateAutoConfiguration {
@Configuration
@ConditionalOnMissingBean(JmsTemplate.class)
protected static class JmsTemplateCreator {
@Autowired
ConnectionFactory connectionFactory;
@Bean
public JmsTemplate jmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
jmsTemplate.setPubSubDomain(true);
return jmsTemplate;
}
}
@Configuration
@ConditionalOnClass(ActiveMQConnectionFactory.class)
@ConditionalOnMissingBean(ConnectionFactory.class)
protected static class ActiveMQConnectionFactoryCreator {
@Bean
ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory("vm://127.0.0.1");
}
}
}
我的 Spring JMS 自动配置类使用 Spring 的 @Configuration
注解进行标记,将其标记为 Spring Bean 的来源,这些 Bean 将被拾取并放入 应用程序上下文 中。它利用了 Spring 4 的 @Conditional
注解,仅在类路径上存在 JmsTemplate
时才添加此 Bean 集。这是一个明显的迹象,表明 spring-jms 位于类路径上。完美!
我的新类有两个内部类,也使用 Spring Java 配置进行标记,并带有额外的条件。这使得整合所有配置需求以自动化 Spring JMS 配置变得非常容易。
JmsTemplateCreator
创建一个 JmsTemplate
。它仅在其他地方没有定义 JmsTemplate
时才有效。这就是 Spring Boot 如何对如何创建 JmsTemplate
有自己的见解,但如果您提供自己的 JmsTemplate
,它会很快退让。ActiveMQConnectionFactoryCreator
创建一个 ActiveMQConnectionFactory
,但前提是它在类路径上检测到 ActiveMQ,并且在所有 Spring Bean 中没有定义其他 ConnectionFactory
。此工厂对于创建 JmsTemplate
是必要的。它设置为内存模式,这意味着您甚至不需要安装独立的代理即可开始使用 JMS。但是您可以轻松地替换您自己的 ConnectionFactory
,无论哪种方式,Spring Boot 都会将其自动连接到 JmsTemplate
中。如果我不正确地注册我的新 JmsTemplateAutoConfiguration
,那么所有这些自动配置都将毫无用处。我通过将 FQDN 添加到 Spring Boot 的 spring.factories 文件中来做到这一点。
. . .
org.springframework.boot.autoconfigure.jms.JmsTemplateAutoConfiguration,\
. . .
当然,没有一些自动化的单元测试,拉取请求是不完整的。我不会在本篇博文中列出我编写的全部测试,但您可以查看 我随拉取请求提交的测试。在提交拉取请求之前,请准备好编写自己的测试集!
这就是向 Spring Boot 添加自动配置的全部内容!它并不复杂。实际上,您可以 浏览现有的自动配置类 以获取更多示例。
Spring Boot 最受关注的功能之一就是它对 Groovy 的强大支持。这在主题演讲中赢得了许多掌声,并在 Dave 和 Phil 次日的演讲中得到了广泛关注。如果您错过了它,以下是 Dave Syer 演示的 Spring Boot REST 服务
@RestController
class ThisWillActuallyRun {
@RequestMapping("/")
String home() {
"Hello World!"
}
}
将该代码放入 app.groovy 后,Dave 通过输入以下命令启动它:
$ spring run app.groovy
Spring Boot 的 命令行工具 使用嵌入式 Groovy 编译器并查看所有符号(如 RestController
)。然后它自动添加 @Grab
和导入语句。它基本上将前面的代码片段扩展为以下内容:
@Grab("org.springframework.boot:spring-boot-starter-web:0.5.0.BUILD-SNAPSHOT")
import org.springframework.web.bind.annotation.*
import org.springframework.web.servlet.config.annotation.*
import org.springframework.web.servlet.*
import org.springframework.web.servlet.handler.*
import org.springframework.http.*
static import org.springframework.boot.cli.template.GroovyTemplate.template
import org.springframework.boot.cli.compiler.autoconfigure.WebConfiguration
@RestController
class ThisWillActuallyRun {
@RequestMapping("/")
String home() {
"Hello World!"
}
public static void main(String[] args) {
SpringApplication.run(ThisWillActuallyRun, args)
}
}
要添加 Spring JMS 支持,我需要向 Boot 的 CLI 添加类似的自动配置,以便每当有人使用 JmsTemplate
、DefaultMessageListenerContainer
或 SimpleMessageListenerContainer
时,它都会添加正确的部分。
在编写该代码之前,我首先编写了一个简单的 Groovy 脚本,它在 jms.groovy 中使用 Spring JMS 功能
package org.test
@Grab("org.apache.activemq:activemq-all:5.2.0")
import java.util.concurrent.CountDownLatch
@Configuration
@Log
class JmsExample implements CommandLineRunner {
private CountDownLatch latch = new CountDownLatch(1)
@Autowired
JmsTemplate jmsTemplate
@Bean
DefaultMessageListenerContainer jmsListener(ConnectionFactory connectionFactory) {
new DefaultMessageListenerContainer([
connectionFactory: connectionFactory,
destinationName: "spring-boot",
pubSubDomain: true,
messageListener: new MessageListenerAdapter(new Receiver(latch:latch)) {{
defaultListenerMethod = "receive"
}}
])
}
void run(String... args) {
def messageCreator = { session ->
session.createObjectMessage("Greetings from Spring Boot via ActiveMQ")
} as MessageCreator
log.info "Sending JMS message..."
jmsTemplate.send("spring-boot", messageCreator)
latch.await()
}
}
@Log
class Receiver {
CountDownLatch latch
def receive(String message) {
log.info "Received ${message}"
latch.countDown()
}
}
此测试脚本期望 Spring Boot 自动提供 JmsTemplate
和 ConnectionFactory
。请注意,除了引入 activemq-all 之外,没有导入语句或任何 @Grab。它使用 Spring Boot 的 CommandLineRunner
接口启动 run()
方法,该方法依次通过 JmsTemplate
发送消息。然后它使用 CountDownLatch
等待来自消费者的信号。
另一端是一个 DefaultMessageListener
,它在收到消息后进行倒计时。为了从 Spring Boot 的测试套件内部调用我的脚本,我向 SampleIntegrationTests
添加了以下测试方法以调用 jms.groovy
@Test
public void jmsSample() throws Exception {
start("samples/jms.groovy");
String output = this.outputCapture.getOutputAndRelease();
assertTrue("Wrong output: " + output,
output.contains("Received Greetings from Spring Boot via ActiveMQ"));
FileUtil.forceDelete(new File("activemq-data")); // cleanup ActiveMQ cruft
}
为了测试我的新补丁,我发现运行特定的测试更容易。这绝对加快了速度。
$ mvn clean -Dtest=SampleIntegrationTests#jmsSample test
注意:我必须首先运行
mvn -DskipTests install
以将我的新 JMS 自动配置功能部署到我的本地 Maven 存储库中。
由于我还没有编写任何 Groovy 自动配置,因此测试将失败。是时候编写 CLI 自动配置了!
package org.springframework.boot.cli.compiler.autoconfigure;
. . .import statements. . .
public class JmsCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneFieldOrMethod(classNode, "JmsTemplate",
"DefaultMessageListenerContainer", "SimpleMessageListenerContainer");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies)
throws CompilationFailedException {
dependencies.add("org.springframework", "spring-jms",
dependencies.getProperty("spring.version")).add(
"org.apache.geronimo.specs", "geronimo-jms_1.1_spec", "1.1");
}
@Override
public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
imports.addStarImports("javax.jms", "org.springframework.jms.core",
"org.springframework.jms.listener",
"org.springframework.jms.listener.adapter");
}
}
这些回调钩子使与 Spring Boot 的 CLI 工具集成变得非常容易。
matches()
允许您定义触发此行为的符号。对于此示例,如果存在 JmsTemplate
、DefaultMessageListenerContainer
或 SimpleMessageListenerContainer
,它将触发操作。applyDependencies()
指定通过 Maven 坐标将哪些库添加到类路径中。这类似于向应用程序添加 @Grab
注解。对于此示例,我们需要 spring-jms 用于 JmsTemplate
,以及 geronimo-jms 用于 JMS API 规范类。applyImports()
将导入语句添加到代码的顶部。我基本上查看了自动配置测试代码中的 Spring JMS 导入,并将它们添加到此处。这次,如果您运行测试套件,它应该会通过。
$ mvn clean -Dtest=SampleIntegrationTests#jmsSample test
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v0.5.0.BUILD-SNAPSHOT)
2013-09-18 11:47:03.800 INFO 22969 --- [ runner-0] o.s.boot.SpringApplication : Starting application on retina with PID 22969 (/Users/gturnquist/.groovy/grapes/org.springframework.boot/spring-boot/jars/spring-boot-0.5.0.BUILD-SNAPSHOT.jar started by gturnquist)
2013-09-18 11:47:03.825 INFO 22969 --- [ runner-0] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4670f288: startup date [Wed Sep 18 11:47:03 CDT 2013]; root of context hierarchy
2013-09-18 11:47:04.428 INFO 22969 --- [ runner-0] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 2147483647
2013-09-18 11:47:04.498 INFO 22969 --- [ runner-0] o.apache.activemq.broker.BrokerService : Using Persistence Adapter: AMQPersistenceAdapter(activemq-data/localhost)
2013-09-18 11:47:04.501 INFO 22969 --- [ runner-0] o.a.a.store.amq.AMQPersistenceAdapter : AMQStore starting using directory: activemq-data/localhost
2013-09-18 11:47:04.515 INFO 22969 --- [ runner-0] org.apache.activemq.kaha.impl.KahaStore : Kaha Store using data directory activemq-data/localhost/kr-store/state
2013-09-18 11:47:04.541 INFO 22969 --- [ runner-0] o.a.a.store.amq.AMQPersistenceAdapter : Active data files: []
2013-09-18 11:47:04.586 INFO 22969 --- [ runner-0] o.apache.activemq.broker.BrokerService : ActiveMQ null JMS Message Broker (localhost) is starting
2013-09-18 11:47:04.587 INFO 22969 --- [ runner-0] o.apache.activemq.broker.BrokerService : For help or more information please see: https://activemq.apache.ac.cn/
2013-09-18 11:47:04.697 INFO 22969 --- [ JMX connector] o.a.a.broker.jmx.ManagementContext : JMX consoles can connect to service:jmx:rmi:///jndi/rmi://127.0.0.1:1099/jmxrmi
2013-09-18 11:47:04.812 INFO 22969 --- [ runner-0] org.apache.activemq.kaha.impl.KahaStore : Kaha Store using data directory activemq-data/localhost/kr-store/data
2013-09-18 11:47:04.814 INFO 22969 --- [ runner-0] o.apache.activemq.broker.BrokerService : ActiveMQ JMS Message Broker (localhost, ID:retina-51737-1379522824687-0:0) started
2013-09-18 11:47:04.817 INFO 22969 --- [ runner-0] o.a.activemq.broker.TransportConnector : Connector vm://127.0.0.1 Started
2013-09-18 11:47:04.867 INFO 22969 --- [ runner-0] o.s.boot.SpringApplication : Started application in 1.218 seconds
2013-09-18 11:47:04.874 INFO 22969 --- [ runner-0] org.test.JmsExample : Sending JMS message...
2013-09-18 11:47:04.928 INFO 22969 --- [ jmsListener-1] org.test.Receiver : Received Greetings from Spring Boot via ActiveMQ
2013-09-18 11:47:04.931 INFO 22969 --- [ main] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4670f288: startup date [Wed Sep 18 11:47:03 CDT 2013]; root of context hierarchy
2013-09-18 11:47:04.932 INFO 22969 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Stopping beans in phase 2147483647
2013-09-18 11:47:05.933 INFO 22969 --- [ main] o.a.activemq.broker.TransportConnector : Connector vm://127.0.0.1 Stopped
2013-09-18 11:47:05.933 INFO 22969 --- [ main] o.apache.activemq.broker.BrokerService : ActiveMQ Message Broker (localhost, ID:retina-51737-1379522824687-0:0) is shutting down
2013-09-18 11:47:05.944 INFO 22969 --- [ main] o.apache.activemq.broker.BrokerService : ActiveMQ JMS Message Broker (localhost, ID:retina-51737-1379522824687-0:0) stopped
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.432 sec - in org.springframework.boot.cli.SampleIntegrationTests
太棒了!
在此阶段,我需要做的就是查看 贡献指南,以确保我遵循 Spring Boot 的编码标准,然后提交我的 拉取请求。请随时查看我的贡献和后续评论。(附言:经过一些微调后,它被接受了。)
希望您喜欢这次对 Spring Boot 及其工作原理的深入探究。希望您能够编写自己的补丁。