Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Caching and Messaging Improvements in Spring Framework 4.1

Caching and Messaging Improvements in Spring Framework 4.1

This session showcases major new features along the lines of two key themes in Spring Framework 4.1: We’ll start with numerous improvements around the caching abstraction, as requested by the community, including the support for JCache (JSR-107) standard annotations. We’ll then move on to messaging-related features such as annotated JMS listener endpoints with flexible method signatures, using the messaging abstraction introduced in Spring Framework 4.0 and therefore aligning our core JMS support with our STOMP endpoint style.

Stéphane Nicoll

September 09, 2014
Tweet

More Decks by Stéphane Nicoll

Other Decks in Programming

Transcript

  1. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Caching and messaging improvements in Spring 4.1 Juergen Hoeller (@springjuergen) - Stéphane Nicoll (@snicoll)
  2. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Caching improvements
  3. Cache abstraction recap 3 public class BookRepository {! ! !

    ! ! ! ! ! ! ! ! ! ! ! }! @Cacheable("books")! public Book findById(String id) { } @Cacheable(value = "books", key = "T(s2gx.caching.BookIdResolver).resolveBookId(#isbn)")! public Book findById(ISBN isbn) { }! @CachePut(value = "books", key = "#book.id")! public Book update(Book book) { } @CacheEvict(value = "books")! public void delete(String id) { }!
  4. Cache abstraction recap (cont’d) 4 @Configuration! @EnableCaching! public class ApplicationConfig

    {! ! @Value("classpath:my-ehcache.xml")! private Resource ehCacheConfig;! ! @Bean! public CacheManager cacheManager() {! return new EhCacheCacheManager(! EhCacheManagerUtils.buildCacheManager(ehCacheConfig));! }! ! }!
  5. Class level customizations 5 @CacheConfig("books")! public class BookRepository {! !

    @Cacheable! public Book findById(String id) { }! ! @Cacheable(key = "T(s2gx.caching.BookIdResolver).resolveBookId(#isbn)")! public Book findById(ISBN isbn) { }! ! @CachePut(key = "#book.id")! public Book update(Book book) { }! ! @CacheEvict! public void delete(String id) { }! ! }!
  6. Custom key generator 6 @Component! public class IsbnKeyGenerator implements KeyGenerator

    {! ! ! @Override! ! public Object generate(Object target, Method method, Object... params) {! ! ! ISBN isbn = extract(params);! ! ! if (isbn != null) {! ! ! ! return BookIdResolver.resolveBookId(isbn);! ! ! }! ! ! throw new IllegalStateException(getClass().getName() +! ! ! ! ! " could not generate a cache id from " + Arrays.toString(params));! ! }! ! ! private ISBN extract(Object... params) { }! }!
  7. Operation level customizations 7 @CacheConfig("books")! public class BookRepository {! !

    @Cacheable! public Book findById(String id) { }! ! @Cacheable(keyGenerator = "isbnKeyGenerator")! public Book findById(ISBN isbn) { }! ! @CachePut(key = "#book.id")! public Book update(Book book) { }! ! @CacheEvict! public void delete(String id) { }! ! }!
  8. CacheResolver 8 public interface CacheResolver {! ! Collection<? extends Cache>

    resolveCaches(CacheOperationInvocationContext<?> context);! ! }! public class MyCacheResolver extends AbstractCacheResolver {! ! @Autowired! public MyCacheResolver(CacheManager cacheManager) {! super(cacheManager);! }! ! @Override! protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {! return getCacheNames(context.getTarget().getClass());! }! ! private Collection<String> getCacheNames(Class<?> serviceType) { }! ! }!
  9. JCache (JSR-107) support 9 import javax.cache.annotation.CacheDefaults;! import javax.cache.annotation.CachePut;! import javax.cache.annotation.CacheRemove;!

    import javax.cache.annotation.CacheResult;! import javax.cache.annotation.CacheValue;! ! @CacheDefaults(cacheName = "books")! public class BookRepository {! ! @CacheResult! public Book findById(String id) { }! ! @CacheResult(cacheKeyGenerator = IsbnCacheKeyGenerator.class)! public Book findById(ISBN isbn) { }! ! @CachePut! public void update(String id, @CacheValue Book book) { }! ! @CacheRemove! public void delete(String id) { }! ! }!
  10. JCache configuration 10 @Configuration! @EnableCaching! public class ApplicationConfig {! !

    @Value("classpath:my-ehcache.xml")! private Resource ehCacheConfig;! ! @Bean! public CacheManager cacheManager() {! return new EhCacheCacheManager(! EhCacheManagerUtils.buildCacheManager(ehCacheConfig));! }! ! }!
  11. Standard JCache bootstraping 11 @Configuration! @EnableCaching! public class ApplicationConfig {!

    ! @Bean! public CacheManager cacheManager() {! return new JCacheCacheManager();! }! ! }!
  12. Wrapping up • More use cases are covered out-of-the-box, no

    need to fallback on programmatic cache access: • CacheResolver: fine-grained runtime cache resolution • Class-level customizations via @CacheConfig: cache name(s), key generator, cache manager and/or cache resolver • Operation-level customizations • JCache (JSR-107) support • Supported automatically when the JSR-107 API is on the classpath • Reuse your existing infrastructure/configuration • Others • Convenient putIfAbsent on Cache interface • Better exception handling 12
  13. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Messaging improvements
  14. Messaging infrastructure recap 14 public class OrderMessageHandler {! ! public

    OrderStatus handleMessage(Order order) {! // order processing, return status! }! }! <beans ...>! <jms:listener-container message-converter="jmsMessageConverter">! <jms:listener destination="order" ref="orderMessageHandler" method="handleMessage"/>! </jms:listener-container>! ! <bean id="orderMessageHandler" class="sgx2014.messaging.spr40.OrderMessageHandler"/>! ! <bean id="jmsMessageConverter"! class="org.springframework.jms.support.converter.MappingJackson2MessageConverter">! <property name="targetType" value="TEXT"/>! <property name="typeIdPropertyName" value="__type"/>! </bean>! </beans>!
  15. Annotated endpoint 15 @Component! public class OrderMessageHandler {! ! @JmsListener(destination

    = "order")! public OrderStatus process(Order order) {! // order processing, return status! }! }! @JmsListener(id = "orderListener", containerFactory = "myContainerFactory", ! destination = "order", selector = "orderType = 'sell'", concurrency = "2-10")! public OrderStatus process(Order order) {! // order processing, return status! }!
  16. Transition from your existing config 16 <?xml version="1.0" encoding="UTF-8"?>! <beans

    ...>! ! <jms:annotation-driven/>! ! <jms:listener-container factory-id="jmsListenerContainerFactory" ! message-converter="jmsMessageConverter"/>! ! <bean id="jmsMessageConverter"! class="org.springframework.jms.support.converter.MappingJackson2MessageConverter">! <property name="targetType" value="TEXT"/>! <property name="typeIdPropertyName" value="__type"/>! </bean>! ! </beans>!
  17. … or remove XML altogether 17 @EnableJms! @Configuration! public class

    ApplicationConfig {! ! @Bean! public JmsListenerContainerFactory<?> jmsListenerContainerFactory(! ConnectionFactory connectionFactory) {! ! DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();! factory.setConnectionFactory(connectionFactory);! factory.setMessageConverter(jmsMessageConverter());! return factory;! }! ! @Bean! public MessageConverter jmsMessageConverter() {! MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();! converter.setTargetType(MessageType.TEXT);! converter.setTypeIdPropertyName("__type");! return converter;! }! }!
  18. Flexible method signature 18 @JmsListener(destination = "order")! public void processOrder(Order

    order) { } @JmsListener(destination = "order")! public void processOrder(Session session, TextMessage textMessage) { } @JmsListener(destination = "order")! public void processOrder(@Valid Order order) { } @JmsListener(destination = "order")! public void processOrder(Order order, @Header String orderType) { } @JmsListener(destination = "order")! @SendTo("orderStatus")! public OrderStatus processOrder(Order order) { }
  19. Messaging abstraction • Introduced in Spring Framework 4.0 • org.springframework.messaging.Message

    is a generic message representation with headers and a body ! ! ! ! • Full access to body and headers for both inbound and outbound messages 19 @JmsListener(destination = "order")! @SendTo("orderStatus")! public Message<OrderStatus> processOrder(Message<Order> order) { }!
  20. JmsMessagingTemplate • Similar to JmsTemplate, using o.s.messaging.Message • Exception translation

    • No JMS api involved at all • Implements common spring-messaging interfaces • MessageSendingOperations • MessageReceivingOperations • MessageRequestReplyOperations 20 Message<Order> orderMessage = MessageBuilder.! withPayload(order).setHeader("orderType", "sell").build();! messagingTemplate.send("order", orderMessage);!
  21. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. https://github.com/SpringOne2GX-2014/messaging-improvements Demo
  22. Programmatic endpoints registration 22 @EnableJms! @Configuration! public class ApplicationConfiguration implements

    JmsListenerConfigurer {! ! @Override! public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {! SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();! endpoint.setDestination("myQueue");! endpoint.setConcurrency("2-10");! endpoint.setMessageListener(message -> {! // message processing! });! registrar.registerEndpoint(endpoint);! }! }!
  23. Container recovery • Recovery policy when the broker becomes unreachable

    • BackOff interface • FixedBackOff: retry every X sec (default to 5 sec) • ExponentialBackOff: increases the back off period for each retry attempt • Implement your own! 23 ExponentialBackOff backOff = new ExponentialBackOff();! backOff.setInitialInterval(2 * 1000); // initial retry every 2 sec! backOff.setMultiplier(1.5); // increase each attempt by 50%! backOff.setMaxInterval(30 * 1000); // stop increasing at 30 sec! factory.setBackOff(backOff);!
  24. Wrapping Up • Annotation-driven endpoints • Full java config support

    • Flexible method signature: @Payload, @Valid, @Header, @Headers • Messaging abstraction integration • JmsMessagingTemplate • Message<?> can be used as method argument / return type • Endpoint abstraction, programmatic endpoint registration • Container recovery customization • Further JMS 2.0 alignments (shared subscriptions) 24
  25. One more thing … • You can use that with

    AMQP too • Spring AMQP 1.4.0.M1 25 @Component! public class OrderMessageHandler {! ! @RabbitListener(queues = "order")! @SendTo("orderStatus")! public OrderStatus processOrder(Order order, @Header String orderType) { }! }!