Qu'est-ce que le printemps? Développement basé sur des composants pour Java

Spring est peut-être le meilleur des cadres basés sur des composants qui ont émergé au tournant du 21e siècle. Il améliore considérablement la manière dont les développeurs écrivent et fournissent le code d'infrastructure dans les applications Java. Depuis sa création, Spring est reconnu comme un cadre de premier plan pour le développement Java d'entreprise. En tant que cadre d'application de bout en bout, Spring reflète certaines des capacités de Java EE, mais il offre une combinaison de fonctionnalités et de conventions de programmation que vous ne trouverez nulle part ailleurs.

Cet article présente Spring, sa philosophie et sa méthodologie de programmation: inversion du contrôle et injection de dépendances. Vous commencerez également avec les annotations Spring et quelques exemples de codage pratiques.

Injection de dépendance et inversion de contrôle

L'idée centrale de Spring est qu'au lieu de gérer vous-même les relations d'objets, vous les déchargez dans le framework. L'inversion de contrôle (IOC) est la méthodologie utilisée pour gérer les relations d'objets. L'injection de dépendance est le mécanisme de mise en œuvre de l'IOC. Puisque ces deux concepts sont liés mais différents, examinons-les de plus près:

  • L'inversion de contrôle (IOC) fait exactement ce que son nom dit: il inverse la hiérarchie traditionnelle de contrôle pour remplir les relations d'objet. Au lieu de s'appuyer sur le code de l'application pour définir les relations entre les objets, les relations sont définies par le framework. En tant que méthodologie, IOC introduit la cohérence et la prévisibilité des relations d'objet, mais cela vous oblige, en tant que développeur, à renoncer à un contrôle fin.
  • L'injection de dépendances (DI) est un mécanisme dans lequel le framework «injecte» des dépendances dans votre application. C'est la mise en œuvre pratique du CIO. L'injection de dépendances repose sur le polymorphisme, dans le sens où elle permet à la réalisation d'un type de référence de changer en fonction des configurations dans le framework. Le framework injecte des références de variables plutôt que de les remplir manuellement dans le code d'application.

JSR-330

Comme beaucoup dans le monde Java, ce qui a commencé comme une innovation sauvage, Spring, a été en partie absorbé par les spécifications standard. Dans ce cas, JSR-330 est le standard Java. La bonne chose à propos de la spécification JSR-330 est que vous pouvez l'utiliser ailleurs et que vous la verrez utilisée ailleurs, au-delà de Spring. Vous pouvez l'utiliser sans utiliser Spring. Cependant, Spring apporte beaucoup plus à la table.

Exemple # 1: injection de dépendance Spring

L'inversion de contrôle et l'injection de dépendances sont mieux comprises en les utilisant, nous allons donc commencer par un exemple de programmation rapide.

Disons que vous modélisez une voiture. Si vous modélisez dans l'ancien Java, vous pouvez avoir un membre d'interface sur la Carclasse pour référencer une Engineinterface, comme indiqué dans le listing 1.

Listing 1. Relations d'objets dans le vieux Java

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

Le listing 1 contient une interface pour un Enginetype et une classe pour le Cartype concret , qui fait référence au Engine. (Notez que dans un scénario de programmation réel, ceux-ci seraient dans des fichiers séparés.) Maintenant, lorsque vous créez une Carinstance, vous définissez l'association comme indiqué dans le listing 2.

Listing 2. Créer une voiture avec l'interface du moteur

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

Notez que vous créez d'abord l' Carobjet. Vous créez ensuite un nouvel objet qui remplit l' Engineinterface et l'affectez manuellement à l' Carobjet. C'est ainsi que fonctionnent les associations d'objets dans l'ancien Java.

Modélisation de classes et d'objets dans Spring

Regardons maintenant le même exemple au printemps. Ici, vous pouvez faire quelque chose comme ce qui est montré dans le Listing 3. Vous commencez avec la Carclasse, mais dans ce cas vous ajoutez une annotation à elle: @Inject.

Listing 3. Exemple d'utilisation de l'annotation @Inject dans Spring

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

L'utilisation de l' @Injectannotation (ou @Autowired, si vous préférez) indique à Spring de rechercher le contexte et d'injecter automatiquement un objet dans la référence, en fonction d'un ensemble de règles.

Ensuite, considérez l' @Componentannotation, montrée dans le Listing 4.

Listing 4. Annotation @Component

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

Annoter une classe avec @Componentindique à Spring qu'elle est disponible pour réaliser des injections. Dans ce cas, le InlineSixCylEngineserait injecté car il est disponible et satisfait à l'exigence d'interface de l'association. Au printemps, cela s'appelle une injection «autowired». (Voir ci-dessous pour plus d'informations sur l' @Autowiredannotation de Spring .)

Le découplage comme principe de conception

L'inversion de contrôle avec injection de dépendances supprime une source de dépendance concrète de votre code. Nulle part dans le programme il n'y a une référence codée en dur à la Enginemise en œuvre. Ceci est un exemple de découplage en tant que principe de conception de logiciel. Le découplage du code d'application de l'implémentation facilite la gestion et la maintenance de votre code. L'application en sait moins sur la façon dont ses composants s'emboîtent, mais il est beaucoup plus facile d'apporter des modifications à tout moment du cycle de vie de l'application.

@Autowired vs @Inject

@Autowiredet @Injectfaites la même chose. Cependant, @Injectc'est l'annotation standard Java, alors qu'elle @Autowiredest spécifique à Spring. Ils ont tous deux le même objectif: dire au moteur DI d'injecter le champ ou la méthode avec un objet correspondant. Vous pouvez utiliser l'un ou l'autre au printemps.

Vue d'ensemble du framework Spring

Maintenant que vous avez vu du code Spring, prenons un aperçu du framework et de ses composants. Comme vous pouvez le voir, le framework se compose de quatre modules principaux, qui sont divisés en packages. Spring vous offre une grande flexibilité avec les modules que vous utiliserez.

  • Conteneur principal
    • Coeur
    • Haricot
    • Le contexte
    • Langue d'expression
  • Programmation orientée aspect (AOP)
    • AOP
    • Aspects
    • Instrumentation
  • Accès aux données et intégration
    • JDBC
    • JPA / ORM
    • JMS
    • Transactions
  • la toile
    • Web / REST
    • Servlet
    • Struts

Plutôt que de tout couvrir ici, commençons avec deux des fonctionnalités Spring les plus couramment utilisées.

Démarrage d'un nouveau projet: 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 vous créez un point de terminaison REST et que vous souhaitez renvoyer JSON à partir de la méthode, vous pouvez annoter la méthode avec @ResponseBody. La réponse sera ensuite automatiquement packagée en JSON. Dans ce cas, vous retournerez un objet de la méthode.

Utilisation de MVC avec Spring Web

Semblable à Struts, le module Spring Web peut facilement être utilisé pour une véritable configuration modèle-vue-contrôleur. Dans ce cas, vous renverriez un mappage dans le langage de modèle donné (comme Thymeleaf), et Spring résoudrait le mappage, fournirait le modèle que vous lui passiez et rendrait la réponse.

Exemple # 3: Spring avec JDBC

Maintenant, faisons quelque chose de plus intéressant avec notre gestionnaire de requêtes: retournons des données d'une base de données. Pour les besoins de cet exemple, nous utiliserons la base de données H2. Heureusement, Spring Boot prend en charge la base de données H2 en mémoire prête à l'emploi.