Cosa c'è di nuovo nella primavera 4.3?

1. Panoramica

La versione Spring 4.3 ha apportato alcuni bei miglioramenti al contenitore principale, al caching, JMS, Web MVC e ai sottomoduli di test del framework.

In questo post, discuteremo alcuni di questi miglioramenti, tra cui:

  • Iniezione implicita del costruttore
  • Supporto dei metodi di interfaccia predefiniti di Java 8
  • Risoluzione migliorata delle dipendenze
  • Perfezionamenti dell'astrazione della cache
  • Varianti di @RequestMapping composte
  • @Requestscope, @Sessionscope, @Applicationscope Annotations
  • @RequestAttribute e @SessionAttribute annotazioni
  • Supporto per versioni di librerie / server di applicazioni
  • la classe InjectionPoint

2. Iniezione implicita del costruttore

Considera la seguente classe di servizio:

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

Un caso d'uso piuttosto comune, ma se dimentichi l' annotazione @Autowired sul costruttore, il contenitore genererà un'eccezione alla ricerca di un costruttore predefinito, a meno che tu non esegua esplicitamente il cablaggio.

Quindi, a partire dalla 4.3, non è più necessario specificare un'annotazione di iniezione esplicita in uno scenario di questo tipo a costruttore singolo. Questo è particolarmente elegante per le classi che non portano alcuna annotazione:

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

In Spring 4.2 e precedenti, la seguente configurazione per questo bean non funzionerà, perché Spring non sarà in grado di trovare un costruttore predefinito per FooService . Spring 4.3 è più intelligente e autowire automaticamente il costruttore:

Allo stesso modo, potresti aver notato che le classi @Configuration storicamente non supportavano l'iniezione del costruttore. A partire da 4.3, lo fanno e naturalmente consentono di omettere @Autowired anche in uno scenario a costruttore singolo:

@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. Supporto per i metodi di interfaccia predefiniti di Java 8

Prima della Spring 4.3, i metodi di interfaccia predefiniti non erano supportati.

Questo non è stato facile da implementare perché anche l'introspector JavaBean di JDK non ha rilevato metodi predefiniti come accessors. Dalla Spring 4.3, i getter e setter implementati come metodi di interfaccia predefiniti vengono identificati durante l'iniezione, il che consente di usarli ad esempio come preprocessori comuni per le proprietà a cui si accede, come in questo esempio:

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

Questo bean potrebbe ora avere la proprietà stringDate inserita :

Lo stesso vale per l'utilizzo di annotazioni di test come @BeforeTransaction e @AfterTransaction sui metodi di interfaccia predefiniti. JUnit 5 supporta già le sue annotazioni di prova sui metodi di interfaccia predefiniti e Spring 4.3 segue l'esempio. Ora puoi astrarre la logica di test comune in un'interfaccia e implementarla nelle classi di test. Ecco un'interfaccia per i test case che registra i messaggi prima e dopo le transazioni nei test:

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

Un altro miglioramento relativo alle annotazioni @BeforeTransaction, @AfterTransaction e @Transactional è l'allentamento del requisito secondo cui i metodi annotati dovrebbero essere pubblici - ora possono avere qualsiasi livello di visibilità.

4. Risoluzione migliorata delle dipendenze

La versione più recente introduce anche ObjectProvider , un'estensione dell'interfaccia ObjectFactory esistente con utili firme come getIfAvailable e getIfUnique per recuperare un bean solo se esiste o se è possibile determinare un singolo candidato (in particolare: un candidato primario in caso di più fagioli abbinati).

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

È possibile utilizzare tale handle ObjectProvider per scopi di risoluzione personalizzata durante l'inizializzazione come mostrato sopra, o memorizzare l'handle in un campo per la risoluzione su richiesta tardiva (come si fa normalmente con un ObjectFactory ).

5. Perfezionamenti dell'astrazione della cache

L'astrazione della cache viene utilizzata principalmente per memorizzare nella cache i valori che consumano CPU e IO. In casi d'uso particolari, una determinata chiave può essere richiesta da più thread (cioè client) in parallelo, specialmente all'avvio. Il supporto della cache sincronizzata è una funzionalità richiesta da tempo che ora è stata implementata. Assumi quanto segue:

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

Notare l' attributo sync = true che dice al framework di bloccare qualsiasi thread simultaneo mentre il valore viene calcolato. Ciò assicurerà che questa operazione intensiva venga richiamata solo una volta in caso di accesso simultaneo.

Spring 4.3 migliora anche l'astrazione della cache come segue:

  • Le espressioni SpEL nelle annotazioni relative alla cache ora possono fare riferimento a bean (ad esempio @ beanName.method () ).
  • ConcurrentMapCacheManager e ConcurrentMapCache ora supportano la serializzazione delle voci della cache tramite un nuovo attributo storeByValue .
  • @Cacheable , @CacheEvict , @CachePut e @Caching possono ora essere utilizzati come meta-annotazioni per creare annotazioni composte personalizzate con sostituzioni di attributi.

6. Varianti @RequestMapping composte

Spring Framework 4.3 introduce le seguenti varianti composte a livello di metodo dell'annotazione @RequestMapping che aiutano a semplificare le mappature per i metodi HTTP comuni e ad esprimere meglio la semantica del metodo del gestore annotato.

  • @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.

Puoi trovare il codice sorgente dell'articolo su GitHub.