Cos'è la primavera? Sviluppo basato su componenti per Java

La primavera è forse la migliore delle strutture basate sui componenti emerse all'inizio del XXI secolo. Migliora notevolmente il modo in cui gli sviluppatori scrivono e forniscono il codice dell'infrastruttura nelle applicazioni basate su Java. Sin dal suo inizio, Spring è stata riconosciuta come un framework leader per lo sviluppo Java aziendale. Essendo un framework applicativo end-to-end, Spring rispecchia alcune delle funzionalità di Java EE, ma offre una combinazione di funzionalità e convenzioni di programmazione che non troverai altrove.

Questo articolo introduce Spring e la sua filosofia e metodologia di programmazione di base: inversione del controllo e iniezione di dipendenze. Inizierai anche con le annotazioni Spring e un paio di esempi di codifica pratici.

Iniezione di dipendenze e inversione di controllo

L'idea centrale di Spring è che invece di gestire da soli le relazioni con gli oggetti, le scarichi nel framework. Inversion of control (IOC) è la metodologia utilizzata per gestire le relazioni tra oggetti. L'inserimento delle dipendenze è il meccanismo per implementare IOC. Poiché questi due concetti sono correlati ma diversi, consideriamoli più da vicino:

  • Inversion of control (IOC) fa esattamente quello che dice il suo nome: inverte la tradizionale gerarchia di controllo per soddisfare le relazioni oggettuali. Invece di fare affidamento sul codice dell'applicazione per definire il modo in cui gli oggetti si relazionano tra loro, le relazioni sono definite dal framework. Come metodologia, IOC introduce coerenza e prevedibilità alle relazioni tra oggetti, ma richiede che tu, in quanto sviluppatore, rinunci a un controllo dettagliato.
  • L'inserimento delle dipendenze (DI) è un meccanismo in cui il framework "inietta" le dipendenze nella tua app. È l'implementazione pratica del CIO. L'inserimento delle dipendenze dipende dal polimorfismo, nel senso che consente di modificare l'adempimento di un tipo di riferimento in base alle configurazioni del framework. Il framework inserisce riferimenti variabili invece di farli soddisfare manualmente nel codice dell'applicazione.

JSR-330

Come molte altre cose nel mondo Java, quella che era iniziata come un'innovazione selvaggia, Spring, è stata in parte assorbita dalle specifiche standard. In questo caso, JSR-330 è lo standard Java. La cosa bella della specifica JSR-330 è che puoi usarla altrove e la vedrai in uso altrove, oltre la primavera. Puoi usarlo senza usare Spring. Tuttavia, la primavera porta molto di più in tavola.

Esempio # 1: inserimento delle dipendenze Spring

L'inversione del controllo e l'inserimento delle dipendenze sono meglio comprese usandole, quindi inizieremo con un rapido esempio di programmazione.

Dì che stai modellando un'auto. Se stai modellando in un semplice vecchio Java, potresti avere un membro dell'interfaccia sulla Carclasse per fare riferimento a Engineun'interfaccia, come mostrato nel Listato 1.

Listato 1. Relazioni tra oggetti nel semplice vecchio Java

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

Il listato 1 contiene un'interfaccia per un Enginetipo e una classe per il Cartipo concreto , che fa riferimento a Engine. (Nota che in uno scenario di programmazione reale questi sarebbero in file separati.) Ora, quando crei Carun'istanza, devi impostare l'associazione come mostrato nel Listato 2.

Listato 2. Creazione di un'auto con l'interfaccia del motore

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

Nota che crei Carprima l' oggetto. Quindi si crea un nuovo oggetto che soddisfi l' Engineinterfaccia e lo si assegna manualmente Carall'oggetto. È così che funzionano le associazioni di oggetti nel semplice vecchio Java.

Modellazione di classi e oggetti in primavera

Ora diamo un'occhiata allo stesso esempio in primavera. Qui, si potrebbe fare qualcosa di simile a ciò che è mostrato nel Listato 3. Si inizia con la Carclasse, ma in questo caso si aggiunge un'annotazione ad esso: @Inject.

Listato 3. Esempio di utilizzo dell'annotazione @Inject in Spring

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

L'utilizzo @Injectdell'annotazione (o @Autowired, se preferisci) dice a Spring di cercare il contesto e di inserire automaticamente un oggetto nel riferimento, in base a un insieme di regole.

Quindi, considera l' @Componentannotazione, mostrata nel Listato 4.

Listato 4. Annotazione @Component

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

Annotare una classe con @Componentindica a Spring che è disponibile per l'esecuzione di iniezioni. In questo caso, InlineSixCylEngineverrebbe iniettato perché è disponibile e soddisfa i requisiti di interfaccia dell'associazione. In primavera, questa operazione viene chiamata iniezione "autowired". (Vedi sotto per ulteriori informazioni @Autowiredsull'annotazione di Spring .)

Disaccoppiamento come principio di progettazione

L'inversione del controllo con l'inserimento delle dipendenze rimuove una fonte di dipendenza concreta dal codice. In nessun punto del programma è presente un riferimento codificato Engineall'implementazione. Questo è un esempio di disaccoppiamento come principio di progettazione del software. Separare il codice dell'applicazione dall'implementazione semplifica la gestione e la manutenzione del codice. L'applicazione sa meno su come le sue parti si adattano insieme, ma è molto più facile apportare modifiche in qualsiasi momento del ciclo di vita dell'applicazione.

@Autowired vs @Inject

@Autowirede @Injectfai la stessa cosa. Tuttavia, @Injectè l'annotazione standard Java, mentre @Autowiredè specifica per Spring. Entrambi hanno lo stesso scopo di dire al motore DI di iniettare il campo o il metodo con un oggetto corrispondente. Puoi usarne uno in primavera.

Panoramica del framework Spring

Ora che hai visto del codice Spring, diamo una panoramica del framework e dei suoi componenti. Come puoi vedere, il framework è composto da quattro moduli principali, suddivisi in pacchetti. Spring ti offre una discreta flessibilità con i moduli che utilizzerai.

  • Contenitore principale
    • Nucleo
    • Fagiolo
    • Contesto
    • Linguaggio di espressione
  • Programmazione orientata agli aspetti (AOP)
    • AOP
    • Aspetti
    • Strumentazione
  • Accesso e integrazione dei dati
    • JDBC
    • JPA / ORM
    • JMS
    • Transazioni
  • ragnatela
    • Web / REST
    • Servlet
    • Struts

Piuttosto che coprire tutto qui, iniziamo con due delle funzionalità di Spring più comunemente utilizzate.

Avvio di un nuovo progetto: 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

Se stai creando un endpoint REST e desideri restituire JSON dal metodo, puoi annotare il metodo con @ResponseBody. La risposta verrà quindi automaticamente impacchettata come JSON. In questo caso restituirai un oggetto dal metodo.

Utilizzo di MVC con Spring Web

Simile a Struts, il modulo Spring Web può essere facilmente utilizzato per una vera configurazione del controller di visualizzazione del modello. In tal caso, restituiresti una mappatura nel linguaggio di modellazione dato (come Thymeleaf) e Spring risolverà la mappatura, fornirà il modello che le passi e restituirà la risposta.

Esempio # 3: primavera con JDBC

Ora facciamo qualcosa di più interessante con il nostro gestore di richieste: restituiamo alcuni dati da un database. Ai fini di questo esempio, useremo il database H2. Per fortuna, Spring Boot supporta l'H2 DB in memoria fuori dalla scatola.