O que há de novo na primavera 4.3?
1. Visão geral
O lançamento do Spring 4.3 trouxe alguns refinamentos interessantes para o contêiner principal, cache, JMS, Web MVC e submódulos de teste da estrutura.
Neste post, discutiremos algumas dessas melhorias, incluindo:
-
Injeção implícita de construtor
-
Suporte a métodos de interface padrão do Java 8
-
Resolução aprimorada de dependências
-
Refinamentos de abstração de cache
-
Variantes@RequestMapping compostas
-
@Requestscope, @Sessionscope, @Applicationscope Anotações
-
@RequestAttributee anotações@SessionAttribute
-
Libraries/Application Servers Versions Support
-
a classeInjectionPoint
2. Injeção implícita de construtor
Considere a seguinte classe de serviço:
@Service
public class FooService {
private final FooRepository repository;
@Autowired
public FooService(FooRepository repository) {
this.repository = repository
}
}
Um caso de uso bastante comum, mas se você esquecer a anotação@Autowired no construtor, o contêiner lançará uma exceção procurando por um construtor padrão, a menos que você explicitamente faça a fiação.
Portanto, a partir do 4.3, você não precisa mais especificar uma anotação de injeção explícita em um cenário de construtor único. Isso é particularmente elegante para classes que não contêm nenhuma anotação:
public class FooService {
private final FooRepository repository;
public FooService(FooRepository repository) {
this.repository = repository
}
}
No Spring 4.2 e abaixo, a seguinte configuração para este bean não funcionará, porque o Spring não será capaz de encontrar um construtor padrão paraFooService. O Spring 4.3 é mais inteligente e conectará automaticamente o construtor automaticamente:
Da mesma forma, você deve ter notado que as classes@Configuration historicamente não suportavam injeção de construtor. A partir de 4.3, eles sim, e naturalmente permitem omitir@Autowired em um cenário de construtor único também:
@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. Suporte a métodos de interface padrão do Java 8
Antes do Spring 4.3, os métodos de interface padrão não eram suportados.
Isso não foi fácil de implementar porque mesmo o introspector JavaBean do JDK não detectou métodos padrão como acessadores. Desde o Spring 4.3, getters e setters implementados como métodos de interface padrão são identificados durante a injeção, o que permite usá-los, por exemplo, como pré-processadores comuns para propriedades acessadas, como neste exemplo:
public interface IDateHolder {
void setLocalDate(LocalDate localDate);
LocalDate getLocalDate();
default void setStringDate(String stringDate) {
setLocalDate(LocalDate.parse(stringDate,
DateTimeFormatter.ofPattern("dd.MM.yyyy")));
}
}
Este bean agora pode ter a propriedadestringDate injetada:
O mesmo vale para o uso de anotações de teste como@BeforeTransactione@AfterTransaction em métodos de interface padrão. O JUnit 5 já suporta suas anotações de teste nos métodos de interface padrão e o Spring 4.3 segue a liderança. Agora você pode abstrair a lógica de teste comum em uma interface e implementá-la nas classes de teste. Aqui está uma interface para casos de teste que registra mensagens antes e depois das transações nos testes:
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");
}
}
Outra melhoria em relação às anotações@BeforeTransaction,@AfterTransactione@Transactional é o relaxamento da exigência de que os métodos anotados sejampublic - agora eles podem ter qualquer nível de visibilidade.
4. Resolução aprimorada de dependências
A versão mais recente também apresentaObjectProvider, uma extensão da interfaceObjectFactory existente com assinaturas úteis, comogetIfAvailableegetIfUnique para recuperar um bean apenas se ele existir ou se um um único candidato pode ser determinado (em particular: um candidato primário no caso de vários beans correspondentes).
@Service
public class FooService {
private final FooRepository repository;
public FooService(ObjectProvider repositoryProvider) {
this.repository = repositoryProvider.getIfUnique();
}
}
Você pode usar esse identificadorObjectProvider para fins de resolução personalizada durante a inicialização, conforme mostrado acima, ou armazenar o identificador em um campo para resolução posterior sob demanda (como normalmente faz com umObjectFactory).
5. Refinamentos de abstração de cache
A abstração do cache é usada principalmente para armazenar em cache valores que consomem CPU e IO. Em casos de uso específicos, uma determinada chave pode ser solicitada por vários threads (ou seja, clientes) em paralelo, especialmente na inicialização. O suporte ao cache sincronizado é um recurso solicitado há muito tempo que foi implementado. Suponha o seguinte:
@Service
public class FooService {
@Cacheable(cacheNames = "foos", sync = true)
public Foo getFoo(String id) { ... }
}
Observe o atributosync = true que diz à estrutura para bloquear quaisquer threads simultâneos enquanto o valor está sendo calculado. Isso garantirá que essa operação intensiva seja invocada apenas uma vez no caso de acesso simultâneo.
O Spring 4.3 também melhora a abstração do armazenamento em cache da seguinte maneira:
-
As expressões SpEL nas anotações relacionadas ao cache agora podem se referir a beans (ou seja, @beanName.method()).
-
ConcurrentMapCacheManager eConcurrentMapCache agora suportam a serialização de entradas de cache por meio de um novo atributostoreByValue.
-
@Cacheable,@CacheEvict,@CachePut e@Caching agora podem ser usados como meta-anotações para criar anotações compostas personalizadas com substituições de atributos.
6. Variantes@RequestMapping compostas
Spring Framework 4.3 apresenta as seguintes variantes compostas em nível de método da anotação@RequestMapping que ajudam a simplificar os mapeamentos para métodos HTTP comuns e melhor expressam a semântica do método do manipulador anotado.
-
@GetMapping
-
@PostMapping
-
@PutMapping
-
@DeleteMapping
-
@PatchMapping
Por exemplo,@GetMapping é uma forma mais curta de dizer@RequestMapping(method = RequestMethod.GET). O exemplo a seguir mostra um controlador MVC que foi simplificado com uma anotação composta@GetMapping.
@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 anotações
Ao usar componentes orientados por anotação ou Java Config, as anotações@RequestScope,@SessionScopee@ApplicationScope podem ser usadas para atribuir um componente ao escopo necessário. Essas anotações não apenas definem o escopo do bean, mas também definem o modo proxy com escopo paraScopedProxyMode.TARGET_CLASS.
O modoTARGET_CLASS significa que o proxy CGLIB será usado para proxy deste bean e garantindo que ele possa ser injetado em qualquer outro bean, mesmo com um escopo mais amplo. O modoTARGET_CLASS permite proxy não apenas para interfaces, mas também para classes.
@RequestScope
@Component
public class LoginAction {
// ...
}
@SessionScope
@Component
public class UserPreferences {
// ...
}
@ApplicationScope
@Component
public class AppPreferences {
// ...
}
8. @RequestAttributee anotações@SessionAttribute
Mais duas anotações para injetar parâmetros da solicitação HTTP nos métodosController apareceram, a saber@RequestAttributee@SessionAttribute. Eles permitem que você acesse alguns atributos preexistentes, gerenciados globalmente (ou seja, fora deController). Os valores para esses atributos podem ser fornecidos, por exemplo, por instâncias registradas dejavax.servlet.Filter ouorg.springframework.web.servlet.HandlerInterceptor.
Suponha que tenhamos registrado a seguinte implementaçãoHandlerInterceptor que analisa a solicitação e adiciona o parâmetrologin à sessão e outro parâmetroquery a uma solicitação:
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);
}
}
Esses parâmetros podem ser injetados em uma instânciaController com anotações correspondentes nos argumentos do método:
@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
O Spring 4.3 suporta as seguintes versões de bibliotecas e gerações de servidores:
-
Hibernate ORM 5.2 (ainda suportando 4.2 / 4.3 e 5.0 / 5.1 também, com 3.6 obsoletos agora)
-
Jackson 2.8 (mínimo aumentado para Jackson 2.6+ a partir da Primavera 4.3)
-
OkHttp 3.x (ainda suportando OkHttp 2.x lado a lado)
-
Netty 4.1
-
Ressaca 1.4
-
Tomcat 8.5.2 e 9.0 M6
Além disso, o Spring 4.3 incorpora o ASM 5.1 atualizado e o Objenesis 2.4 emspring-core.jar.
10. InjectionPoint
A classeInjectionPoint é uma nova classe introduzida no Spring 4.3 queprovides information about places where a particular bean gets injected, seja um parâmetro de método / construtor ou um campo.
Os tipos de informações que você pode encontrar usando esta classe são:
-
ObjetoField - você pode obter o ponto de injeção envolvido como um objetoField usando o métodogetField() se o bean for injetado em um campo
-
MethodParameter - você pode chamar o métodogetMethodParameter() para obter o ponto de injeção envolvido como um objetoMethodParameter se o bean for injetado em um parâmetro
-
Member - chamar o métodogetMember() retornará a entidade que contém o bean injetado envolvido em um objetoMember
-
Class<?> - obtenha o tipo declarado do parâmetro ou campo onde o bean é injetado, usandogetDeclaredType()
-
Annotation[] - usando o métodogetAnnotations(), você pode recuperar uma matriz de objetos de anotação que representam as anotações associadas ao campo ou parâmetro
-
AnnotatedElement - chamegetAnnotatedElement() para obter o ponto de injeção envolvido como um objetoAnnotatedElement
Um caso em que esta classe é muito útil é quando queremos criarLogger beans com base na classe a que pertencem:
@Bean
@Scope("prototype")
public Logger logger(InjectionPoint injectionPoint) {
return Logger.getLogger(
injectionPoint.getMethodParameter().getContainingClass());
}
O bean deve ser definido com um escopoprototype para que um logger diferente seja criado para cada classe. Se você criar um beansingleton e injetar em vários lugares, o Spring retornará o primeiro ponto de injeção encontrado.
Então, podemos injetar o bean em nossoAppointmentsController:
@Autowired
private Logger logger;
11. Conclusão
Neste artigo, discutimos alguns dos novos recursos introduzidos no Spring 4.3.
Cobrimos anotações úteis que eliminam clichês, novos métodos úteis de pesquisa e injeção de dependências e várias melhorias substanciais na web e nos recursos de cache.
Você pode encontrar o código-fonte do artigoon GitHub.