搶先一步
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://127.0.0.1");
}
}
}
我的 Spring JMS 自動配置類別使用 Spring 的 @Configuration
註解標記,將其標記為 Spring bean 的來源,以便拾取並放入 應用程式內容中。它利用 Spring 4 的 @Conditional
註解,限制它僅在 JmsTemplate
位於類別路徑上時才新增這組 bean。這是 spring-jms 位於類別路徑上的一個明顯標誌。完美!
我的新類別有兩個內部類別,也標記為 Spring Java Configuration 並具有額外的條件。這使得可以輕鬆地整合所有配置需求,以自動化 Spring JMS 配置。
JmsTemplateCreator
建立一個 JmsTemplate
。它僅在其他地方尚未定義 JmsTemplate
時才有效。這就是 Spring Boot 如何對如何建立 JmsTemplate
抱持自己的觀點,但如果您提供自己的 JmsTemplate
,它會很快退讓。ActiveMQConnectionFactoryCreator
建立一個 ActiveMQConnectionFactory
,但僅當它偵測到 ActiveMQ 位於類別路徑上,並且在所有 Spring bean 中沒有定義其他 ConnectionFactory
時。這個 factory 是建立 JmsTemplate
所必需的。它被設定為記憶體模式,這意味著您甚至不需要安裝獨立的 broker 即可開始使用 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 import,並將它們新增到此處。這次,如果您運行測試套件,它應該會通過。
$ 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.dev.org.tw/
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
Ta dah!
在這個階段,我所要做的就是查看 貢獻指南 以確保我遵循 Spring Boot 的程式碼編寫標準,然後提交我的 pull request。隨時查看我的貢獻和後續評論。(附註:經過一些微調後,它被接受了。)
我希望您喜歡對 Spring Boot 及其工作原理的深入研究。希望您能夠編寫自己的修補程式。