Quoi de neuf dans Spring 4.3?

1. Vue d'ensemble

La version Spring 4.3 a apporté quelques améliorations intéressantes dans le conteneur principal, la mise en cache, JMS, Web MVC et les sous-modules de test du framework.

Dans cet article, nous discuterons de quelques-unes de ces améliorations, notamment:

  • Injection de constructeur implicite
  • Prise en charge des méthodes d'interface par défaut Java 8
  • Résolution améliorée des dépendances
  • Améliorations de l'abstraction du cache
  • Variantes composées de @RequestMapping
  • @Requestscope, @Sessionscope, @Applicationscope Annotations
  • @RequestAttribute et @SessionAttribute annotations
  • Prise en charge des versions de bibliothèques / serveurs d'applications
  • la classe InjectionPoint

2. Injection de constructeur implicite

Considérez la classe de service suivante:

@Service public class FooService { private final FooRepository repository; @Autowired public FooService(FooRepository repository) { this.repository = repository } }

Un cas d'utilisation assez courant, mais si vous oubliez l' annotation @Autowired sur le constructeur, le conteneur lèvera une exception à la recherche d'un constructeur par défaut, sauf si vous faites explicitement le câblage.

Ainsi, à partir de 4.3, vous n'avez plus besoin de spécifier une annotation d'injection explicite dans un tel scénario à constructeur unique. Ceci est particulièrement élégant pour les classes qui ne portent aucune annotation:

public class FooService { private final FooRepository repository; public FooService(FooRepository repository) { this.repository = repository } }

Dans Spring 4.2 et versions antérieures , la configuration suivante pour ce bean ne fonctionnera pas, car Spring ne pourra pas trouver de constructeur par défaut pour FooService . Spring 4.3 est plus intelligent et câblera automatiquement le constructeur:

De même, vous avez peut-être remarqué que les classes @Configuration ne supportaient pas historiquement l'injection de constructeur. À partir de 4.3, ils le font, et ils permettent naturellement d'omettre @Autowired dans un scénario à un seul constructeur:

@Configuration public class FooConfiguration { private final FooRepository repository; public FooConfiguration(FooRepository repository) { this.repository = repository; } @Bean public FooService fooService() { return new FooService(this.repository); } }

3. Prise en charge des méthodes d'interface par défaut Java 8

Avant Spring 4.3, les méthodes d'interface par défaut n'étaient pas prises en charge.

Cela n'a pas été facile à mettre en œuvre car même l'introspecteur JavaBean de JDK ne détectait pas les méthodes par défaut comme accesseurs. Depuis le printemps 4.3, les getters et setters implémentés comme méthodes d'interface par défaut sont identifiés lors de l'injection, ce qui permet de les utiliser par exemple comme préprocesseurs communs pour les propriétés accédées, comme dans cet exemple:

public interface IDateHolder { void setLocalDate(LocalDate localDate); LocalDate getLocalDate(); default void setStringDate(String stringDate) { setLocalDate(LocalDate.parse(stringDate, DateTimeFormatter.ofPattern("dd.MM.yyyy"))); } } 

Ce bean peut maintenant avoir la propriété stringDate injectée:

Il en va de même pour l'utilisation d'annotations de test telles que @BeforeTransaction et @AfterTransaction sur les méthodes d'interface par défaut. JUnit 5 prend déjà en charge ses annotations de test sur les méthodes d'interface par défaut, et Spring 4.3 suit l'exemple. Vous pouvez désormais résumer la logique de test commune dans une interface et l'implémenter dans des classes de test. Voici une interface pour les cas de test qui enregistre les messages avant et après les transactions dans les tests:

public interface ITransactionalTest { Logger log = LoggerFactory.getLogger(ITransactionalTest.class); @BeforeTransaction default void beforeTransaction() { log.info("Before opening transaction"); } @AfterTransaction default void afterTransaction() { log.info("After closing transaction"); } }

Une autre amélioration concernant les annotations @BeforeTransaction, @AfterTransaction et @Transactional est l'assouplissement de l'exigence selon laquelle les méthodes annotées doivent être publiques - maintenant elles peuvent avoir n'importe quel niveau de visibilité.

4. Résolution améliorée des dépendances

La dernière version introduit également l' ObjectProvider , une extension de l' interface ObjectFactory existante avec des signatures pratiques telles que getIfAvailable et getIfUnique pour récupérer un bean uniquement s'il existe ou si un seul candidat peut être déterminé (en particulier: un candidat principal en cas de les haricots correspondants).

@Service public class FooService { private final FooRepository repository; public FooService(ObjectProvider repositoryProvider) { this.repository = repositoryProvider.getIfUnique(); } }

Vous pouvez utiliser ce descripteur ObjectProvider à des fins de résolution personnalisée lors de l'initialisation, comme indiqué ci-dessus, ou stocker le descripteur dans un champ pour une résolution à la demande tardive (comme vous le faites généralement avec un ObjectFactory ).

5. Améliorations de l'abstraction du cache

L'abstraction de cache est principalement utilisée pour mettre en cache les valeurs qui consomment du processeur et des E / S. Dans des cas d'utilisation particuliers, une clé donnée peut être demandée par plusieurs threads (ie clients) en parallèle, notamment au démarrage. La prise en charge du cache synchronisé est une fonctionnalité demandée depuis longtemps qui a maintenant été implémentée. Supposons ce qui suit:

@Service public class FooService { @Cacheable(cacheNames = "foos", sync = true) public Foo getFoo(String id) { ... } }

Notez l' attribut sync = true qui indique au framework de bloquer tous les threads simultanés pendant le calcul de la valeur. Cela garantira que cette opération intensive n'est appelée qu'une seule fois en cas d'accès simultané.

Spring 4.3 améliore également l'abstraction de la mise en cache comme suit:

  • Les expressions SpEL dans les annotations liées au cache peuvent désormais faire référence à des beans (c'est- à- dire @ beanName.method () ).
  • ConcurrentMapCacheManager et ConcurrentMapCache prennent désormais en charge la sérialisation des entrées de cache via un nouvel attribut storeByValue .
  • @Cacheable , @CacheEvict , @CachePut et @Caching peuvent désormais être utilisés comme méta-annotations pour créer des annotations composées personnalisées avec des remplacements d'attributs.

6. Composé de variantes @RequestMapping

Spring Framework 4.3 présente les variantes composées au niveau de la méthode suivantes de l' annotation @RequestMapping qui aident à simplifier les mappages pour les méthodes HTTP courantes et à mieux exprimer la sémantique de la méthode de gestionnaire annotée.

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

For example, @GetMapping is a shorter form of saying @RequestMapping(method = RequestMethod.GET). The following example shows an MVC controller that has been simplified with a composed @GetMapping annotation.

@Controller @RequestMapping("/appointments") public class AppointmentsController { private final AppointmentBook appointmentBook; @Autowired public AppointmentsController(AppointmentBook appointmentBook) { this.appointmentBook = appointmentBook; } @GetMapping public Map get() { return appointmentBook.getAppointmentsForToday(); } }

7. @RequestScope, @SessionScope, @ApplicationScope Annotations

When using annotation-driven components or Java Config, the @RequestScope, @SessionScope and @ApplicationScope annotations can be used to assign a component to the required scope. These annotations not only set the scope of the bean but also set the scoped proxy mode to ScopedProxyMode.TARGET_CLASS.

TARGET_CLASS mode means that CGLIB proxy will be used for proxying of this bean and ensuring that it can be injected in any other bean, even with a broader scope. TARGET_CLASS mode allows proxying not only for interfaces but classes too.

@RequestScope @Component public class LoginAction { // ... }
@SessionScope @Component public class UserPreferences { // ... }
@ApplicationScope @Component public class AppPreferences { // ... }

8. @RequestAttribute and @SessionAttribute Annotations

Two more annotations for injecting parameters of the HTTP request into Controller methods appeared, namely @RequestAttribute and @SessionAttribute. They allow you to access some pre-existing attributes, managed globally (i.e. outside the Controller). The values for these attributes may be provided, for instance, by registered instances of javax.servlet.Filter or org.springframework.web.servlet.HandlerInterceptor.

Suppose we have registered the following HandlerInterceptor implementation that parses the request and adds login parameter to the session and another query parameter to a request:

public class ParamInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { request.getSession().setAttribute("login", "john"); request.setAttribute("query", "invoices"); return super.preHandle(request, response, handler); } }

Such parameters may be injected into a Controller instance with corresponding annotations on method arguments:

@GetMapping public String get(@SessionAttribute String login, @RequestAttribute String query) { return String.format("login = %s, query = %s", login, query); }

9. Libraries/Application Servers Versions Support

Spring 4.3 supports the following library versions and server generations:

  • Hibernate ORM 5.2 (still supporting 4.2/4.3 and 5.0/5.1 as well, with 3.6 deprecated now)
  • Jackson 2.8 (minimum raised to Jackson 2.6+ as of Spring 4.3)
  • OkHttp 3.x (still supporting OkHttp 2.x side by side)
  • Netty 4.1
  • Undertow 1.4
  • Tomcat 8.5.2 as well as 9.0 M6

Furthermore, Spring 4.3 embeds the updated ASM 5.1 and Objenesis 2.4 in spring-core.jar.

10. InjectionPoint

The InjectionPoint class is a new class introduced in Spring 4.3 which provides information about places where a particular bean gets injected, whether it is a method/constructor parameter or a field.

The types of information you can find using this class are:

  • Field object – you can obtain the point of injection wrapped as a Field object by using the getField() method if the bean is injected into a field
  • MethodParameter – you can call getMethodParameter() method to obtain the injection point wrapped as a MethodParameter object if the bean is injected into a parameter
  • Member – calling getMember() method will return the entity containing the injected bean wrapped into a Member object
  • Class – obtain the declared type of the parameter or field where the bean in injected, using getDeclaredType()
  • Annotation[] – by using the getAnnotations() method, you can retrieve an array of Annotation objects which represent the annotations associated with the field or parameter
  • AnnotatedElement – call getAnnotatedElement() to get the injection point wrapped as an AnnotatedElement object

A case in which this class is very useful is when we want to create Logger beans based on the class to which they belong:

@Bean @Scope("prototype") public Logger logger(InjectionPoint injectionPoint) { return Logger.getLogger( injectionPoint.getMethodParameter().getContainingClass()); }

The bean has to be defined with a prototype scope so that a different logger is created for each class. If you create a singleton bean and inject in multiple places, the Spring will return the first encountered injection point.

Then, we can inject the bean into our AppointmentsController:

@Autowired private Logger logger;

11. Conclusion

In this article, we discussed some of the new features introduced with Spring 4.3.

We've covered useful annotations that eliminate boilerplate, new helpful methods of dependency lookup and injection and several substantial improvements within the web and caching facilities.

Vous pouvez trouver le code source de l'article sur GitHub.