Was ist Frühling? Komponentenbasierte Entwicklung für Java

Der Frühling ist vielleicht das beste komponentenbasierte Framework, das um die Wende des 21. Jahrhunderts entstanden ist. Es verbessert die Art und Weise, wie Entwickler Infrastrukturcode in Java-basierten Anwendungen schreiben und bereitstellen, erheblich. Seit seiner Gründung ist Spring als führendes Framework für die Java-Entwicklung von Unternehmen anerkannt. Als End-to-End-Anwendungsframework spiegelt Spring einige der Java EE-Funktionen wider, bietet jedoch eine Kombination aus Funktionen und Programmierkonventionen, die Sie anderswo nicht finden.

Dieser Artikel stellt Spring und seine zentrale Programmierphilosophie und -methode vor: Inversion der Steuerung und Abhängigkeitsinjektion. Sie werden auch mit Frühlingsanmerkungen und einigen praktischen Codierungsbeispielen beginnen.

Abhängigkeitsinjektion und Inversion der Kontrolle

Die Kernidee von Spring ist, dass Sie Objektbeziehungen nicht selbst verwalten, sondern in das Framework verlagern. Inversion of Control (IOC) ist die Methode zur Verwaltung von Objektbeziehungen. Die Abhängigkeitsinjektion ist der Mechanismus zur Implementierung des IOC. Da diese beiden Konzepte verwandt, aber unterschiedlich sind, betrachten wir sie genauer:

  • Inversion of Control (IOC) macht genau das, was sein Name sagt: Es kehrt die traditionelle Hierarchie der Kontrolle um, um Objektbeziehungen zu erfüllen. Anstatt sich auf Anwendungscode zu verlassen, um zu definieren, wie Objekte zueinander in Beziehung stehen, werden Beziehungen vom Framework definiert. Als Methodik führt IOC die Konsistenz und Vorhersagbarkeit von Objektbeziehungen ein, erfordert jedoch, dass Sie als Entwickler eine fein abgestimmte Kontrolle aufgeben.
  • Dependency Injection (DI) ist ein Mechanismus, bei dem das Framework Abhängigkeiten in Ihre App "einfügt". Es ist die praktische Implementierung von IOC. Die Abhängigkeitsinjektion hängt vom Polymorphismus ab, in dem Sinne, dass sich die Erfüllung eines Referenztyps basierend auf Konfigurationen im Framework ändern kann. Das Framework fügt variable Referenzen ein, anstatt sie manuell im Anwendungscode ausführen zu lassen.

JSR-330

Wie so viel in der Java-Welt wurde das, was als In-the-Wild-Innovation Spring begann, teilweise von der Standardspezifikation übernommen. In diesem Fall ist JSR-330 der Java-Standard. Das Schöne an der JSR-330-Spezifikation ist, dass Sie sie an anderer Stelle verwenden können und sie auch außerhalb des Frühlings an anderer Stelle verwenden werden. Sie können es ohne Spring verwenden. Der Frühling bringt jedoch noch viel mehr auf den Tisch.

Beispiel 1: Federabhängigkeitsinjektion

Die Inversion der Steuerung und die Abhängigkeitsinjektion lassen sich am besten anhand ihrer Verwendung verstehen. Daher beginnen wir mit einem kurzen Programmierbeispiel.

Angenommen, Sie modellieren ein Auto. Wenn Sie in einfachem altem Java modellieren, verfügt die CarKlasse möglicherweise über ein Schnittstellenmitglied, das auf eine EngineSchnittstelle verweist , wie in Listing 1 gezeigt.

Listing 1. Objektbeziehungen in einfachem altem Java

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

Listing 1 enthält eine Schnittstelle für einen EngineTyp und eine Klasse für den konkreten CarTyp, die auf die verweist Engine. (Beachten Sie, dass sich diese in einem realen Programmierszenario in separaten Dateien befinden.) Wenn Sie jetzt eine CarInstanz erstellen , legen Sie die Zuordnung wie in Listing 2 gezeigt fest.

Listing 2. Erstellen eines Autos mit der Motorschnittstelle

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

Beachten Sie, dass Sie zuerst das CarObjekt erstellen . Anschließend erstellen Sie ein neues Objekt, das die EngineSchnittstelle erfüllt , und weisen es dem CarObjekt manuell zu . So funktionieren Objektassoziationen in einfachem altem Java.

Modellieren von Klassen und Objekten im Frühjahr

Schauen wir uns jetzt das gleiche Beispiel im Frühjahr an. Hier können Sie so etwas wie das tun, was in Listing 3 gezeigt wird. Sie beginnen mit der CarKlasse, aber in diesem Fall fügen Sie ihr eine Anmerkung hinzu : @Inject.

Listing 3. Beispiel für die Verwendung der Annotation @Inject in Spring

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

Wenn Sie die @InjectAnmerkung verwenden (oder @Autowired, wenn Sie es vorziehen), wird Spring angewiesen, den Kontext zu durchsuchen und basierend auf einer Reihe von Regeln automatisch ein Objekt in die Referenz einzufügen.

Betrachten Sie als Nächstes die @Componentin Listing 4 gezeigte Anmerkung.

Listing 4. @ Komponentenanmerkung

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

Das Kommentieren einer Klasse mit @Componentteilt Spring mit, dass sie für die Erfüllung von Injektionen verfügbar ist. In diesem Fall InlineSixCylEnginewürde das injiziert, weil es verfügbar ist und die Schnittstellenanforderungen der Zuordnung erfüllt. Im Frühjahr wird dies als "automatisch verdrahtete" Injektion bezeichnet. (Weitere Informationen zu Spring's @AutowiredAnnotation finden Sie weiter unten .)

Entkopplung als Gestaltungsprinzip

Durch die Umkehrung der Steuerung mit Abhängigkeitsinjektion wird eine Quelle konkreter Abhängigkeit aus Ihrem Code entfernt. Nirgendwo im Programm gibt es einen fest codierten Verweis auf die EngineImplementierung. Dies ist ein Beispiel für die Entkopplung als Software-Design-Prinzip. Durch das Entkoppeln des Anwendungscodes von der Implementierung wird die Verwaltung und Wartung Ihres Codes vereinfacht. Die Anwendung weiß weniger darüber, wie ihre Teile zusammenpassen, aber es ist viel einfacher, Änderungen an jedem Punkt im Anwendungslebenszyklus vorzunehmen.

@Autowired vs @Inject

@Autowiredund @Injectdas Gleiche tun. Dies @Injectist jedoch die Java-Standardanmerkung, während sie @Autowiredspezifisch für Spring ist. Sie dienen beide dem gleichen Zweck, der DI-Engine anzuweisen, dem Feld oder der Methode ein passendes Objekt zu injizieren. Sie können beide im Frühjahr verwenden.

Überblick über das Spring Framework

Nachdem Sie einige Spring-Codes gesehen haben, geben wir einen Überblick über das Framework und seine Komponenten. Wie Sie sehen können, besteht das Framework aus vier Hauptmodulen, die in Pakete unterteilt sind. Der Frühling bietet Ihnen ein hohes Maß an Flexibilität bei den verwendeten Modulen.

  • Kernbehälter
    • Ader
    • Bohne
    • Kontext
    • Ausdruckssprache
  • Aspektorientierte Programmierung (AOP)
    • AOP
    • Aspekte
    • Instrumentierung
  • Datenzugriff und Integration
    • JDBC
    • JPA / ORM
    • JMS
    • Transaktionen
  • Netz
    • Web / REST
    • Servlet
    • Streben

Anstatt hier alles zu behandeln, beginnen wir mit zwei der am häufigsten verwendeten Spring-Funktionen.

Ein neues Projekt starten: 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

Wenn Sie einen REST-Endpunkt erstellen und JSON von der Methode zurückgeben möchten, können Sie die Methode mit Anmerkungen versehen @ResponseBody. Die Antwort wird dann automatisch als JSON gepackt. In diesem Fall geben Sie ein Objekt von der Methode zurück.

Verwenden von MVC mit Spring Web

Ähnlich wie bei Struts kann das Spring Web-Modul problemlos für ein echtes Model-View-Controller-Setup verwendet werden. In diesem Fall würden Sie eine Zuordnung in der angegebenen Vorlagensprache (wie Thymeleaf) zurückgeben, und Spring würde die Zuordnung auflösen, das Modell bereitstellen, das Sie an sie übergeben, und die Antwort rendern.

Beispiel 3: Frühling mit JDBC

Lassen Sie uns nun etwas Interessanteres mit unserem Anforderungshandler tun: Lassen Sie uns einige Daten aus einer Datenbank zurückgeben. In diesem Beispiel verwenden wir die H2-Datenbank. Zum Glück unterstützt Spring Boot die speicherinterne H2-DB sofort.