¿Qué es la primavera? Desarrollo basado en componentes para Java

Spring es quizás el mejor de los marcos basados ​​en componentes que surgieron a principios del siglo XXI. Mejora enormemente la forma en que los desarrolladores escriben y entregan código de infraestructura en aplicaciones basadas en Java. Desde sus inicios, Spring ha sido reconocido como un marco líder para el desarrollo empresarial de Java. Como marco de aplicación de un extremo a otro, Spring refleja algunas de las capacidades de Java EE, pero ofrece una combinación de características y convenciones de programación que no encontrará en ningún otro lugar.

Este artículo presenta Spring y su filosofía y metodología de programación central: Inversión de control e inyección de dependencia. También comenzará con las anotaciones de Spring y un par de ejemplos prácticos de codificación.

Inyección de dependencia e inversión de control

La idea central de Spring es que en lugar de administrar las relaciones de objetos usted mismo, las descargue en el marco. La inversión de control (IOC) es la metodología utilizada para gestionar las relaciones de objetos. La inyección de dependencia es el mecanismo para implementar la COI. Dado que estos dos conceptos están relacionados pero son diferentes, considerémoslos más de cerca:

  • La inversión de control (IOC) hace exactamente lo que su nombre dice: invierte la jerarquía tradicional de control para cumplir las relaciones de objeto. En lugar de depender del código de la aplicación para definir cómo se relacionan los objetos entre sí, el marco define las relaciones. Como metodología, IOC introduce consistencia y previsibilidad en las relaciones de objeto, pero requiere que usted, como desarrollador, renuncie a un control detallado.
  • La inyección de dependencia (DI) es un mecanismo en el que el marco "inyecta" dependencias en su aplicación. Es la implementación práctica del COI. La inyección de dependencia depende del polimorfismo, en el sentido de que permite que el cumplimiento de un tipo de referencia cambie en función de las configuraciones en el marco. El marco inyecta referencias de variables en lugar de completarlas manualmente en el código de la aplicación.

JSR-330

Al igual que en el mundo de Java, lo que comenzó como una innovación salvaje, Spring, ha sido absorbido en parte por las especificaciones estándar. En este caso, JSR-330 es el estándar de Java. Lo bueno de la especificación JSR-330 es que puede usarla en otros lugares y la verá en uso en otros lugares, más allá de Spring. Puede usarlo sin usar Spring. Sin embargo, Spring aporta mucho más a la mesa.

Ejemplo # 1: inyección de dependencia de Spring

La inversión de control y la inyección de dependencias se comprenden mejor usándolos, por lo que comenzaremos con un ejemplo de programación rápida.

Digamos que está modelando un automóvil. Si está modelando en Java antiguo simple, es posible que tenga un miembro de interfaz en la Carclase para hacer referencia a una Engineinterfaz, como se muestra en el Listado 1.

Listado 1. Relaciones de objeto en Java simple y antiguo

 public Interface Engine() { ... } public class Car { private Engine engine; public Engine getEngine() { ... } public void setEngine(Engine engine) { ... } } 

El Listado 1 contiene una interfaz para un Enginetipo y una clase para el Cartipo concreto , que hace referencia al Engine. (Tenga en cuenta que en un escenario de programación real, estos estarían en archivos separados). Ahora, cuando esté creando una Carinstancia, establecería la asociación como se muestra en el Listado 2.

Listado 2. Creación de un automóvil con la interfaz del motor

 // ... Car newCar = new Car(); Engine sixCylEngine = new InlineSixCylinderEngine(); newCar.setEngine(sixCylEngine ); // Do stuff with the car 

Tenga en cuenta que Carprimero crea el objeto. Luego, crea un nuevo objeto que cumple con la Engineinterfaz y lo asigna manualmente al Carobjeto. Así es como funcionan las asociaciones de objetos en Java antiguo.

Modelado de clases y objetos en Spring

Ahora veamos el mismo ejemplo en Spring. Aquí, usted podría hacer algo como lo que se muestra en el Listado 3. Se comienza con la Carclase, pero en este caso se agrega una anotación a que: @Inject.

Listado 3. Ejemplo de uso de la anotación @Inject en Spring

 public class Car { @Inject private Engine engine; // ... } 

El uso de la @Injectanotación (o @Autowired, si lo prefiere) le dice a Spring que busque el contexto e inyecte automáticamente un objeto en la referencia, según un conjunto de reglas.

A continuación, considere la @Componentanotación, que se muestra en el Listado 4.

Listado 4. @Anotación de componente

 @Component public class InlineSixCylinderEngine implements Engine{ //... } 

Anotar una clase con @Componentle dice a Spring que está disponible para cumplir con las inyecciones. En este caso, InlineSixCylEnginese inyectaría porque está disponible y satisface el requisito de interfaz de la asociación. En primavera, esto se denomina inyección "autowired". (Consulte a continuación para obtener más información sobre la @Autowiredanotación de Spring ).

El desacoplamiento como principio de diseño

La inversión de control con inyección de dependencia elimina una fuente de dependencia concreta de su código. En ninguna parte del programa hay una referencia codificada a la Engineimplementación. Este es un ejemplo de desacoplamiento como principio de diseño de software. Desacoplar el código de la aplicación de la implementación hace que su código sea más fácil de administrar y mantener. La aplicación sabe menos acerca de cómo encajan sus partes, pero es mucho más fácil realizar cambios en cualquier momento del ciclo de vida de la aplicación.

@Autowired vs @Inject

@Autowiredy @Injecthaz lo mismo. Sin embargo, @Injectes la anotación estándar de Java, mientras que @Autowiredes específica de Spring. Ambos tienen el mismo propósito de decirle al motor DI que inyecte el campo o método con un objeto coincidente. Puedes usar cualquiera de los dos en Spring.

Descripción general del marco Spring

Ahora que ha visto algo de código Spring, echemos un vistazo al marco y sus componentes. Como puede ver, el marco consta de cuatro módulos principales, que se dividen en paquetes. Spring le brinda una gran cantidad de flexibilidad con los módulos que usará.

  • Contenedor de núcleo
    • Núcleo
    • Frijol
    • Contexto
    • Lenguaje de expresión
  • Programación orientada a aspectos (AOP)
    • AOP
    • Aspectos
    • Instrumentación
  • Acceso e integración de datos
    • JDBC
    • JPA / ORM
    • JMS
    • Actas
  • Web
    • Web / REST
    • Servlet
    • Puntales

En lugar de cubrir todo aquí, comencemos con dos de las funciones de Spring más utilizadas.

Iniciar un nuevo proyecto: Spring Boot

We'll use Spring Boot to create an example project, which we'll use to demo Spring features. Spring Boot makes starting new projects much easier, as you'll see for yourself. To begin, take a look at the main class shown below. In Spring Boot, we can take a main class with a main() method, and then choose to run it standalone, or package for deployment in a container like Tomcat.

Listing 5 has the outlines of our main class, which will live at the standard src/main/java/hello location.

Listing 5. Main class with Spring Boot

 package hello; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 

Note two things about the above code: First, all of the work is abstracted into the framework. The main class boots up the app, but it doesn't know anything about how the app works or delivers its functionality. Second, the SpringApplication.run() does the actual job of booting the app and passing in the Application class itself. Again, the work the app does is not apparent here.

The @SpringBootApplication annotation wraps up a few standard annotations and tells Spring to look at the package where the main class exists for components. In our previous example, with the car and engine, this would allow Spring to find all classes annotated with @Component and @Inject. The process itself, called component scanning, is highly customizable.

You can build the app with the standard mvn clean install, and you can run it with the Spring Boot goal (mvn spring-boot:run). Before doing that, let's look at this application's pom.xml file.

Listing 6. Starter pom.xml

 com.javaworld what-is-spring 1.0.0  org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE     1.8     org.springframework.boot spring-boot-maven-plugin    

Note two important features in the above code:

  1. The parent element relies on the spring-boot-starter-parent project. This parent project defines a number of useful defaults, such as the default compiler level of JDK 1.8. For the most part, you can just trust that it knows what it's doing. As an example, you can omit the version number for many common dependencies, and SpringBootParent will set the versions to be compatible. When you bump up the parent's version number, the dependency versions and defaults will also change.
  2. The spring-boot-maven-plugin allows for the executable JAR/WAR packaging and in-place run (via the mvn spring-boot:run command).

Adding Spring Web as a dependency

So far, we've been able to use spring-boot to limit how much work we put in to get an app up and running. Now let's add a dependency and see how quickly we can get something in a browser.

Listing 7. Adding Spring Web to a project

  org.springframework.boot spring-boot-starter-web  

Note

Spring will automatically detect what files have changed and compile accordingly. You can just execute mvn spring-boot:run to pickup changes.

Now that we've got a basic project setup, we're ready for our two examples.

Example #2: Building RESTful endpoints with Spring Web

We've used spring-boot-starter-web to bring in several dependencies that are useful for building web applications. Next we'll create a route handler for a URL path. Spring's web support is part of the Spring MVC (Model-View-Controller) module, but don't let that worry you: Spring Web has full and effective support for building RESTful endpoints, as well.

The class whose job it is to field URL requests is known as a controller, as shown in Listing 8.

Listing 8. Spring MVC REST controller

 package hello; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RequestParam; @Controller public class GreetingController { @RequestMapping(value = "/hi", method = RequestMethod.GET) public String hi(@RequestParam(name="name", required=false, defaultValue="JavaWorld") String name, Model model) { return "Hello " + name; } } 

The @Controller annotation

The @Controller annotation identifies a class as a controller. A class marked as a controller is also automatically identified as a component class, which makes it a candidate for auto-wiring. Wherever this controller is needed, it will be plugged into the framework. In this case, we'll plug it into the MVC system to handle requests.

The controller is a specialized kind of component. It supports the @RequestMapping and @ResponseBody annotations that you see on the hi() method. These annotations tell the framework how to map URL requests to the app.

At this point, you can run the app with mvn spring-boot:run. When you hit the /hi URL, you'll get a response like "Hello, JavaWorld."

Notice how Spring has taken the basics of autowiring components, and delivered a whole web framework. With Spring, you don't have to explicitly connect anything together!

The @Request annotations

The @RequestMapping allows you to define a handler for a URL path. Options include defining the HTTP method you want, which is what we've done in this case. Leaving RequestMethod off would instruct the program to handle all HTTP method types.

The @RequestParam argument annotation allows us to map the request parameters directly into the method signature, including requiring certain params and defining default values as we've done here. We can even map a request body to a class with the @RequestBody argument annotation.

REST and JSON response

Si está creando un punto final REST y desea devolver JSON del método, puede anotar el método con @ResponseBody. La respuesta se empaquetará automáticamente como JSON. En este caso, devolverá un objeto del método.

Usando MVC con Spring Web

Al igual que Struts, el módulo Spring Web se puede usar fácilmente para una verdadera configuración de controlador de vista de modelo. En ese caso, devolvería un mapeo en el lenguaje de plantillas dado (como Thymeleaf), y Spring resolvería el mapeo, proporcionaría el modelo que le pasa y generaría la respuesta.

Ejemplo n. ° 3: Spring con JDBC

Ahora hagamos algo más interesante con nuestro controlador de solicitudes: devolvamos algunos datos de una base de datos. Para el propósito de este ejemplo, usaremos la base de datos H2. Afortunadamente, Spring Boot admite la base de datos H2 en memoria lista para usar.