get ready for spring 4
TRANSCRIPT
Speaker: Oleg Tsal-‐Tsalko (@tsaltsol)
Get ready for Spring 4
Modern Spring ecosystem
Spring Boot
Spring Boot Groovy app
@Controller class ThisWillActuallyRun { @RequestMapping("/") @ResponseBody String home() { return "Hello World!" } }
Spring Boot Java app import org.springframework.boot.*; import org.springframework.boot.autoconfigure.*; import org.springframework.stereotype.*; import org.springframework.web.bind.annotaSon.*; @Controller @EnableAutoConfigura9on public class SampleController { @RequestMapping("/") @ResponseBody String home() { return "Hello World!"; } public staSc void main(String[] args) throws ExcepSon { SpringApplica9on.run(SampleController.class, args); } }
Spring XD architecture
Taps Compute
HDFS
Workflow
Export
Spring XD RunSme
Ingest
Jobs
Export
Files Sensors Mobile Social
RDBMS
NoSQL
R, SAS
Spring XD Shell
Streams
Redis
Gemfire
PredicSve modeling
Spring XD RunSme
hbp | filter | file
Rabbit, Redis, (Pluggable)
XD Admin
CLUSTERED NODE
Filter Module
CLUSTERED NODE
HTTP Module
CLUSTERED NODE
File Module
In Memory Transport
hbp | filter | file
SINGLE NODE
All Modules
Spring 4.0
Almost there… RC1 now!!!
Spring 4.0 [RC1]
Biggest changes: • Comprehensive Java 8 support • Support for Java EE 7 APIs • WebSocket support • Programming model for message-‐oriented architectures Smaller features: • Support for @javax.transacSon.TransacSonal • New @CondiSonal annotaSon • New @RestController annotaSon • AsyncRestTemplate • Autowiring of generic types • Groovy based config And more…
Java 8 & Java EE 7 support
Comprehensive Java 8 support • Lambdas • Date and Time API (JSR-‐310) • Parameter name discovery • Repeatable annotaSons • Concurrency enhancement
Support for Java EE 7 APIs • Asynchronous API for RestTemplate • Bean ValidaSon 1.1 (JSR-‐349) • Expression Language 3.0 (JSR-‐341) • JMS 2.0 (JSR-‐343) • Concurrency USliSes for Java EE (JSR-‐236)
• JPA 2.1 (JSR-‐338) • Servlet 3.1 (JSR-‐340) • JSF 2.2 (JSR-‐344)
4.0 M1
Get advantage of Java 8 lambdas in well known Spring APIs!!!
JdbcTemplate jdbcTemplate; // method references private Customer map(ResultSet rs, int rowNum) throws SQLExcepSon {
return new Customer( rs.getString("name"), rs.getInt("age") ); } // let it saSsfy the `RowMapper` funcSonal interface Customer customer = jdbcTemplate.queryForObject(
"select name, age from customers ",this::map); Customer customer = jdbcTemplate.queryForObject(sql,
(rs, rowNum) -‐> new Customer(rs.getLong("id")));
4.0 M1
Lambdas fit naturally wherever you have funcSonal interfaces
JmsTemplate+MessageCreator: => Message createMessage(Session session) throws JMSExcepSon TransacSonTemplate+Transac9onCallback: => Object doInTransacSon(TransacSonStatus status) And other: • JdbcTemplate:
– ResultSetExtractor, RowCallbackHandler • JmsTemplate:
– MessagePostProcessor, BrowserCallback • TaskExecutor:
– Runnable, Callable
4.0 M1
JSR-‐310 Date and Time support import java.Sme.*; import org.springframework.format.annotaSon.*; public class Trade{
//…
@DateTimeFormat(iso=ISO.DATE) private LocalDate tradeDate;
@DateTimeFormat(iso=ISO.DATE) private LocalDateTime createTimestamp;
}
4.0 M1
JDK8 support in depth • Implicit use of LinkedHashMap/Set instead of simple
HashMap/Set to preserve ordering diffs in JDK7 and JDK8 • Embed ASM4.1 into Spring codebase to support JDK8
bytecode changes and to keep compaSbility with CGLib3.0 • Support for JDK 8 Data & Time (JSR-‐310) encorporated with
Spring’s Joda Time lib • Add awaitTerminaSonSeconds/commonPool properSes to
ForkJoinPoolFactoryBean to support JDK8 changes in it. • Encorporate JDK8 changes in reflecSon API (such as
java.lang.reflect.Parameter)
4.0 M1
Bean ValidaSon 1.1 support
MethodValida+onInterceptor autodetects Bean Valida/on 1.1's ExecutableValidator API now and uses it in favor of Hibernate Validator 4.2's na/ve variant.
4.0 M1
JMS 2.0 support What was done already: • Added "deliveryDelay" property on JmsTemplate • Added support for "deliveryDelay" and CompleSonListener to
CachedMessageProducer • Added support for the new "create(Shared)DurableConsumer" variants in
Spring’s CachingConnecSonFactory • Added support for the new "createSession" variants with fewer
parameters in Spring’s SingleConnecSonFactory
Known constraints: • There is no special support for the simplified JMSContext API, and likely
never will be, because of different Spring mechanism of managing connecSon pools and sessions
• JmsTemplate has no out-‐of-‐the-‐box support for send calls with an async compleSon listener.
4.0 M1
JEE7 concurrency uSliSes in Spring 4 This is built into ConcurrentTaskExecutor and ConcurrentTaskScheduler now,
automaScally detecSng the JSR-‐236 ExecutorService variants and adapSng to them.
Example of ManagedExecutorService usage introduced in JEE7:
4.0 M1
JPA 2.1 support EnStyManagerFactory.createEnStyManager( Synchroniza9onType.SYNCHRONIZED/UNSYNCHRONIZED, Map)
@PersistenceContext( synchronizaSonType=SYNCHRONIZED/UNSYNCHRONIZED)
Support for both transacSonal and extended EnStyManagers and for both Spring-‐managed resource transacSons and JTA transacSons
4.0 M1
WebSocket JSR-‐356 support (Annota/on driven using Servlet container scan) import javax.websocket.server.ServerEndpoint; import org.springframework.web.socket.server.endpoint.SpringConfigurator; @ServerEndpoint(value = "/echo", configurator = SpringConfigurator.class) public class EchoEndpoint { private final EchoService echoService; @Autowired public EchoEndpoint(EchoService echoService) { this.echoService = echoService; } @OnMessage public void handleMessage(Session session, String message) { // ... } }
4.0 M1
WebSocket JSR-‐356 support (Spring container-‐centric registra/on)
import org.springframework.web.socket.server.endpoint.ServerEndpointExporter; @ConfiguraSon public class EndpointConfig { @Bean public EchoEndpoint echoEndpoint() { return new EchoEndpoint(echoService()); } @Bean public EchoService echoService() { // ... } @Bean public ServerEndpointExporter endpointExporter() { return new ServerEndpointExporter(); } }
4.0 M1
WebSocket JSR-‐356 support (Separate endpoint instance per socket)
import org.springframework.web.socket.server.endpoint.ServerEndpointExporter; import org.springframework.web.socket.server.endpoint.ServerEndpointRegistra9on; @ConfiguraSon public class EndpointConfig { @Bean public EndpointRegistra9on echoEndpoint() { return new EndpointRegistra9on("/echo", EchoEndpoint.class); } @Bean public ServerEndpointExporter endpointExporter() { return new ServerEndpointExporter(); } // .. }
4.0 M1
Purely programmaSc way:
WebSocketContainer container = ContainerProvider.getWebSocketContainer(); container.connectToServer(EchoEndpoint.class, new URI("ws:localhost:8080/webapp/echo"));
Using Spring ApplicaSonContext:
import org.springframework.web.socket.client.endpoint.AnnotatedEndpointConnec9onManager; @ConfiguraSon public class EndpointConfig { // For Endpoint sub-‐classes use EndpointConnecSonManager instead @Bean public AnnotatedEndpointConnec9onManager connecSonManager() { return new AnnotatedEndpointConnec9onManager(echoEndpoint(), "ws://localhost:8080/webapp/echo"); } @Bean public EchoEndpoint echoEndpoint() { // ... } }
WebSocket JSR-‐356 client side support 4.0 M1
Spring WebSocket API Why own API? • To support available fallback opSons for WebSockets such as SockJS.
• WebSocket API is too low level (messages could be anything, no broadcast, no failure handling mechanism, etc.)
• No way to handle both HTTP and WebSocket requests in one place as per Front Controller pabern.
• No sub-‐protocol and higher level protocols support.
Spring WebSocket API I
Programming model for building message-‐oriented applicaSons using STOMP over
WebSocket protocol for example
Inspira/on
• Transparent WebSocket emulaSon using SockJS • Separate messaging module with main messaging abstracSons taken from Spring IntegraSon
• STOMP sub-‐protocol support (built-‐in simple STOMP broker)
• Higher level programming model
STOMP SUBSCRIBE id:sub-‐1 desSnaSon:/topic/price.stock.* MESSAGE subscripSon:sub-‐1 message-‐id:wm2si1tj-‐4 content-‐type: applicaSon/json desSnaSon:/topic/stocks.PRICE.STOCK.NASDAQ.EM
{\"Scker\":\"EMC\",\"price\":24.19}
WebSocket Client API var socket = new SockJS('/spring-‐websocket-‐porholio/porholio'); var client = Stomp.over(socket); var onConnect = funcSon() { client.subscribe("/topic/price.stock.*", func9on(message) { // process quote }); }; client.connect('guest', 'guest', onConnect);
Spring Simple Messaging API (Handling STOMP message sent via WebSocket
protocol) @Controller public class Por~olioController { // ... @MessageMapping(value="/app/trade") public void executeTrade(Trade trade, Principal principal) {
trade.setUsername(principal.getName()); this.tradeService.executeTrade(trade); } }
4.0 M1
Spring Simple Messaging API (Failures handling)
@Controller public class Por~olioController { // ... @MessageExcep9onHandler @SendToUser("/queue/errors") public String handleExcepSon(Throwable excepSon) { return excepSon.getMessage(); } }
4.0 M1
Spring Simple Messaging API (Handle SUBSCRIBE events from client)
@Controller public class Por~olioController { // ... @SubscribeMapping("/app/posi9ons") public List<Por~olioPosiSon> getPor~olios(Principal principal) { String user = principal.getName(); Por~olio por~olio = this.por~olioService.findPor~olio(user); return por~olio.getPosiSons(); } }
4.0 M1
Spring Simple Messaging API (Sending updates to client periodically)
@Service public class QuoteService { private final MessageSendingOpera9ons<String> messagingTemplate;
@Scheduled(fixedDelay=1000) public void sendQuotes() { for (Quote quote : this.quoteGenerator.generateQuotes()) { String desSnaSon = "/topic/price.stock." + quote.getTicker(); this.messagingTemplate.convertAndSend(des9na9on, quote); } } }
4.0 M1
Spring Simple Messaging API (Configura/on)
@ConfiguraSon @EnableWebSocketMessageBroker @EnableScheduling @ComponentScan(basePackages="org.springframework.samples") public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/porholio").withSockJS(); }
@Override public void configureMessageBroker(MessageBrokerConfigurer configurer) { configurer.enableSimpleBroker("/queue/", "/topic/"); //configurer.enableStompBrokerRelay("/queue/", "/topic/"); configurer.setApplica9onDes9na9onPrefixes("/app"); }
}
4.0 M1
spring-‐websocket-‐por~olio DEMO app architecture
Composable stereotype model @Service @Scope(“session”) @Primary @Transac9onal(rollbackFor=Run9meExcep9on.class) @RetenSon(RetenSonPolicy.RUNTIME) public @interface MyService {
boolean readOnly(); } @MyService(readOnly=true) public class MyTradeService {
//… }
JEE annotaSon support conSnue @ManagedBean public class MyTradeService implements TradeService{
@Inject public MyTradeService(MatchingService service){ //… }
@javax.transac9on.Transac9onal public void matchTrade(Trade trade){ //… }
}
@CondiSonal annotaSon @Condi9onal(MyCondi9on.class) @Configura9on public class ExampleConfiguraSon {
// @Bean methods } public class MyCondi9on implements Condi9on {
public boolean matches(...) { // return true if the condiSon holds true }
}
4.0 M2
Make #result available for SpEL in @CachePut key abribute
@CachePut(value = "personEn9ty", key = "#result.id") public Person persistPerson(Person p) { Person result = personRepository.save(p); return result; } @EnSty public class Person { @Id @GeneratedValue private Long id; … }
4.0 M2
Introduce AcSveProfilesResolver in the TestContext framework
4.0 M2
@RunWith(SpringJUnitRunner.class) @ContextConfiguraSon(TestContext.class) @Ac9veProfiles public class IntegraSonTest { //… @Test public void integraSonTest(){ //... } } @ConfiguraSon public class TestContext{ @Bean public Ac9veProfilesResolver acSveProfilesResolver(){ return new Ac9veProfilesResolver(){ String[] resolve(Class<?> testClass){ //… return new String[]{"test"}; } }; } //... }
Autowiring ordered collecSons based on @Order annotaSon
@Component @Order(value=1) public class BusinessServiceA implements BusinessService{} @Component @Order(value=2) public class BusinessServiceB implements BusinessService{} @Component public class ServiceRegistry {
@Autowired public List<BusinessService> services;
} assertThat(services.get(0).getClass(), is(BusinessServiceA.class)); assertThat(services.get(1).getClass(), is(BusinessServiceB.class));
4.0 M3
@PropertySources annotaSon @ConfiguraSon @PropertySources({ @PropertySource(value = "file:/etc/applicaSon/config.properSes",
ignoreResourceNotFound = true), @PropertySource(value = "file:/usr/local/etc/applicaSon/config.properSes",
ignoreResourceNotFound = true), @PropertySource(value = "file:conf/config.properSes",
ignoreResourceNotFound = true), @PropertySource(value = "classpath:applicaSon.properSes") }) public class AppConfiguraSon{
//… } Or even without @PropertySources using Java8 repeatable annota/ons!!!
4.0 RC1
Autowiring of generic types 4.0 RC1
Groovy config 4.0 RC1
def bb = new BeanBuilder() bb.loadBeans("classpath:*SpringBeans.groovy") def applica9onContext = bb.createApplicaSonContext() // MySpringBeans.groovy import o.sf.jdbc.core.JdbcTemplate import o.sf.jdbc.datasource.DataSourceTransac9onManager beans {
jdbcTemplate(JdbcTemplate) { dataSource = dataSource } transac9onManager(DataSourceTransac9onManager){ dataSource = dataSource } dataSource(BasicDataSource) driverClassName ="org.h2.Driver" url="jdbc:h2:mem:grailsDB" username="sa" password="" }
}
@ControllerAdvice annotaSon @ControllerAdvice({"com.example.web.api"}) public class GlobalErrorHandler { // common @ExcepSonHandler methods are defined here } Will be applied to both Controllers below: com.example.web.api.feature1.Feature1Controller com.example.web.api.feature2.Featuer2Controller
4.0 RC1
Other changes… 4.0 M1 • Replace EasyMock with Mockito • [SPR-‐8258] EhCache 2.5 support 4.0 M2 • [SPR-‐10664] Make #result available for SpEL in @CachePut key abribute • [SPR-‐10338] Introduce AcSveProfilesResolver in the TestContext
framework 4.0 M3 • [SPR-‐5574] Autowiring should support ordered collecSon driven by Order
annotaSon or Ordered interface 4.0 RC1 • [SPR-‐8371] Add @PropertySources annotaSon and support
ignoreResourceNotFound and preserve mulSple @PropertySources order • [SPR-‐10222] Allow @ControllerAdvice to be cofigured with a join point to
target a subset of controller
How to track progress?
• Track JIRA -‐ hbps://jira.springsource.org/browse/SPR/fixforversion/14229
• Check commits to codebase -‐ hbps://github.com/SpringSource/spring-‐framework/commits/master
Thank you!
Oleg Tsal-‐Tsalko Email: [email protected] Twiber: @tsaltsol