領先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多如果您錯過了今年的 SpringOne 2GX 大會,那麼一個熱門的專題是釋出 Spring Boot。Dave Syer 展示瞭如何透過能夠容納在 一條推文 中的程式碼來快速建立一個 Spring MVC 應用。在這篇博文裡,我將揭開 Spring Boot 的神秘面紗,並透過提交一個 Pull Request 來向您展示它是如何工作的。
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://");
}
}
}
我的 Spring JMS 自動配置類用 Spring 的 @Configuration 註解標記,將其標記為將被拾取並放入 應用程式上下文 的 Spring bean 源。它利用 Spring 4 的 @Conditional 註解,將其限制為僅當類路徑上有 JmsTemplate 時才新增這組 bean。這是 spring-jms 在類路徑上的一個明顯標誌。太棒了!
我的新類有兩個內部類,也都標記了 Spring Java 配置和附加條件。這使得將我所有的配置需求整合起來以自動化 Spring JMS 配置變得很容易。
JmsTemplateCreator 建立一個 JmsTemplate。它只在沒有其他地方定義 JmsTemplate 時才工作。這就是 Spring Boot 如何對如何建立 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,\
. . .
當然,沒有任何 Pull Request 會在沒有一些自動化單元測試的情況下是完整的。我不會把所有寫的測試都放在這篇博文裡,但您可以檢視 我與我的 Pull Request 一起提交的測試。在提交 Pull Request 之前,請準備好編寫您自己的測試套件!
這就是為 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 和 import 語句。它基本上將前面的片段擴充套件為
@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 之外,沒有 import 語句也沒有 @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() 將 import 語句新增到您程式碼的頂部。我基本上查看了自動配置測試程式碼中的 Spring JMS imports,並將它們新增到了這裡。這一次,如果您執行測試套件,它應該會透過。
$ 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: http://activemq.apache.org/
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://: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:// 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:// 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 的編碼標準,然後提交我的 Pull Request。隨時檢視我的貢獻和後續評論。(附註:經過一些微調後,它被接受了。)
我希望您喜歡這篇對 Spring Boot 及其工作原理的深入探討。希望您也能編寫自己的補丁。