Los 10 errores más comunes al trabajar con la plataforma Spring. Parte 2

Saludo, Khabrovsk. Entonces, la traducción de la segunda parte de un artículo preparado especialmente para los estudiantes del curso "Desarrollador en el Marco de Primavera" llegó a tiempo. La primera parte se puede leer aquí .



Spring es quizás una de las plataformas de desarrollo de Java más populares. Esta es una herramienta poderosa, pero bastante difícil de aprender. Sus conceptos básicos son bastante fáciles de entender y aprender, pero lleva tiempo y esfuerzo convertirse en un desarrollador experimentado de Spring.

En este artículo, veremos algunos de los errores más comunes cometidos al trabajar en Spring y, en particular, relacionados con el desarrollo de aplicaciones web y el uso de la plataforma Spring Boot. Como se señaló en el sitio web de Spring Boot , Spring Boot adopta un enfoque estandarizado para crear aplicaciones listas para usar, y este artículo seguirá este enfoque. Dará una serie de recomendaciones que se pueden utilizar de manera efectiva en el desarrollo de aplicaciones web estándar basadas en Spring Boot.

En caso de que no esté muy familiarizado con la plataforma Spring Boot, pero quiera experimentar con los ejemplos de este artículo, creé un repositorio de GitHub con materiales adicionales para este artículo . Si en algún momento está un poco confundido mientras lee este artículo, le aconsejaría que cree un clon de este repositorio y experimente con el código en su computadora.




Error común n. ° 6: no utilizar la validación de datos basada en anotaciones


Imaginemos que nuestro servicio TopTalent de los ejemplos anteriores necesita un punto final para agregar nuevos datos TopTalent. Además, supongamos que por alguna razón realmente importante, cada nombre que agregue debe tener exactamente 10 caracteres de longitud. Esto se puede implementar, por ejemplo, de la siguiente manera:

@RequestMapping("/put") public void addTopTalent(@RequestBody TopTalentData topTalentData) { boolean nameNonExistentOrHasInvalidLength = Optional.ofNullable(topTalentData) .map(TopTalentData::getName) .map(name -> name.length() == 10) .orElse(true); if (nameNonExistentOrInvalidLength) { //   } topTalentService.addTopTalent(topTalentData); } 

Sin embargo, el código anterior no solo está mal estructurado, sino que en realidad no es una solución "limpia". Realizamos varios tipos de validación de datos (es decir, verificamos que el objeto TopTalentData no TopTalentData nulo, que el valor del campo TopTalentData.name no es nulo y que la longitud del campo TopTalentData.name es de 10 caracteres), y también lanzamos una excepción si los datos son incorrectos.

Todo esto se puede hacer con mayor precisión utilizando el validador de Hibernate en Spring. Primero reescribamos el método addTopTalent , agregando soporte para la validación de datos:

 @RequestMapping("/put") public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData) { topTalentService.addTopTalent(topTalentData); } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) { //    } 

Además, debemos indicar qué validación de propiedad queremos realizar en la clase TopTalentData :

 public class TopTalentData { @Length(min = 10, max = 10) @NotNull private String name; } 

Spring ahora interceptará la solicitud y la verificará antes de llamar al método, por lo que no se requerirán verificaciones manuales adicionales.

El objetivo deseado también se puede lograr creando sus propias anotaciones. En condiciones reales, generalmente tiene sentido usar sus propias anotaciones solo cuando sus necesidades exceden las capacidades del conjunto incorporado de restricciones de Hibernate , pero para este ejemplo, imaginemos que @Length anotaciones @Length no existen. Puede crear un validador de datos que verifique la longitud de una cadena creando dos clases adicionales: una para la validación y otra para las propiedades de anotación:

 @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = { MyAnnotationValidator.class }) public @interface MyAnnotation { String message() default "String length does not match expected"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; int value(); } @Component public class MyAnnotationValidator implements ConstraintValidator<MyAnnotation, String> { private int expectedLength; @Override public void initialize(MyAnnotation myAnnotation) { this.expectedLength = myAnnotation.value(); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { return s == null || s.length() == this.expectedLength; } } 


Tenga en cuenta que en estos casos, la aplicación correcta del principio de separación de responsabilidad requiere marcar la propiedad como válida si su valor es nulo (s == null en el método isValid ), y luego usar la anotación @NotNull si esto es adicionalmente necesario para esta propiedad:

 public class TopTalentData { @MyAnnotation(value = 10) @NotNull private String name; } 

Error común n. ° 7: uso de configuraciones heredadas basadas en XML


El uso de XML era una necesidad cuando se trabajaba con versiones anteriores de Spring, pero ahora la mayoría de las tareas de configuración se pueden implementar utilizando código Java y anotaciones. Las configuraciones XML ahora actúan como código de plantilla adicional y opcional.

En este artículo (así como en los materiales complementarios del repositorio de GitHub), las anotaciones se usan para configurar Spring, y Spring sabe qué componentes JavaBean deben vincularse porque el paquete raíz se anota usando la anotación compuesta @SpringBootApplication, de esta manera:

 @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 

Esta anotación compuesta (consulte la documentación de Spring para obtener más información al respecto) simplemente le dice a la plataforma Spring qué paquetes escanear para extraer los componentes JavaBean. En nuestro caso particular, esto significa que se utilizarán los siguientes subpaquetes de co.kukurin para la unión:

  • Componente (TopTalentConverter, MyAnnotationValidator)
  • @RestController (TopTalentController)
  • Repositorio (TopTalentRepository)
  • Clases de servicio (TopTalentService)

Si tenemos clases adicionales que tienen la anotación @Configuration , también se comprobará la configuración de Java.

Error común # 8: no usar perfiles de configuración


Cuando se desarrollan sistemas de servidor, un problema común es cambiar entre diferentes configuraciones (como regla, estas son configuraciones para los entornos operativos y de desarrollo). En lugar de cambiar manualmente varios parámetros en cada transición entre los modos de prueba y funcionamiento, es más eficiente usar perfiles de configuración.

Imagine el caso cuando en el entorno de desarrollo local utiliza la base de datos en RAM, y en el entorno de la operación real de su aplicación, se utiliza la base de datos MySQL. Esto significa esencialmente que utilizará diferentes URL y, presumiblemente, diferentes credenciales para acceder a cada una de estas bases de datos. Veamos cómo se puede implementar esto usando dos archivos de configuración:

APLICACIÓN DE ARCHIVO. YAML

 # set default profile to 'dev' spring.profiles.active: dev # production database details spring.datasource.url: 'jdbc:mysql://localhost:3306/toptal' spring.datasource.username: root spring.datasource.password: 


APLICACIÓN DE ARCHIVO-DEV.YAML

 spring.datasource.url: 'jdbc:h2:mem:' spring.datasource.platform: h2 


Debe suponerse que al trabajar con código, no querrá realizar algunas acciones aleatorias con una base de datos destinada al entorno operativo, por lo que tiene sentido seleccionar el perfil para el entorno de desarrollo (DEV) como el perfil predeterminado. Posteriormente, puede anular manualmente el perfil de configuración en el servidor especificando -Dspring.profiles.active=prod para la JVM. Además, el perfil de configuración predeterminado se puede especificar en la variable de entorno del sistema operativo.

Error común número 9. Falla en el uso del mecanismo de inyección de dependencia


El uso adecuado del mecanismo de inyección de dependencias en Spring significa que Spring puede vincular todos sus objetos escaneando todas las clases de configuración necesarias. Esto es útil para aflojar las interdependencias y facilita enormemente las pruebas. En lugar de vincular estrechamente las clases, algo como esto:

 public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController() { this.topTalentService = new TopTalentService(); } } 

... permitimos que la plataforma Spring se una:

 public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController(TopTalentService topTalentService) { this.topTalentService = topTalentService; } } 

La conferencia de Mishko Hevery sobre el canal Google Tech Talks explica en detalle por qué debería usarse la inyección de dependencia, pero aquí veremos cómo se usa este mecanismo en la práctica. En la división de responsabilidad ("Error común # 3"), creamos las clases de servicio y controlador. Supongamos que queremos probar un controlador, suponiendo que la clase TopTalentService funciona correctamente. Podemos insertar un objeto simulador en lugar de la implementación del servicio actual creando una clase de configuración separada:

 @Configuration public class SampleUnitTestConfig { @Bean public TopTalentService topTalentService() { TopTalentService topTalentService = Mockito.mock(TopTalentService.class); Mockito.when(topTalentService.getTopTalent()).thenReturn( Stream.of("Mary", "Joel").map(TopTalentData::new).collect(Collectors.toList())); return topTalentService; } } 

Después de eso, podemos implementar el objeto simulador SampleUnitTestConfig plataforma Spring que necesitamos usar SampleUnitTestConfig como fuente de configuración:
@ContextConfiguration(classes = { SampleUnitTestConfig.class })

Posteriormente, esto nos permitirá usar la configuración contextual para incrustar el componente JavaBean personalizado en una prueba unitaria.

Error común número 10. Falta de prueba o prueba incorrecta


A pesar de que la idea de las pruebas unitarias no es en absoluto nueva, parece que muchos desarrolladores “se olvidan” (especialmente si no es obligatorio ) o la pasan demasiado tarde. Obviamente, esto está mal, porque las pruebas no solo le permiten verificar el funcionamiento correcto del código, sino que también sirven como documentación que muestra cómo debe comportarse la aplicación en diversas situaciones.
Cuando prueba servicios web, rara vez ejecuta pruebas unitarias excepcionalmente "limpias", porque para una conexión HTTP generalmente necesita usar el servlet Spring DispatcherServlet y ver qué sucede cuando recibe una solicitud HttpServletRequest real (es decir, resulta una prueba de integración que usa validación, serialización, etc.). Una solución elegante y probada es utilizar REST Assured , una biblioteca Java para probar convenientemente los servicios REST, con MockMVC. Considere el siguiente fragmento de código con inyección de dependencia:

 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { Application.class, SampleUnitTestConfig.class }) public class RestAssuredTestDemonstration { @Autowired private TopTalentController topTalentController; @Test public void shouldGetMaryAndJoel() throws Exception { // given MockMvcRequestSpecification givenRestAssuredSpecification = RestAssuredMockMvc.given() .standaloneSetup(topTalentController); // when MockMvcResponse response = givenRestAssuredSpecification.when().get("/toptal/get"); // then response.then().statusCode(200); response.then().body("name", hasItems("Mary", "Joel")); } } 

SampleUnitTestConfig vincula la implementación sustitutiva de la clase TopTalentService al TopTalentController , y todas las demás clases se enlazan utilizando la configuración estándar obtenida escaneando paquetes basados ​​en el paquete de la clase Aplicación. RestAssuredMockMvc usa simplemente para configurar un entorno ligero y enviar una solicitud GET al /toptal/get .

Use Spring profesionalmente


Spring es una plataforma poderosa con la que es fácil comenzar, pero lleva tiempo y un poco de esfuerzo dominarla por completo. Si se toma el tiempo para familiarizarse con esta plataforma, al final, sin duda, aumentará la eficiencia de su trabajo, lo ayudará a crear un código más limpio y aumentará sus calificaciones como desarrollador.

Le recomiendo que preste atención a Spring In Action : este es un buen libro orientado a la aplicación que analiza muchos temas importantes relacionados con la plataforma Spring.

En este punto, la traducción de este artículo llegó a su fin.
Lee la primera parte .

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


All Articles