JavaRush /Blog Java /Random-ES /Bota de primavera Conquest
Surplus
Nivel 37
Москва

Bota de primavera Conquest

Publicado en el grupo Random-ES
¡Buenos días, querido lector! Y es un placer conocerlo, incluso si el nombre pomposo fue la razón principal para abordar el modesto tema sobre el primer contacto con el desarrollo de Spring Boot. Me gustaría compartir mi experiencia al completar la tarea introductoria para una pasantía en el portal JavaRush, presentando una descripción general desde el lado de un estudiante universitario técnico completamente común que quiere probar la solidez de sus conocimientos acumulados. Bota de primavera Conquest - 1De ninguna manera niego la posible presencia de mala educación en el código o método de pensamiento adjunto, y agradezco las críticas constructivas, porque es gracias a los "golpes y magulladuras" que es posible desarrollarse en una dirección profesional. Además, no pretendo en absoluto ser una "panacea" para resolver las condiciones dadas y omito deliberadamente fragmentos individuales del programa, dejando la importancia clave de entrar en un tema relativamente complejo sin las más mínimas consecuencias para el sistema nervioso. Es cierto, es una temeridad negar lo obvio: fue duro para mí y no quedó absolutamente nada claro hasta cierto momento. Y si experimenta sentimientos similares desde el primer encuentro con la tarea, entonces “¡Bienvenido!” Escribamos una aplicación web en Spring Boot usando una analogía simplificada de una prueba de ingreso a una pasantía usando un motor de plantillas Thymeleafy queryconsultas a un servidor MySQL local para filtrar la matriz de información entrante. ¡Entonces empecemos!

Bota de primavera. ¿Qué tipo de animal es y cómo cocinarlo?

En resumen y concisamente, es una excelente herramienta de Pivotel para ahorrar tiempo valioso en el proceso de creación de una aplicación, eliminando la necesidad de conectar directamente bibliotecas de terceros, escribir un impresionante lienzo de mapeo y servlets. Basta con utilizar el constructor Spring Initializr , integrado en IntelliJ IDEA Ultimate Edition (Archivo - Nuevo - Proyecto... - Spring Initializr) o ubicado en el servicio web start.spring.io , especificando paquetes a incluir entre una amplia gama de ofertas.
Bota de primavera Conquest - 2
Siguiendo las especificaciones técnicas expuestas, utilizaremos el estándar de caballeros para crear una aplicación web sencilla utilizando la base de datos MySQL :
  • WEB es el componente principal para desarrollar una aplicación web, incluido un servidor Apache Tomcat local en la dirección estándar localhost:8080 y el marco universal Spring MVC.

  • DevTools : se utiliza para reiniciar rápidamente una aplicación en una JVM activa cuando se detectan cambios en el código compilado o en las plantillas; Además, libera a Thymeleaf de borrar el caché si el motor seleccionado está incluido en el proyecto.

  • JPA es una tecnología necesaria para trabajar con bases de datos y proporciona mapeo relacional de objetos de objetos Java, proporciona una API ( Hibernate en nuestro caso) para administrar, guardar y recuperar entidades.

  • Thymeleaf (Mustache, AngularJS, Vaadin y más) : motor de plantillas para visualización de aplicaciones; Gracias a mi relativa familiaridad con los principios de HTML, elegí Thymeleaf, que impulsó el lenguaje a la piedra angular del mundo.

  • MySQL : conecta los controladores de Java Database Connectivity para ejecutar consultas SQL en la base de datos.
Después de la selección final de los componentes y la creación, obtenemos una arquitectura de aplicación web normal con directorios listos para seguir llenándose. Los fragmentos para interactuar con la parte visual, ya sean estilos gráficos CSS, páginas HTML estándar o funcionalidad JavaScript, deben ubicarse en "recursos" y, en consecuencia, el componente back-end debe ubicarse en "java". También debemos prestar atención al archivo pom.xml en el rango raíz, que almacena la estructura del proyecto y las dependencias entre componentes. Si desea ampliar aún más la funcionalidad con paquetes adicionales o eliminar cosas innecesarias, debe realizar manipulaciones entre etiquetas <dependencies></dependencies>de acuerdo con un método similar.
Bota de primavera Conquest - 3

Primeros pasos hacia un gran futuro

A continuación surge una pregunta bastante interesante y bastante lógica: “¿Qué hacer ahora? ¿Cómo funcionará esto? El programa se basa en los principios de Model-View-Controller: organiza la lectura de entidades de la base de datos conectada (Modelo) y se muestra en la interfaz de usuario con controles (Ver); la comunicación entre componentes y la ejecución de acciones de acuerdo con las solicitudes transmitidas se realiza gracias al Controlador. Es la creación de elementos clave lo que sirve como punto de referencia para el desarrollo continuo. Para evitar una pendiente resbaladiza y mantener el respeto de sus compañeros en el campo de trabajo, debe colocar los componentes en los directorios apropiados (por ejemplo, coloque el archivo Controlador en la carpeta de controladores en la rama "java") y guardar cuidadosamente orden en el lugar de trabajo.

La esencia es una pequeña parte de un gran mecanismo.

O dicho de otro modo, nuestro Modelo según las condiciones marcadas en el problema. Saliendo del tema de discusión y volviendo al proyecto introductorio, podemos afirmar con confianza que existen diferencias mínimas entre las tareas y atenernos al concepto promedio en una revisión posterior. Digamos, notas en un cuaderno, que incluyen:
  • Número de identificación para determinar ubicación en el flujo general;
  • Un mensaje de texto de una determinada cantidad de caracteres;
  • La fecha en que el usuario lo agregó a la lista general;
  • Una variable booleana para determinar “Hecho o no hecho” (“Leído o no leído”).
Por lo tanto, creemos una clase Note en un directorio llamado "entidad" y agreguemos los campos apropiados:
@Entity
public class Note {

   @Id
   @GeneratedValue
   private int id;
   private String message;
   private Date date;
   private boolean done;

   public Note() {
   }

   public Note(String message) {
       this.message = message;
       this.date = new Date();
       this.done = false;
   }
}
Otro desvío del tema de discusión para una mayor comprensión de lo que sucede desde una posición teórica. La conexión entre componentes en Spring se especifica mediante anotaciones : punteros especiales delante de los objetos, cada uno de los cuales desempeña un papel específico en el mecanismo y comienza con el símbolo "@". La anotación @Entity indica a Spring Boot que los datos de clase posteriores pertenecen a la "Entidad", y @Id y @GeneratedValue especifican el campo seleccionado como un identificador con generación automática de un iterador al procesar una matriz de información. Omito deliberadamente agregar Getter y Setter estándar para aumentar la compacidad del formato visual. A continuación, teniendo en cuenta el uso de una base de datos para almacenar registros, pasamos al siguiente paso en el desarrollo de la aplicación: crearemos la interfaz NoteRepository en el directorio "repositorio", un elemento de conexión en la cadena de intercambio, y heredaremos la mayoría repositorio adecuado para trabajos posteriores, indicando la entidad almacenada y el iterador entero al que acceder.
public interface NoteRepository extends JpaRepository<Note, Integer> {
}
En realidad, eso es todo. Breve y conciso. Ahora Spring Boot utilizará el componente creado para organizar las interacciones con la base de datos. Hay relativamente muchos tipos de repositorios heredados con diferentes posibilidades de acción. JpaRepository está en la parte superior de la escalera y tiene el mayor potencial, incluidos CrudRepository y PageAndSortingRepository debajo. No iremos más lejos y nos desviaremos del tema, porque algunas de las sutilezas se pueden encontrar en el sitio web de Pivotel en la documentación técnica. Ahora, después de implementar la imagen de datos y especificar los métodos de comunicación en el lado de la aplicación, debe prestar atención a la creación de una base de datos MySQL en el entorno externo apropiado "MySQL Workbench", preinstalado en la plataforma de escritorio en un ensamblaje del desarrollador oficial. con paquetes adicionales para crear un servidor local:
Bota de primavera Conquest - 4
A continuación, siguiendo las instrucciones del entorno tras pulsar en el icono con el servidor local actual en la ventana principal, creamos un diagrama de tabla según los campos de nuestra entidad (Nota) y lo rellenamos con los datos adecuados. Es necesario aclarar por separado las sutilezas del dialecto MySQL, que requieren atención urgente para lograr con éxito el resultado deseado:
  • No existe un tipo booleano separado como tal. Cualquier acción de procesamiento de solicitudes convertirá "verdadero" o "falso" al valor de bit "1" o "0", respectivamente;
  • La fecha se almacena completamente en el tipo Marca de tiempo. Si utiliza la Fecha, que le resulta familiar, tendrá que limitarse únicamente a la posición en el calendario.
Bota de primavera conquistadora - 5
Una vez finalizados los pasos preparatorios, indicamos “MySQL Workbench” para enviar datos al servidor local haciendo clic en el icono del “relámpago” en la barra de herramientas. Ahora, si la adición de información se completó correctamente, podemos regresar con confianza a nuestro IDE nativo para continuar con el desarrollo agregando la configuración de la base de datos actual a application.properties (generalmente ubicado en el directorio "recursos"):
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
Y finalmente vincular la entidad Note a MySQL mediante anotaciones. @Table indica el uso de una tabla con el nombre y esquema seleccionados, y @Column indica que las variables pertenecen a un campo específico.
@Entity
@Table(name = "test", schema = "test", catalog = "")
public class Note {

   @Id
   @GeneratedValue
   private int id;
   @Column(name = "message")
   private String message;
   @Column(name = "date")
   private Date date;
   @Column(name = "done")
   private boolean done;

   public Note() {
   }

   public Note(String message) {
       this.message = message;
       this.date = new Date();
       this.done = false;
   }
}

Vista o interfaz de usuario

Por desgracia, podemos decir con seguridad lo siguiente: "La visualización de la aplicación se convertirá en el principal obstáculo sin el más mínimo conocimiento teórico o práctico". Para ser sincero, el componente de front-end requirió una cantidad asombrosa de la cantidad total de trabajo y con confianza me puso de los nervios durante un largo período de tiempo. Pero gracias a la asombrosa sencillez de Thymeleaf, fue posible encontrar un compromiso adecuado después de una serie de derrotas encantadoras. Se discutirá más a fondo las complejidades del uso del motor seleccionado, aunque el concepto general se adhiere a una posición similar. La técnica principal es la capacidad de utilizar el HTML más puro y ensamblar la visualización final a partir de fragmentos individuales para evitar múltiples repeticiones de secciones idénticas. Supongamos que la arquitectura de la interfaz de usuario consta de una página principal que consta de una barra de navegación con controles (agregar una nueva entrada, regresar a la página principal) y una tabla dinámica para mostrar entidades ordenadas por el momento en que se agregó la nota en forma ascendente (ASC). o decreciente (DESC). Tomemos como posición estándar la visualización de todos los registros en orden ascendente. De acuerdo con la política jerárquica del motor de plantillas seleccionado, los elementos de visualización de componentes deben ubicarse en la rama "plantillas" del directorio "recursos". Por lo tanto, en futuras manipulaciones con los componentes se tienen en cuenta las condiciones propuestas. Creemos una página principal con el nombre "índice" (o cualquier otro nombre según sus preferencias personales) en una plantilla html5. Por ejemplo:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/head :: head"></head>
<body>
<div class="container">
   <div th:replace="fragments/header :: header"></div>
   <div th:if="${not #lists.isEmpty(notes)}">
       <div th:replace="operations/list :: notebook"></div>
   </div>
   <div th:replace="fragments/footer :: footer"></div>
</div>
</body>
</html>
Entonces, analicemos los componentes clave de la aplicación final. Thymeleaf utiliza una sintaxis separada para indicar el uso de procedimientos y comienza con la palabra clave “th:”, cuyo enlace a la biblioteca se incluye necesariamente en la etiqueta <html> de apertura.
<div th:if="${not #lists.isEmpty(notes)}">
La operación "si" no es completamente diferente de la forma habitual de hacer las cosas y verifica el atributo "notas" entrantes en busca de objetos para su posterior visualización. Vale la pena mencionar por separado la superposición del tema con el uso del Controlador, teniendo en cuenta su uso para organizar la interacción del modelo y la visualización. Muchos momentos vagos toman forma en el futuro, simplemente regresa si quieres.
<head th:replace="operations/list :: notebook"></head>
La operación "reemplazar" indica el reemplazo de un "stub" o un bloque activo con un fragmento seleccionado de la página actual o separada; este último caso se observa claramente en el ejemplo. Copiamos el fragmento llamado “notebook” de “list.html” del directorio “operaciones” al <div></div> del archivo “index”, reemplazando completamente el contenido en el destino final. El saliente tiene el siguiente contenido:
<!DOCTYPE html>
<!--suppress ALL -->
<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:th="http://www.thymeleaf.org">

<div th:fragment="notebook">
   <table class="table table-bordered table-hover horizontal-align">
       <thead>
       <tr>
           <th style="width: 5%">#</th>
           <th style="width: 60%">Message</th>
           <th class="dropdown" style="width: 20%">Date
               <a th:href="@{'/sort/{sortDate}' (sortDate = 'ASC')}"><i class="fa fa-chevron-circle-up"></i></a>
               <a th:href="@{'/sort/{sortDate}' (sortDate = 'DESC')}"><i class="fa fa-chevron-circle-down"></i></a>
           </th>
           <th style="width: 5%">Done</th>
           <th style="width: 5%">Edit</th>
           <th style="width: 5%">Delete</th>
       </tr>
       </thead>
       <tbody>
       <tr th:each="note : ${notes}">
           <td th:text="${note.id}" style="text-align: center">#</td>
           <td th:text="${note.message}">Message</td>
           <td th:text="${#dates.format(note.date, 'EEE, d MMM yyyy HH:mm')}" style="text-align: center">Date</td>
           <td style="text-align: center">
               <i th:if="${note.done} == true" class="fa fa-plus-square-o" style="font-size:20px;color:#337ab7"></i>
               <i th:if="${note.done} == false" class="fa fa-minus-square-o" style="font-size:20px;color:#337ab7"></i>
           </td>
           <td style="text-align: center"><a th:href="@{'/edit/{id}'(id=${note.id})}"><i class="fa fa-edit" style="font-size:20px"></i></a></td>
           <td style="text-align: center"><a th:href="@{'/delete/{id}'(id=${note.id})}"><i class="fa fa-trash" style="font-size:20px"></i></a></td>
       </tr>
       </tbody>
   </table>
</div>
</html>
Volvamos a la descripción general constructiva y repasemos las funciones de Thymeleaf utilizadas en orden, omitiendo la sintaxis HTML estándar o los estilos gráficos utilizados y centrándonos específicamente en comprender el mecanismo del motor de plantillas.
<div th:fragment="notebook">
La operación "fragmento" especifica el nombre del fragmento y permite utilizar el contenido del bloque para el comando "reemplazar". ¡Además! No se excluyen de ninguna manera los usos múltiples dentro de una sola página, lo que nuevamente presenta la analogía con los procedimientos o funciones en los lenguajes de programación.
<a th:href="@{'/sort/{sortDate}' (sortDate = 'ASC')}">
Se utiliza una llamada a la anotación @PostMapping en el Controlador con la asignación “/sort/{sortDate}”, donde {sortDate} es el atributo de dirección de clasificación saliente. Algo similar se puede ver en el siguiente bloque, que agrega un cambio dinámico dependiendo de la posición del elemento seleccionado por el usuario en el bucle de iteración:
<a th:href="@{'/edit/{id}'(id=${note.id})}">
<tr th:each="note : ${notes}">
Enumerar valores es muy similar al uso familiar de un bloque for en la sintaxis de Java: la variable “nota” toma el elemento actual de la matriz de atributos de entrada ${notes} (una matriz de entidades) y se usa para cambiar los valores. Más tarde. Para ser sincero, podríamos dedicar un artículo aparte a enumerar la amplia gama de capacidades de Thymeleaf con ejemplos de aplicaciones prácticas: el motor de plantillas es extremadamente simple y no requiere aprender en absoluto un impresionante bagaje de sintaxis adicional. Las funciones descritas anteriormente se describen en la documentación técnica en el sitio web oficial de los desarrolladores y son de vital importancia para organizar la comunicación con el back-end. Por lo tanto, puedes pasar con confianza a la siguiente y última parte. Por supuesto, adjuntando los componentes restantes de la visualización en un enlace a la aplicación terminada al final del artículo.

Controlador, administrador en una pequeña empresa.

"La piedra angular en la arquitectura de una aplicación web": tal vez no haya forma de encontrar una descripción más precisa de la importancia del componente Controlador en la organización del trabajo del programa: la mayoría de las operaciones se llevan a cabo precisamente mediante el elemento de conexión entre los modelo y la vista. Gracias a la mecánica de acción de Spring Boot, puede utilizar con confianza los métodos de mapeo y solicitud GET/POST sin el menor problema y conectar automáticamente el repositorio de datos. Creemos la clase NoteController en un archivo separado en el directorio "controladores", nuevamente haciendo referencia al uso de la anotación apropiada:
@Controller
public class NoteController {

   private NoteService service;

   @Autowired
   public void setNoteService(NoteService service) {
       this.service = service;
   }

   @GetMapping("/")
   public String list(Model model) {
       return "index";
   }
}
Una mirada atenta puede notar un cambio importante en el diseño de la arquitectura de la aplicación asociado con la adición de un servicio para aislar la lógica empresarial del trabajo con el servicio de administración de bases de datos. Las acciones completadas son necesarias para aumentar la versatilidad del producto terminado y proporcionar un amplio margen para cambiar la funcionalidad de la interfaz de usuario sin la necesidad de cambios en los métodos de comunicación con la base de datos. La representación estándar no se destaca de ninguna manera entre la multitud de similares: la interfaz está ubicada en un directorio separado y está implementada por una clase con la anotación @Service para la detección de Spring Boot:
public interface NoteService {
   Note getNoteById(Integer id);
   void saveNote(Note note);
   void updateNote(Integer id, String message, boolean done);
   void deleteNote(Integer id);
   List<Note> findAll();
}

@Service
public class NoteServiceImpl implements NoteService{

   private NoteRepository repository;

   @Autowired
   public void setProductRepository(NoteRepository repository) {
       this.repository = repository;
   }

   @Override
   public Note getNoteById(Integer id) {
       return repository.findOne(id);
   }

   @Override
   public void saveNote(Note note) {
       repository.save(note);
   }

   @Override
   public void updateNote(Integer id, String message, boolean done) {
       Note updated = repository.findOne(id);
       updated.setDone(done);
       updated.setMessage(message);
       repository.save(updated);
   }

   @Override
   public void deleteNote(Integer id) {
       repository.delete(id);
   }

   @Override
   public List<Note> findAll() {
       return repository.findAll();
   }
}
Volvamos a revisar el controlador y veamos las complejidades de organizar el trabajo utilizando los métodos Spring Boot. La anotación @Autowired indica la necesidad de vincular automáticamente un servicio a una variable específica del tipo apropiado y establecer una conexión con la base de datos. Se debe prestar más atención a la forma en que se comunica la vista, indicada por la anotación @GetMapping("/"), que devuelve una página llamada "índice" cuando recibe una llamada a localhost:8080. Puede utilizar un enfoque diferente, especificando la descripción extendida @RequestMapping(value = "/", método = RequestMethod.GET) o reemplazando el tipo de retorno con un ModelAndView ya preparado. Sin embargo, según el estado actual de la experiencia en la aplicación práctica, no noto diferencias fundamentales en el resultado final y utilizo la opción habitual. Ampliemos el controlador agregando nuevos elementos usando una pestaña adicional. Después de que el usuario hace clic en un elemento de la barra de navegación, se llama a @GetMapping("/new") y se redirige a la página "nueva" desde el directorio de "operaciones", devolviendo un parámetro llamado "mensaje" al confirmar los datos ingresados ​​usando el botón y redirigiendo al bloque principal. Mención especial merece la necesidad de que el nombre de la variable en la ventana de entrada coincida completamente con el nombre del valor transferido.
<input type="text" class="form-control" id="message" th:name="message" placeholder="Enter your note." maxlength="100"/>
@GetMapping("/new")
public String newNote() {
   return "operations/new";
}

@PostMapping("/save")
public String updateNote(@RequestParam String message) {
   service.saveNote(new Note(message));
   return "redirect:/";
}
Se utiliza una técnica similar para actualizar un registro. Después de hacer clic en el control, se llama a la asignación @GetMapping("/edit/{id}") y se transfiere el identificador de la cadena URL, se agrega el atributo "nota" con una entrada para su posterior edición. @RequestParam(value = "done", require = false) boolean done) especificar un valor específico juega un papel clave en el uso de la casilla de verificación cuando se utiliza el motor de plantillas Thymeleaf y está configurado en "falso" de forma predeterminada.
@GetMapping("/edit/{id}")
public String edit(@PathVariable Integer id, Model model) {
   Note note = service.getNoteById(id);
   model.addAttribute("note", note);
   return "operations/edit";
}

@PostMapping("/update")
public String saveNote(@RequestParam Integer id, @RequestParam String message,
                      @RequestParam(value = "done", required = false) boolean done) {
   service.updateNote(id, message, done);
   return "redirect:/";
}
Eliminar elementos de la base de datos es extremadamente simple y no requiere ninguna manipulación significativa llamando a la función de servicio apropiada usando el valor pasado:
@GetMapping("/delete/{id}")
public String delete(@PathVariable Integer id) {
   service.deleteNote(id);
   return "redirect:/";
}
Ahora hagamos pequeños ajustes en los fragmentos terminados y pasemos a una comunicación interesante con MySQL utilizando consultas en Spring Data JPA, agregando por separado una función para administrar el filtrado simple antes de cerrar el Controlador.
@Controller
public class NoteController {

   private String sortDateMethod = "ASC";

   @GetMapping("/")
   public String list(Model model) {
       List<Note> notebook = filterAndSort();
       model.addAttribute("notes", notebook);
       model.addAttribute("sort", sortDateMethod);
       return "index";
   }

private List<Note> filterAndSort() {
   List<Note> notebook = null;
   switch (sortDateMethod) {
       case "ASC":
           notebook = service.findAllByOrderByDateAsc();
           break;
       case "DESC":
           notebook = service.findAllByOrderByDateDesc();
           break;
   }
   return notebook;
}

Consulta tan pequeña, pero tan importante.

Es vergonzoso admitirlo, filtrar valores, contrariamente a lo esperado, resultó ser otro obstáculo en el cumplimiento de la tarea técnica, superando con seguridad el umbral de complejidad establecido por la paginación: dividir la matriz de datos en páginas separadas de un cierto tamaño para su posterior visualización. Lo más probable es que el cansancio acumulado estuviera pasando factura, pero... la inspiración llegó tras un encuentro completamente accidental con las consultas de Query.
public interface NoteRepository extends JpaRepository<Note, Integer> {
   List<Note> findAllByOrderByDateAsc();
   List<Note> findAllByOrderByDateDesc();
}
Spring Data JPA brinda la capacidad de crear consultas de bases de datos altamente granulares que eliminan la necesidad de ordenar la información una vez recibida y tienen una amplia gama de aplicaciones potenciales. Por ejemplo:
List<Note> findAllByOrderByDateAsc();
El método se convertirá en una consulta SQL y mostrará todos los registros (findAll) ordenados (byOrder) por fecha (byDate) en orden ascendente (Asc). Además, puede crear combinaciones complejas y muestras en muchos campos con un único requisito. Digamos, seleccione todos (findAll) los registros completados (byDoneTrue) en orden (byOrder) disminuyendo (Decs) por valor de fecha (byDate):
Page<Note> findAllByDoneTrueOrderByDateDesc(Pageable pageable);

Conclusión u otra confesión de un programador novato.

¡Todo! Puede iniciar la aplicación web de forma segura utilizando la combinación Shift+F10 o haciendo clic en el icono correspondiente. Spring Boot compilará el programa en Apache Maven e instalará un servidor Apache Tomcat local en localhost:8080. Ahora sólo necesitas seguir el enlace en cualquier navegador.
Bota de primavera Conquest - 6
Y, por supuesto, desarrollar una metodología para cumplir con otros requisitos comerciales. El potencial de la aplicación está limitado por el esfuerzo, el ingenio y la imaginación del desarrollador.
Bota de primavera Conquest - 7
Siendo franco y prestando atención al camino recorrido, estoy una y otra vez convencido de la exactitud de la dirección elegida y me doy cuenta de los beneficios de estudiar en el portal educativo JavaRush. Gracias a una variedad de tareas prácticas, fue posible recuperar el atractivo interés por aprender a programar, que había sido completamente suprimido en el programa obsoleto y sorprendentemente aburrido de una institución de educación superior de similar dirección. Cuatro meses de estudiar activamente material en la pila de tecnología de back-end invirtieron mucho más conocimiento en comparación con años enteros de asistir a conferencias y clases de laboratorio. Por extraño que parezca. Deseo que no cedan ante las dificultades de ingresar a material complejo, porque es superando obstáculos que mejoramos y nos desarrollamos profesional y personalmente. Espero que esta pequeña historia me haya ayudado a descubrir algunas ideas nuevas para usar la increíble herramienta llamada SpringBoot. PD : Github .
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION