Первоначально автор Грег Тернквист в блоге SpringSource.
В случае, если вы пропустили конференцию SpringOne 2GX этого года, одним из ключевых моментов стало объявление о Spring Boot . Дейв Сайер показал, как быстро создать приложение Spring MVC с кодом, который поместился бы в одном твите . В этой записи блога я открою обложки Spring Boot и покажу вам, как это работает, составив запрос на извлечение .
Autoconfiguration
Spring Boot имеет мощную функцию автоматической настройки. Когда он обнаруживает определенные вещи в пути к классам, он автоматически создает компоненты. Но есть еще одна особенность — поддержка 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://localhost"); } } }
Мой класс автоконфигурирования Spring JMS помечен @Configuration
аннотацией Spring , помечая его как источник bean-компонентов Spring, которые нужно выбрать и поместить в контекст приложения . Он использует @Conditional
аннотации Spring 4 , ограничивая его добавлением только этого набора bean-компонентов, если он JmsTemplate
находится на пути к классам. Это явный признак того, что spring-jms находится на пути к классам. Отлично!
Мой новый класс имеет два внутренних класса, также помеченных для Spring Java Configuration и с дополнительными условиями. Это облегчает объединение всех моих потребностей в конфигурации для автоматизации конфигурации Spring JMS.
JmsTemplateCreator
создаетJmsTemplate
. Это работает, только если еще неJmsTemplate
определено в другом месте. Вот как Spring Boot может иметь мнение о том, как создатьJmsTemplate
, но быстро отступит, если вы предоставите свое собственное.ActiveMQConnectionFactoryCreator
создаетActiveMQConnectionFactory
, но только если он обнаруживает ActiveMQ на пути к классам и еслиConnectionFactory
среди всех bean-компонентов Spring не определено ни одного другого . Эта фабрика необходима для созданияJmsTemplate
. Он настроен на режим в памяти, что означает, что вам даже не нужно устанавливать автономный брокер, чтобы начать использовать JMS. Но вы можете легко заменить свой собственныйConnectionFactory
, и в любом случае Spring Boot автоматически подключит его кJmsTemplate
.
Вся эта автоконфигурация будет напрасной, если я не зарегистрирую новую должным образом JmsTemplateAutoConfiguration
. Я делаю это, добавляя полное доменное имя в файл Spring Boot spring.factories .
. . . org.springframework.boot.autoconfigure.jms.JmsTemplateAutoConfiguration,\ . . .
Конечно, ни один запрос на включение не будет полным без некоторых автоматических модульных тестов. Я не буду помещать все тесты, которые я написал в этой записи в блоге, но вы можете просмотреть тесты, которые я отправил с моим запросом на получение . Просто будьте готовы написать собственную батарею тестов, прежде чем отправлять запрос на извлечение!
И это все, что нужно для добавления автоконфигурации в Spring Boot! Это не так сложно. Фактически, вы можете просмотреть существующие классы автоконфигурации для большего количества примеров.
Groovy поддержка Spring Boot
Одной из главных особенностей Spring Boot, которая привлекла большое внимание, была его сильная поддержка Groovy. Это вызвало много аплодисментов во время основного выступления и было съедено во время выступления Дэйва и Фила на следующий день. В случае, если вы пропустили это, вот служба REST Spring Boot, которую продемонстрировал Дейв Сайер:
@RestController class ThisWillActuallyRun { @RequestMapping("/") String home() { "Hello World!" } }
Поместив этот код в app.groovy , Дейв запустил его, набрав:
$ 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) } }
Добавление собственной интеграции Groovy
Чтобы добавить поддержку Spring JMS, мне нужно добавить аналогичную автоконфигурацию в интерфейс командной строки Boot, чтобы каждый раз, когда кто-либо использовал a JmsTemplate
, a DefaultMessageListenerContainer
или a SimpleMessageListenerContainer
, он добавлял правильные биты.
Перед написанием этого кода я сначала написал простой скрипт на Groovy, который использует Spring JMS в jms.groovy :
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()
}
}
В этом тестовом сценарии ожидается, JmsTemplate
что ConnectionFactory
Spring Boot автоматически предоставит как a, так и a . Обратите внимание, что нет ни операторов import, ни каких-либо @ Grab’s, кроме добавления activemq-all . CommandLineRunner
Для запуска run()
метода используется интерфейс Spring Boot , который, в свою очередь, отправляет сообщение JmsTemplate
. Затем он использует CountDownLatch
ожидание сигнала от потребителя.
На другом конце находится тот, DefaultMessageListener
который при получении сообщения ведет обратный отсчет. Чтобы вызвать мой скрипт из набора тестов Spring Boot, я добавил следующий метод test 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"); } }
Эти функции обратного вызова упрощают интеграцию с инструментом CLI Spring Boot.
matches()
позволяет вам определить, какие символы вызывают это поведение. Для этого, если естьJmsTemplate
,DefaultMessageListenerContainer
илиSimpleMessageListenerContainer
, это вызовет действие.applyDependencies()
точно определяет, какие библиотеки добавить в путь к классам через координаты Maven. Это похоже на добавление@Grab
аннотаций к приложению. Для этого нам понадобятся spring-jms forJmsTemplate
и 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: 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://localhost: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://localhost 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://localhost 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, а затем отправить свой запрос на извлечение . Не стесняйтесь видеть мой вклад и следить за комментариями. (PS Это было принято после некоторой подстройки.)
Я надеюсь, что вам понравилось это глубокое погружение в Spring Boot и как оно работает. Надеюсь, вы сможете написать свой собственный патч.