Caching and Messaging Improvements in Spring Framework 4.1

Post on 06-Jul-2015

1.458 views 1 download

description

Speaker: Juergen Hoeller, Stéphane Nicoll Core Spring Track 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.

Transcript of Caching and Messaging Improvements in Spring Framework 4.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)

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

Caching improvements

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) { }!

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));! }!!}!

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) { }!!}!

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) { }!}!

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) { }!!}!

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) { }! !}!

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) { }!!}!

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));! }!!}!

Standard JCache bootstraping

11

@Configuration!@EnableCaching!public class ApplicationConfig {!! @Bean! public CacheManager cacheManager() {! return new JCacheCacheManager();! }!!}!

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

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

Messaging improvements

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>!

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!}!

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>!

… 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;! }!}!

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) { }

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) { }!

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);!

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

https://github.com/SpringOne2GX-2014/messaging-improvements

Demo

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);! }!}!

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);!

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

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) { }!}!

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

Q/A

Thank you!