Spring e JDK 8: Você ainda está usando @Param e nome / valor nas anotações do Spring MVC? Então o artigo é para você


Olá, o leitor!


Enquanto desenvolvia um projeto de treinamento no Spring Boot 2, decidi experimentar o @Param nas consultas JPA do Spring Data, ou melhor, com a ausência deles :


 @Transactional(readOnly = true) public interface UserRepository extends JpaRepository<User, Integer> { @Query("SELECT u FROM User u WHERE LOWER(u.email) = LOWER(:email)") Optional<User> findByEmailIgnoreCase(@Param("email") String email); List<User> findByLastNameContainingIgnoreCase(@Param("lastname") String lastName); } 

(sobre mágica, como o segundo método funciona está na publicação antiga Following the Trail of Spring Pet Clinic ).


@Param remover o @Param você pode garantir que o Spring funcione bem sem eles . Ouvi falar de um parâmetro na compilação que permite que você não duplique nomes nas anotações, mas como não fiz nada de especial, decidi cavar mais fundo fazer um acordo.


Se você ainda usa as anotações do título do artigo, Spring Boot e JDK 8, peço cat:


UPDATE: as @RequestParam e @RequestParam ainda são necessárias para que o aplicativo funcione corretamente. Mas seus atributos de value/name não são mais necessários: a correspondência é pesquisada pelos nomes de variáveis.


  • A primeira coisa que tentei foi alterar o nome no parâmetro ( mail vez de email ):


     @Query("SELECT u FROM User u WHERE LOWER(u.email) = LOWER(:email)") Optional<User> findByEmailIgnoreCase(String mail); 

    Recebo a recepção e o local do ponto de interrupção:


     Caused by: java.lang.IllegalStateException: Using named parameters for method public abstract java.util.Optional ru.javaops.bootjava.restaurantvoting.repository.UserRepository.findByEmailIgnoreCase(java.lang.String) but parameter 'Optional[mail]' not found in annotated query 'SELECT u FROM User u WHERE LOWER(u.email) = LOWER(:email)'! at org.springframework.data.jpa.repository.query.JpaQueryMethod.assertParameterNamesInAnnotatedQuery(JpaQueryMethod.java:125) ~[spring-data-jpa-2.1.3.RELEASE.jar:2.1.3.RELEASE] 

  • Em seguida, encontramos o local em que o nome do parâmetro do método é determinado:




Pode-se observar que duas estratégias são usadas: StandardReflectionParameterNameDiscoverer e LocalVariableTableParameterNameDiscoverer . O primeiro usa o JDK8 JEP 118: acesso aos nomes de parâmetros em tempo de execução . De acordo com o SPR-9643 , se não for possível determinar os nomes dos parâmetros pela primeira estratégia, o Spring tenta usar a "análise de símbolo de depuração baseada em ASM".


  • Há muitas informações sobre os nomes de parâmetros do Java 8 na Internet, é -parameters compilação com o sinalizador -parameters . Eu vou para as configurações do Spring Boot do projeto IDEA:


Sim, está realmente incluído ... Mas e se eu criar e executar o projeto através do Maven?
O resultado é o mesmo!


  • Ative a saída de depuração nas configurações do Maven, compile o projeto e veja:


     [DEBUG] Goal: org.apache.maven.plugins:maven-compiler-plugin:3.8.0:compile (default-compile) ... <parameters default-value="false">true</parameters> 

    Parece que o maven-compiler-pluginmaven-compiler-plugin configurado no spring-boot-starter-parent , de onde spring-boot projetos de spring-boot são herdados por padrão quando gerados pelo SPRING INITIALIZR . Vamos lá e ( apenas para o Spring Boot 2 ), com certeza, o plugin está configurado lá:


      <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <parameters>true</parameters> </configuration> </plugin> 

  • Finalmente, podemos redefinir a configuração do maven-compiler-plugin em nosso projeto, onde definimos esse sinalizador como false . Cheque - o projeto foi iniciado. E ao tentar puxar o método, obtemos:


     Unable to detect parameter names for query method ru.javaops.bootjava.restaurantvoting.repository.UserRepository.findByEmailIgnoreCase! Use @Param or compile with -parameters on JDK 8. 


Isso significa que:


  1. nosso raciocínio está correto
  2. Com base na segunda estratégia do ASM, não consegui obter as informações (embora tenha iniciado o Debug)

RESULTADO: o sinalizador -parameters no Spring Boot 2 é ativado por padrão; portanto, se você herdar do spring-boot-starter-parent , os nomes dos parâmetros serão definidos em @Param e @Param , @RequestParam , @PathVariable não @PathVariable mais necessários. Menos código, menos erros.


Para o Spring Boot 1.x, o sinalizador de compilação pode ser ativado à força, veja acima.


PS: usei o JDK 8, JDK 11 e Spring Boot 2.1.1 para pesquisa


ATUALIZAÇÃO 2: é interessante que para @RequestParam e @PathVariable segundo funcione a segunda estratégia LocalVariableTableParameterNameDiscoverer base nas informações recebidas pelo ASM do bytecode. Incluindo o Spring regular (sem inicialização) e sem uma opção de compilação.


Obrigado pela atenção!

Source: https://habr.com/ru/post/pt440214/


All Articles