Spring y JDK 8: ¿Sigues usando @Param y name / value en las anotaciones de Spring MVC? Entonces el artículo es para ti


Hola, el lector!


Mientras desarrollaba un proyecto de capacitación en Spring Boot 2, decidí experimentar con @Param en consultas de Spring Data JPA, o más bien, con su ausencia :


 @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 magia, cómo funciona el segundo método está en la publicación anterior Siguiendo el rastro de la Clínica de mascotas de primavera ).


@Param eliminar @Param puedes asegurarte de que Spring funcione bien sin ellos . Escuché sobre un parámetro en la compilación que le permite no duplicar nombres en anotaciones, pero no hice nada especial, así que decidí cavar más profundo hacer un trato


Si todavía usa las anotaciones del título del artículo, Spring Boot y JDK 8, le pido gato:


ACTUALIZACIÓN: @PathVariable @RequestParam @PathVariable y @RequestParam todavía son necesarias para que la aplicación funcione correctamente. Pero sus atributos de value/name ya no son necesarios: los nombres de las variables buscan la coincidencia.


  • Lo primero que intenté fue cambiar el nombre en el parámetro ( mail lugar de email ):


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

    Recibo la recepción y el lugar para el punto de interrupción:


     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] 

  • A continuación, encontramos el lugar donde se determina el nombre del parámetro del método:




Se puede ver que se utilizan 2 estrategias: StandardReflectionParameterNameDiscoverer y LocalVariableTableParameterNameDiscoverer . El primero usa el JDK8 JEP 118: Acceso a los nombres de los parámetros en tiempo de ejecución . De acuerdo con SPR-9643 , si no es posible determinar los nombres de los parámetros de acuerdo con la primera estrategia, Spring intenta usar el "análisis de símbolos de depuración basado en ASM".


  • Hay mucha información sobre los nombres de parámetros de Java 8 en Internet, se -parameters compilación con el indicador -parameters . Voy a la configuración de Spring Boot del proyecto IDEA:


Sí, está realmente incluido ... ¿Pero qué pasa si construyo y ejecuto el proyecto a través de Maven?
¡El resultado es el mismo!


  • Enciendo la salida de depuración en la configuración de Maven, compilo el proyecto y veo:


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

    Parece que el maven-compiler-plugin ya maven-compiler-plugin configurado en spring-boot-starter-parent , de donde spring-boot proyectos spring-boot se heredan por defecto cuando se generan a través de SPRING INITIALIZR . Vamos allí y ( solo para Spring Boot 2 ) seguro, el complemento está configurado allí:


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

  • Finalmente, podemos redefinir la configuración del maven-compiler-plugin en nuestro proyecto, donde establecemos este indicador en false . Comprobar: el proyecto ha comenzado. Y cuando intentamos extraer el método obtenemos:


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


Esto significa que:


  1. nuestro razonamiento es cierto
  2. Basado en la segunda estrategia de ASM, no pude obtener la información (aunque comencé a través de Debug)

RESULTADO: el indicador de -parameters en Spring Boot 2 está habilitado de manera predeterminada, por lo que si hereda de spring-boot-starter-parent , los nombres de los parámetros se definen en @Param de @Param y @Param , @RequestParam , @PathVariable no son necesarios. Menos código, menos errores.


Para Spring Boot 1.x, el indicador de compilación se puede habilitar por la fuerza, ver arriba.


PD: utilicé JDK 8, JDK 11 y Spring Boot 2.1.1 para la investigación


ACTUALIZACIÓN 2: es interesante que para @RequestParam y @PathVariable segundo funcione con la segunda estrategia LocalVariableTableParameterNameDiscoverer basada en la información recibida por ASM del LocalVariableTableParameterNameDiscoverer . Incluyendo para Spring regular (sin arranque) y sin una opción de compilación.


Gracias por su atencion!

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


All Articles