Upload
jworks-powered-by-ordina
View
349
Download
4
Embed Size (px)
Citation preview
Spring 4Ken Coenen, Dieter Hubau, Tim De Grande, Steve De Zitter, Serguei Storojenko, Andreas Evers
Agenda
Steve De Zitter
Java SDK & EE support
Tim De Grande
Spring Core
Serguei Storojenko
Spring WebMVC
Ken Coenen
WebSockets and Messaging
Dieter Hubau
Testing improvements
Andreas Evers
Spring Core
Java 8 support
Java 8 features in Spring 4/4.1
▪ Lambda expressions▪ Method references▪ JSR-310 Date/Time Support▪ Repeatable annotations▪ Parameter name discovery▪ Java.util.Optional
▪ Related new injection features▪ Ordering, @Priority, Generic types
Java 8 lamda conventions
▪ Functional interfaces▪ Typically callback interfaces such as Runnable/Callable▪ Common functional interfaces in java.util.function
▪ Function, Predicate, Supplier▪ Used extensively in Java8 Streams Api
Lambda conventions in Spring api’s
▪ TransactionTemplate with TransactionCallback▪ JdbcTemplate/JdbcOperations with PreparedStatementSetter▪ JdbcTemplate/JdbcOperations with RowMapper▪ TaskExecutor▪ ListenableFuture
Lambdas JdbcOperations example
▪ PreparedStatementSetter and RowMapper pre Java 8List<Beer> beers = jdbcOperations.query(SELECT_BEER_BY_NAME_AND_ALCOHOL_PERCENTAGE, new PreparedStatementSetter() {
@Override public void setValues(PreparedStatement ps) throws SQLException{ ps.setString(1, "%" + name + "%"); ps.setBigDecimal(2, alcoholPercentage); } }, new RowMapper<Beer>() {
@Override public Beer mapRow(ResultSet rs, int rowNum) throws SQLException{ return new Beer(rs.getLong("id"), rs.getString("name"), rs.getString("description"), rs.getBigDecimal("alcoholPercentage"), rs.getTimestamp("modifiedTimestamp”)); }
});
Lambdas JdbcOperations example
▪ PreparedStatementSetter and RowMapper with lambdas
List<Beer> beers = jdbcOperations.query(SELECT_BEER_BY_NAME_AND_ALCOHOL_PERCENTAGE,ps -> {
ps.setString(1, "%" + name + "%"); ps.setBigDecimal( 2, alcoholPercentage);},(rs, rowNum) -> new Beer(rs.getLong("id"),
rs.getString("name"),rs.getString("description"),rs.getBigDecimal( "alcoholPercentage”))
);
Lambdas TaskExecutor example
▪ TaskExecutor (ThreadPoolTaskExecutor) pre Java 8
return threadPoolTaskExecutor.submitListenable( new Callable<List<Beer>>() {@Overridepublic List<Beer> call() throws Exception {
return beerRepository.getAllBeers();}
});
▪ TaskExecutor with lambdas
return threadPoolTaskExecutor.submitListenable( () -> beerRepository.getAllBeers());
ListenableFuture example
▪ ListenableFuture pre Java 8 with ListenableFutureCallback
ListenableFuture<List<Beer>> future = taskExecutorService.getBeersAsyncUsingTaskExecutor();
future.addCallback( new ListenableFutureCallback<List<Beer>>() {@Overridepublic void onFailure(Throwable throwable) {
//Handle Failure}
@Overridepublic void onSuccess(List<Beer> beers) {
//Handle success}
});
ListenableFuture example
▪ ListenableFuture pre Java 8 with success and failure callback
ListenableFuture<List<Beer>> future = taskExecutorService.getBeersAsyncUsingTaskExecutor();
future.addCallback( new SuccessCallback<List<Beer>>() {@Overridepublic void onSuccess(List<Beer> beers) {
//Handle success}
}, new FailureCallback() {@Overridepublic void onFailure(Throwable throwable) {
//Handle Failure}
});
Lambdas ListenableFuture examples
▪ ListenableFuture with Lambdas
ListenableFuture<List<Beer>> listenableFuture = taskExecutorService.getBeersAsyncUsingAnnotationsAndListenableFuture();
listenableFuture.addCallback((beers) -> {beers.stream().forEach(System. out::println);
},(throwable) -> {
//Handle failure}
);
Method references
▪ PreparedStatementSetter and RowMapper method reference example
List<Beer> beers = jdbcOperations.query(SELECT_BEER_BY_NAME_AND_ALCOHOL_PERCENTAGE,ps -> {
ps.setString(1, "%" + name + "%"); ps.setBigDecimal( 2, alcoholPercentage);
},this::mapRow
);
JSR-310 Date/Time Api
▪ Support for Java8 LocalDate, LocalDateTime, …▪ @DateTimeFormat annotation is also applicable to Java8 Date/Time
types.▪ Bind String to JSR-310 Date/Time objects▪ Render JSR-310 objects to Strings
JSR-310 Date/Time Api support example
▪ Mapping incoming @PathVariable to LocalDateTime
@RequestMapping(value = "/modified/{modifiedDate}",method = RequestMethod.GET)public List<Beer> getBeersModifiedAfterDate(
@PathVariable @DateTimeFormat(pattern="ddMMyyyyHHmm”)LocalDateTime modifiedDate){
return beerRepository.getBeersLastModifiedTimestampGreaterThan(Timestamp.valueOf(modifiedDate));
}
▪ /modified/110320151930
JSR-310 Date/Time Api support example
▪ Domain object
public class Beer {private Long id;private String name;private String description;private BigDecimal alcoholPercentage;
// @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)private LocalDate since;
@DateTimeFormat(pattern = "ddMMyyyyHHmm")private LocalDateTime modifiedTimestamp;
Rendering out the LocalDateTime property
▪ Jsp SnippetBeer modified timestamp: <form:input path="modifiedTimestamp" />
▪ Renders:
Repeatable annotations
▪ @Scheduled▪ @PropertySource▪ …
@PropertySource
▪ Java 8 Repeatable annotations@Configuration@PropertySource("classpath:/someProperties.properties")@PropertySource("classpath:/someProperties2.properties")public class SpringConfiguration {
▪ Pre-java8@Configuration@PropertySources(value = { @PropertySource("classpath:/someProperties.properties"), @PropertySource("classpath:/someProperties2.properties")})public class SpringConfiguration {
@Scheduled
▪ Java 8 Repeatable annotations@Scheduled(cron = "0 0 12 * * ?"),@Scheduled(cron = "0 0 18 * * ?")public void scheduledTask() {
System.out.println(message + LocalDateTime.now());}
▪ Pre Java-8@Schedules({ @Scheduled(cron = "0 0 12 * * ?"), @Scheduled(cron = "0 0 18 * * ?")})public void scheduledTask() {
System.out.println(message + LocalDateTime.now());}
Spring 4 Parameter discovery
▪ As of Spring 4.0 aware of Java8’s parameter reflection▪ Parameter names now available in Spring through common Java8
reflection methods.▪ Java8
▪ ‘-parameters’ compiler flag▪ Java 6 and 7: via asm
▪ ‘-debug’ compiler flag
Spring 4 parameter discovery
▪ Without parameter discovery (no –debug or –parameters compilation flag)
@RequestMapping(value = "/{id}", method = RequestMethod. GET)public Beer getBeerById( @PathVariable("id") Long id) {
return beerRepository.getBeerById(id);}
▪ With Parameter discovery
@RequestMapping(value = "/{id}", method = RequestMethod. GET)public Beer getBeerById( @PathVariable Long id) {
return beerRepository.getBeerById(id);}
Spring data custom query parameter discovery
▪ Custom query in Spring data JPA without parameter name discovery
▪ Custom query in Spring data JPA with parameter name discovery
Java 8 Optional
▪ Java.util.Optional as method parameter type with annotations that have required attribute
▪ @RequestParam▪ @RequestHeader▪ @MatrixVariable▪ …
▪ Optional on Autowired dependencies
Optional MVC Handler method parameters
▪ Using required=false@RequestMapping(value = "/{id}", method = RequestMethod. GET)public Beer getBeerById(
@PathVariable Long id,@RequestHeader(required=false) String country) {
if(country!=null)System.out.println(country);
return beerRepository.getBeerById(id);}
Optional on MVC handler method parameter
▪ Usage of Optional on @RequestHeader parameter
@RequestMapping(value = "/{id}", method = RequestMethod. GET)public Beer getBeerById(
@PathVariable Long id,@RequestHeader Optional<String> country) {
country.ifPresent(System.out::println);
return beerRepository.getBeerById(id);}
Optional on Injected dependencies
▪ Required=false on dependency@RestController@RequestMapping("/beers")public class BeerController {
@Autowired(required = false)private NotificationService notificationService;
▪ Using the dependency@RequestMapping(value = "/{id}", method = RequestMethod. GET)public Beer getBeerById(
@PathVariable Long id,@RequestHeader Optional<String> country) {
if(notificationService != null)notificationService.sendNotification("getBeerForId: " + id);
Optional on Injected dependencies
▪ Optional dependency@RestController@RequestMapping("/beers")public class BeerController {
@Autowiredprivate Optional<NotificationService> notificationService;
▪ Using the dependency@RequestMapping(value = "/{id}", method = RequestMethod. GET)
public Beer getBeerById( @PathVariable Long id, @RequestHeader Optional<String> country) {
notificationService.ifPresent(service -> service.sendNotification( "getBeerForId: " + id)
);return beerRepository.getBeerById(id);
}
Injection of Ordered Lists (Spring 4.0)
▪ Injection point or Ordered List@RestController @RequestMapping ("/beers")public class BeerController {
@Autowiredprivate List<MyService> services;
▪ MyService dependency with @Order(1)@Service @Order(1)public class MyServiceImpl implements MyService {
▪ MyService dependency with @Order(2)@Service @Order(2)public class MyOtherServiceImpl implements MyService {
▪ Injection point for Ordered List@RestController @RequestMapping ("/beers")public class BeerController {
@Autowiredprivate List<MyService> services;
▪ MyService dependency with @Order(1)@Bean @Order(1)public MyService service1() {
return new MyServiceImpl();}
▪ MyService dependency with @Order(3)@Bean @Order(3)public MyService service2() {
return new MyOtherServiceImpl();}
@Order on @Bean methods (Spring 4.1)
@javax.annotation.Priotity
▪ Injection point for Ordered List@RestController@RequestMapping("/beers")public class BeerController {
@Autowiredprivate List<MyService> services;
▪ MyService dependency with @Priority(1)@Service @Priority (1)public class MyServiceImpl implements MyService {
▪ MyService dependency with @Priority(2)@Service @Priority (2)public class MyOtherServiceImpl implements MyService {
@Priority for primary candidate selection
▪ Inject 1 Bean with multiple @Priority beans@RestController @RequestMapping ("/beers")public class BeerController {
@Autowiredprivate MyService service; //Injects MyServiceImpl
▪ Inject 1 Bean with multiple @Order beans▪ Fails! Causes: org.springframework.beans.factory.
NoUniqueBeanDefinitionException▪ This can be solved however!
▪ By Electing 1 of the @Order beans with @Primary (Spring 3 annotation)@Service
@Order(1) @Primarypublic class MyServiceImpl implements MyService {
@Qualifier for candidate selection
@Service @Qualifier ("myServiceImpl")public class MyServiceImpl implements MyService {
@Service @Qualifier("myOtherServiceImpl")public class MyOtherServiceImpl implements MyService {
@RestController @RequestMapping ("/beers")public class BeerController {
@Autowired @Qualifier ("myServiceImpl") private MyService service; //Injects MyServiceImpl
Generic type for candidate selection (Spring 4.0)
@Servicepublic class BeerServiceImpl implements MyService<Beer> {
@Servicepublic class MyOtherServiceImpl implements MyService<OtherEntity> {
@RestController @RequestMapping ("/beers")public class BeerController {
@Autowired private MyService<Beer> service; //Injects BeerServiceImpl
Sources and code
▪ Sources▪ http://spring.io/blog/2015/01/14/springone2gx-2014-replay-spring-framework-on-java-8▪ http://www.infoq.com/articles/spring-4-java-8▪ http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#new-in-4.0▪ http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#new-in-4.1
▪ Code▪ GitHub: https://github.com/Turbots/spring4_workshop▪ Maven module: spring4-java8-features
Core Spring
In the beginning there was… XML
In the beginning there was… XML
▪ Spring 3 made it possible to get rid of XML configuration for Spring (mostly)
@Configuration
But what about descriptions?
Spring 4 adds the @Description annotation
But what about descriptions? (2)
Spring 4 allows you to use all information to decide which beans to inject through @Conditional
Different strokes for different folks
Different strokes for different folks (2)
Conditions have access to:
Different strokes for different folks (3)
Annotated object Condition Context
AnnotationsMeta annotationsClass hierarchy...
Bean definition registryScopesBeans that are createdClassloaderEnvironmentResources...
One step further
In Spring 3▪ Only at the class level
▪ @Lazy components get created when they need to be injected.
Laziness
Spring 4▪ Also at injection point
Laziness (2)
Web Improvements
48
General Web Improvements Spring 4.0
▪ Focused primarily on Servlet 3.0+ environments.▪ WebSocket support▪ New @RestController annotation▪ New AsyncRestTemplate class▪ Comprehensive time zone support
49
Focus on Servlet 3.0 + environments
▪ Deployment to Servlet 2.5 servers remains an option
▪ Keep in mind: If you are using the Spring MVC Test Framework you will need to ensure that a Servlet 3.0 compatible JAR is in your test classpath.
▪ Main practical implication: web.xml is no longer needed (still supported, though)
50
Interface WebApplicationInitializer
▪ WebApplicationInitializer must be used, if web.xml is not used
▪ DEMO
51
@RestController annotation
▪ Before spring 4.0
▪ Spring 4.0
52
AsyncRestTemplate
▪ Web applications often need to query external REST services these days. The very nature of HTTP and synchronous calls can lead up to challenges when scaling applications for those needs: multiple threads may be blocked, waiting for remote HTTP responses.
53
AsyncRestTemplate (2)
▪ AsyncRestTemplate returns ListenableFuture wrapper instead of concrete results as RestTemplate does.
▪ ListenableFuture accepts completion callbacks
54
ListenableFuture
▪ DEMO
55
Comprehensive time zone support
▪ Before Spring 4.0
56
Comprehensive time zone support (2)
▪ Spring 4.0: the new extension of LocaleContext provides TimeZone support
57
Comprehensive time zone support (3)
▪ Spring 4.0: new LocalContextResolver
58
Comprehensive time zone support (4)
▪ Out of box, there is a bunch of classes implementing LocalContextResolver interface.
▪ Some of them support setting default TimeZone▪ Some of them also support fall back to the server TimeZone
59
Comprehensive time zone support (5)
▪ When available, the user’s TimeZone can be obtained using the RequestContext.getTimeZone() method.
▪ Time zone information will automatically be used by Date/Time Converter and Formatter objects registered with Spring’s ConversionService.
60
Comprehensive time zone support (6)
QuestionHow to pass time zone info from the client to LocaleContext?
61
Comprehensive time zone support (7)
▪ Old good Locale scenario:- Locale info passed as a request parameter (ex: user chooses language)- Out-of-box LocaleChangeInterceptor would intercept the request and
set the required Locale in LocaleResolver
62
Comprehensive time zone support (8)
▪ Possible TimeZone scenario:- TimeZone info passed as a request parameter (ex: JavaScript function
returning TimeZone)- Extending LocaleChangeInterceptor (writing own interceptor) to
handle TimeZone info and setting the correct LocaleContext in LocaleContextResolver
63
Spring 4.1 web improvement
▪ There is a number of small and bigger improvements. Further, in this presentation some of them will be highlighted.
▪ For the complete and comprehensive list of the improvements, please, consult the Spring documentation
64
JDK 1.8’s java.util.Optional support
▪ Optional is now supported for @RequestParam, @RequestHeader, and @MatrixVariable controller method arguments.
▪ DEMO
65
@JsonView
▪ Jackson’s @JsonView is supported directly on @ResponseBody and ResponseEntity controller methods for serializing different amounts of detail for the same POJO (e.g. summary vs. detail page).
▪ Also supported with View-based rendering by adding the serialization view type as a model attribute under a special key.
▪ DEMO
66
New type: RequestEntity
▪ Provides a builder-style API to guide client-side REST code towards the preparation of HTTP requests.
▪ For example:
67
ResponseEntity provides builder-style API
▪ Example:
68
Full list of improvements
▪ Full list of improvements to be found here:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/new-in-4.1.html
WebSockets
In the beginning there was… polling
Do you have new data?
In the beginning there was… polling (2)
Do you have new data?
No
In the beginning there was… polling (3)
In the beginning there was… polling (4)
Do you have new data?
In the beginning there was… polling (5)
Do you have new data?
No
In the beginning there was… polling (6)
In the beginning there was… polling (7)
Long polling
Do you have new data?
Long polling (2)
Do you have new data?
Long polling (3)
Do you have new data?
Here is your data
Polling disadvantages
● Client initiates a request (even with long polling mechanism)
● Traffic and overhead (even if there is no data!)
● Big delay between event and notification
● Consider server loads
And with HTML5 came… WebSockets!
What do you think about a protocol upgrade?
And with HTML5 came… WebSockets! (2)
What do you think about a protocol upgrade?
Sure, why not!HTTP 101 Switching Protocols
And with HTML5 came… WebSockets! (3)
Open TCP socket
What about AJAX? Can I still use it?
AJAX
High latency (HTTP req/res for each
roundtrip)
Small to medium messages
WebSockets
Low latency (TCP socket remains
open)
Large and frequent messages
Realtime applications (stocks,
games, …)
● Not every project needs WebSockets!
WebSockets in Spring
● New Spring WebSocket module
● Fallback option with SockJS
● as underlying sub-protocol (https://stomp.github.io/)
○ Simple interoperable protocol for asynchronous messaging
○ Client actions: SEND, SUBSCRIBE / UNSUBSCRIBE, ACK / NACK
○ Server actions: MESSAGE, RECEIPT, ERROR
WebSockets in Spring (2)
● Generic Spring Messaging Module
○ Some Spring integration types promoted to Core (eg. Message, @MessageMapping, …)
○ Also usable when using JMS
WebSockets in Spring (3)
@EnableWebSocket @EnableWebSocketMessageBroker
Raw WebSocket handler support
Extend TextWebSocketHandler or BinaryWebSocketHandler and implement handleTextMessage
WebSocket using a higher-level messaging sub-protocol (eg. STOMP)
Raw example
import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.WebSocketSession;import org.springframework.web.socket.TextMessage;
public class MyHandler extends TextWebSocketHandler { @Override public void handleTextMessage(WebSocketSession session, TextMessage msg) { // ... }}
Raw example (2)
@Configuration@EnableWebSocketpublic class WebSocketConfig implements WebSocketConfigurer {
@Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myHandler(), "/myHandler"); }
@Bean public WebSocketHandler myHandler() { return new MyHandler(); }}
Demo application
● AngularJS application
● Spring WebMVC backend
● SockJS with higher-level STOMP protocol and Jackson serialization
Testing improvements
Cleaning up after Spring 3.x
Cleaning up after Spring 3.x (2)
● Many packages/classes/methods were marked as deprecated in the Spring 3.x version
● In Spring 4.x:
○ All deprecated packages have been removed
○ Many deprecated methods and fields have been removed
● Before upgrading from 3.x to 4.x → Look at the deprecation warnings!
Pruning in Spring 4.0
● Several things have been pruned to improve the testing experience
< 4.0 4.0+
No more JUnit 3.8 support Use JUnit 4 or TestNG
@ExpectedException Use @Test(expected = MyEx.class) or @Rule
@NotTransactional Use @Transactional(propagation = NOT_SUPPORTED)
SimpleJdbcTestUtils Use JdbcTestUtils or ScriptUtils or @Sql
Changes in dependencies in Spring 4.0
● Servlet API mocks → upgraded to Servlet 3.0 API
○ Servlet 3.0 API is easier, more powerful and more versatile
○ Servlet 2.5 API is still supported
● JUnit
○ minimum version 4.8
○ recommended version 4.11
● TestNG → Use version 6.8.5
Changes in dependencies in Spring 4.1
● Servlet API mocks → upgraded to Servlet 3.0 API
○ Servlet 3.0 API is easier, more powerful and more versatile
○ Servlet 2.5 API is still supported
● JUnit
○ minimum version 4.9 is supported
○ version 4.11 is recommended
● TestNG
○ version 6.8.8 is recommended
New in Spring 4.0
● SocketUtils
○ Used to scan for available UDP and TCP ports
○ Handy for setting up different containers / servers during integration tests
● ActiveProfilesResolver API
○ Programmatic alternative to static profile strings (like in Spring 3.x)
○ Use the resolver attribute in @ActiveProfiles annotation
● Meta-annotation support for tests
○ Easy to create annotations for your annotations
New in Spring 4.1
● Groovy scripts can be used to configure your Spring context
● Programmatically start/stop/commit your test transaction with TestTransaction
● Default & Custom TestExecutionListeners can now be automatically discovered
● Custom TestExecutionListeners can be merged with the default ones
● @Sql and @SqlConfig allow you to configure execution of SQL scripts declaratively:
○ before/after a test class
○ before/after a test method
Demo
Questions