Статьи

Содействие Spring Boot с запросом на выгрузку


Первоначально автор Грег Тернквист в блоге 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() позволяет вам определить, какие символы вызывают это поведение. Для этого, если есть JmsTemplateDefaultMessageListenerContainerили SimpleMessageListenerContainer, это вызовет действие.
  • applyDependencies() точно определяет, какие библиотеки добавить в путь к классам через координаты Maven. Это похоже на добавление  @Grab аннотаций к приложению. Для этого нам понадобятся  spring-jms  for  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: 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 и как оно работает. Надеюсь, вы сможете написать свой собственный патч.