Lassen Sie uns mit Java ein supereinfaches Webframework erstellen

Überblick

Heutzutage hören wir in der Webanwendungs-Community häufig Wörter wie "Cloud Native" und "Microservices". In der Java-Entwicklung werden häufig ** Frameworks ** wie Spring Boot verwendet, um diese Architekturen zu implementieren.

Als ich jedoch mit dem Java-Verhalten nicht so vertraut war und plötzlich mit der Entwicklung eines solchen ** Frameworks ** begann, sagte ich: "Warum funktioniert das so?" "Anmerkung? Was ist das?" "DI? Nurupoja" Sie könnten in einer Situation wie "Ist es nicht?"

In diesem Artikel hoffe ich, dass ich, um solche Zweifel auszuräumen, selbst ein super ~~ ähnliches ~~ einfaches Framework erstellen und den internen Mechanismus des Java-Frameworks so gut wie möglich verstehen kann. Ich werde.

Endgültige Kundenprobe

Spring Boot-Schnellstart Unter Bezugnahme auf https://projects.spring.io/spring-boot/#quick-start lauten die Client-Programme, die das diesmal realisierte Framework verwenden, wie folgt.

src/main/java/hello/SampleController.java


package hello;

...<Import weggelassen>

@Controller
public class SampleController {

    @Resource
    private SampleService service;

    @RequestMapping("/hello")
    public String home() {
        return service.hello();
    }

    public static void main(String[] args) {
        EasyApplication.run(SampleController.class, args);
    }
}

Führen Sie dieses Client-Programm aus und erwarten Sie, dass "Hello World!" Angezeigt wird, wenn es von einem http-Client (z. B. Curl) aufgerufen wird.

$ curl http://localhost:8080/hello
Hello World!

Annahme

Zu verwendende Techniken und Wörter

(Zusätzliche Bemerkungen)

Quellcode

https://github.com/hatimiti/easyframework Befolgen Sie die nachstehenden Anweisungen, um den Quellcode in Ihrer lokalen Umgebung zu platzieren.

$ cd ~
$ mkdir java
$ git clone https://github.com/hatimiti/easyframework.git
$ cd easyframework

Versuche zu rennen

#Lauf mit Gradle. Bei der ersten Ausführung dauert es einige Zeit.
$ ./gradlew clean build run

> Task :run
Registered Components => {class hello.SampleServiceImpl=hello.SampleServiceImpl@723279cf, interface hello.SampleService=hello.SampleServiceImpl@723279cf, class hello.SampleController=hello.SampleController@10f87f48}
Registered Controller => /hello - { "path": "/hello", "instance": hello.SampleController@10f87f48", "method": public java.lang.String hello.SampleController.home()" }
<============-> 93% EXECUTING [16s]
> :run

Wenn die oben genannte Situation auftritt, starten Sie eine andere Konsole und überprüfen Sie mit curl oder wget.

$ curl http://localhost:8080/hello
Hello World!

* Wenn die Ausführung erfolgreich ist, beenden Sie die ursprüngliche Konsole mit ** Strg + C **. </ font>

Dateistruktur

Die Struktur des Quellcodes ist wie folgt.

~/java/easyframework/src
|--main
|  |--java
|  |  |--easyframework
|  |  |  |--Component.java
|  |  |  |--Controller.java
|  |  |  |--EasyApplication.java
|  |  |  |--RequestMapping.java
|  |  |--hello
|  |  |  |--SampleController.java
|  |  |  |--SampleService.java
|  |  |  |--impl
|  |  |  |  |--SampleServiceImpl.java

Einfaches Konfigurationsdiagramm

Abbildung 1: Konfiguration

Das Beispiel ist in die Client-Seite (Benutzer) und die Framework-Seite (Anbieter) unterteilt. Achten Sie beim Lesen immer darauf, auf welche Ebene Sie sich beziehen.

Anmerkung

** Anmerkungen ** sind Funktionen, die in Java SE 5 eingeführt wurden. Was Sie normalerweise sehen, ist "@Override" "[@Deprecated](https: / /docs.oracle.com/javase/jp/8/docs/api/java/lang/Deprecated.html) "" [@SuppressWarnings](https://docs.oracle.com/javase/jp/8/docs/ api / java / lang / SuppressWarnings.html) “und„ [@FunctionalInterface](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/FunctionalInterface] in Java 8 hinzugefügt Geht es nicht um ".html)"? Dies sind auch Anmerkungen.

Anmerkungen werden auch als "Kommentartypen" bezeichnet und sind, wie der Name schon sagt, Typen, die dem Quellcode Anmerkungen hinzufügen (bedeuten). Beispielsweise bedeutet "@Override", dass die Methode "der Superklasse überlagert" ist, und "@Deprecated" bedeutet, dass die Methode oder der Typ "veraltet" ist. Kann im Quellcode ausgedrückt werden.

Da Anmerkungen als Typen behandelt werden, definieren Sie sie zum Erstellen eigener Anmerkungen mit dem Schlüsselwort " @ interface </ font>" sowie "class" und "interface".

public @interface Hoge {
}

Anmerkungen können auch Attributinformationen definieren.

public @interface Fuga {
    String value() default "";
}

Mit Attributen können Sie zusätzliche Informationen angeben, wenn Sie Anmerkungen verwenden.

@Fuga(value = "/hello")
public void x() {
}

Wenn das Attribut als Wert bezeichnet wird und nur ein Wert angegeben wird, kann der Attributname weggelassen werden.

@Fuga("/hello")
public void x() {
}

Wenn Sie den Standardwert zum Zeitpunkt der Definition auf Standard setzen, können Sie die Angabe weglassen.

@Fuga 
public void x() {
}

Im Folgenden sind die in diesem Artikel erforderlichen Anmerkungsdefinitionen aufgeführt.

  • Details der Definition werden später beschrieben.

src/main/java/easyframework/Controller.java


package easyframework;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Inherited;

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}

src/main/java/easyframework/Component.java


package easyframework;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Inherited;

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}

src/main/java/easyframework/RequestMapping.java


package easyframework;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Inherited;

@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
  String value() default "";
}

Was ist also nützlich bei diesen Anmerkungen? Sicher im Beispielprogramm

@Controller
public class SampleController {
...

Ebenso wird der zuvor erstellte @Controller am Anfang der normalen Klassendefinition hinzugefügt.

** Kommentieren Sie den Quellcode **, aber das ist alles, was Sie tun müssen, um mit Javadoc zu kommentieren. Ja, im Gegensatz zu Javadoc ist die Annotation Teil des Quellcodes und kein Kommentar </ font>. Da es Teil des Quellcodes ist, können Sie mithilfe der später beschriebenen Reflection-API </ font> selbst definieren, wie die Zielanmerkung funktioniert. Werden. Umgekehrt bewirkt das einfache Hinzufügen einer Anmerkung nichts und wird genauso behandelt wie (oder weniger als) Javadoc.

Wenn Sie eindeutige Anmerkungen verarbeiten möchten, müssen Sie hier die Reflection-API </ font> verwenden.

Meta-Annotation

In der obigen Anmerkungsdefinition wurde der Anmerkungsdefinition selbst eine Anmerkung hinzugefügt. Die mit einer solchen Annotation verknüpfte Annotation wird als ** Meta-Annotation ** bezeichnet.

# Name Attribut Überblick
1 @Inherited - Gibt an, dass die Anmerkungsinformationen von den untergeordneten Klassen der Klasse geerbt werden, zu der diese Anmerkung hinzugefügt wird.
2 @Target ElementType ElementType gibt an, wo in der Quelle diese Anmerkung hinzugefügt werden kann.
3 @Retention RetentionPolicy RetentionPolicy gibt an, wie lange diese Anmerkungsinformationen aufbewahrt werden.
# Attribut Wert Überblick
1 ElementType ANNOTATION_TYPE Kommentierter Typ
2 CONSTRUCTOR Konstrukteur
3 FIELD Feld
4 LOCAL_VARIABLE lokal
5 METHOD Methode
6 PACKAGE Paket
7 PARAMETER Streit
8 TYPE Klasse, Schnittstelle, Aufzählung
9 TYPE_PARAMETER Generika(Geben Sie Parameter ein)
10 TYPE_USE Überall, wo Sie die Form benutzen
11 RetentionPolicy SOURCE Zur Kompilierungszeit: ○
Klassendatei: ×
Laufzeit: ×
12 CLASS Zur Kompilierungszeit: ○
Klassendatei: ○
Laufzeit: ×
13 RUNTIME Zur Kompilierungszeit: ○
Klassendatei: ○
Laufzeit: ○

In Bezug auf RetentionPolicy muss beim Lesen aus der Reflection-API "RUNTIME" festgelegt werden, sodass sich die Anzahl der RUNTIME-Spezifikationen natürlich erhöht. Alle diesmal erstellten Anmerkungen geben auch RUNTIME an.

Reflexions-API

In den folgenden Beispielen wird die ** Reflexions-API ** (im Folgenden als Reflexion bezeichnet) verwendet, daher werde ich sie einführen, bevor ich mit der Erläuterung des Framework-Teils beginne. Reflection ist eine API zur Realisierung von "Metaprogrammierung" in Java und kann Metainformationen in einem Programm verarbeiten. Die Metainformationen sind hauptsächlich der Klassenname, der Typ der Klasse, in welchem Paket sie definiert ist, welche Art von Feld sie hat und welchen Typ sie hat, welche Art von Methode definiert ist und was der Rückgabewert ist. Informationen, die aus der Klassendefinition (.class) gelesen werden können, z. Mit Reflection können Sie auch Werte in ** (einschließlich privaten) ** Feldern festlegen und Methoden ausführen. Mit Reflection können Sie Ihr Java-Programm daher besser oder schlechter steuern und dabei die Java-Syntax und -Kapselung ignorieren. Das Framework ist so konzipiert, dass Benutzer (Clients) es mithilfe dieser Reflexion in nicht geringem Maße problemlos entwickeln können.

Schauen wir uns nun ein Beispiel an, das Reflexion verwendet. (Ich verwende jshell, eine Java 9 REPL) Dies ist ein Beispiel für das Aufrufen einer Instanz der String-Klasse und das Anzeigen von "Hallo, Reflexion". In der Standardausgabe.

Wenn Sie keine Reflexion verwenden


$ jshell
jshell> System.out.println(new String("Hello, Reflection."))
Hello, Reflection.

Bei Verwendung von Reflexion


$ jshell
jshell> Class<String> strClass = String.class
strClass ==> class java.lang.String

jshell> java.lang.reflect.Constructor c = strClass.getConstructor(String.class)
c ==> public java.lang.String(java.lang.String)

jshell> System.out.println(c.newInstance("Hello, Reflection."))
Hello, Reflection.

In dem Beispiel, in dem keine Reflektion verwendet wird, wird eine Instanz der String-Klasse mit dem neuen Schlüsselwort erstellt. In dem Beispiel, in dem Reflection verwendet wird, wird sie durch Aufrufen der newInstance () -Methode des Konstruktortyps ohne Verwendung des neuen Schlüsselworts erstellt. .. Außerdem basiert die Konstruktorinstanz auf einer Instanz vom Typ Klasse, die von String.class erstellt wurde. Der Klassentyp wird im nächsten Abschnitt erläutert.

Auf diese Weise ist es möglich, eine Instanz zu erstellen und eine Methode basierend auf den Definitionsinformationen der Klasse aufzurufen.

java.lang.Class-Typ

So erhalten Sie eine Instanz vom Typ Klasse

Es gibt drei Hauptmethoden, um eine Instanz des Klassentyps abzurufen.

  1. .class: Von der Typdefinition abrufen
  2. Class.getClass (): Von Instanz abrufen
  3. Klasse # forName (String): Aus String holen
//<Typ>.class:Holen Sie sich aus der Typdefinition
jshell> Class<?> c1 = String.class
c1 ==> class java.lang.String

// Class.getClass():Holen Sie sich von der Instanz
jshell> Class<?> c2 = "Hello".getClass()
c2 ==> class java.lang.String

// Class#forName(String):Holen Sie sich aus einer Zeichenfolge
jshell> Class<?> c3 = Class.forName("java.lang.String")
c3 ==> class java.lang.String

//Die erfassten Klasseninformationen sind identisch
jshell> c1 == c2
$13 ==> true

jshell> c2 == c3
$14 ==> true

jshell> c1.equals(c2)
$15 ==> true

jshell> c2.equals(c3)
$16 ==> true

Insbesondere wird die Angabe von " .class" häufig beim Generieren eines Loggers zum Zeitpunkt der Protokollausgabe angezeigt.

Logger LOG = LoggerFactory.getLogger(String.class);

Klasse In der Klasse definierte Hauptmethoden

Unter Verwendung der Klasseninstanz, die durch das obige Verfahren erhalten wurde, können verschiedene Metainformationen über die Klasse erhalten werden. Nachfolgend sind die wichtigsten Methoden aufgeführt, die in der Klasse Class definiert sind. Die in dieser Probe verwendete ist ein Auszug.

  • Zitiert aus Javadoc (https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Class.html)
# Rückgabewert Methode static Überblick
1 Class<?> forName(String className) Gibt ein Klassenobjekt zurück, das einer Klasse oder Schnittstelle mit dem angegebenen Zeichenfolgennamen zugeordnet ist.
2 Annotation[] getAnnotations() Gibt die für dieses Element vorhandenen Anmerkungen zurück.
3 <A extends Annotation> A getAnnotation(Class<A> annotationClass) Gibt eine Annotation des angegebenen Typs für dieses Element zurück, falls vorhanden, oder andernfalls null.
4 Field[] getDeclaredFields() Gibt ein Array von Feldobjekten zurück, die alle Felder widerspiegeln, die von der Klasse oder Schnittstelle deklariert wurden, die von diesem Klassenobjekt dargestellt wird.
5 Field[] getFields() Gibt ein Array zurück, das ein Feldobjekt enthält, das alle zugänglichen öffentlichen Felder der Klasse oder Schnittstelle widerspiegelt, die von diesem Klassenobjekt dargestellt werden.
6 Method[] getDeclaredMethods() Gibt ein Array zurück, das ein Methodenobjekt enthält, das alle deklarierten Methoden der Klasse oder Schnittstelle widerspiegelt, die von diesem Klassenobjekt dargestellt werden. Dies schließt public, protected, default ein(package)Schließt Zugriffs- und private Methoden ein, schließt jedoch geerbte Methoden aus.
7 Method[] getMethods() Gibt ein Array zurück, das ein Methodenobjekt enthält, das alle öffentlichen Methoden der Klasse oder Schnittstelle widerspiegelt, die von diesem Klassenobjekt dargestellt werden. Dies schließt diejenigen ein, die in Klassen oder Schnittstellen deklariert sind, sowie diejenigen, die von Superklassen und Superschnittstellen geerbt wurden.
8 boolean isInterface() Bestimmt, ob das angegebene Klassenobjekt einen Schnittstellentyp darstellt.
9 boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) Gibt true zurück, wenn ein Kommentar des angegebenen Typs zu diesem Element vorhanden ist, andernfalls false.
10 T newInstance() Erstellen Sie eine neue Instanz der Klasse, die von diesem Klassenobjekt dargestellt wird.

Beispiel für das Abrufen einer Methode der String-Klasse


$ jshell
jshell> String.class
$1 ==> class java.lang.String

// getMethods()Enthält keine privaten Methoden, aber auch Definitionen, die von der übergeordneten Klasse geerbt wurden
jshell> Arrays.asList($1.getMethods()).forEach(System.out::println)
public boolean java.lang.String.equals(java.lang.Object)
public int java.lang.String.length()
public java.lang.String java.lang.String.toString()
public int java.lang.String.hashCode()
public void java.lang.String.getChars(int,int,char[],int)
public int java.lang.String.compareTo(java.lang.Object)
public int java.lang.String.compareTo(java.lang.String)
public int java.lang.String.indexOf(int)
public int java.lang.String.indexOf(java.lang.String)
public int java.lang.String.indexOf(java.lang.String,int)
public int java.lang.String.indexOf(int,int)
public static java.lang.String java.lang.String.valueOf(int)
public static java.lang.String java.lang.String.valueOf(char)
public static java.lang.String java.lang.String.valueOf(boolean)
public static java.lang.String java.lang.String.valueOf(float)
public static java.lang.String java.lang.String.valueOf(double)
public static java.lang.String java.lang.String.valueOf(java.lang.Object)
public static java.lang.String java.lang.String.valueOf(long)
public static java.lang.String java.lang.String.valueOf(char[])
public static java.lang.String java.lang.String.valueOf(char[],int,int)
public java.util.stream.IntStream java.lang.String.codePoints()
public boolean java.lang.String.isEmpty()
public char java.lang.String.charAt(int)
public int java.lang.String.codePointAt(int)
public int java.lang.String.codePointBefore(int)
public int java.lang.String.codePointCount(int,int)
public int java.lang.String.offsetByCodePoints(int,int)
public byte[] java.lang.String.getBytes(java.nio.charset.Charset)
public byte[] java.lang.String.getBytes()
public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException
public void java.lang.String.getBytes(int,int,byte[],int)
public boolean java.lang.String.contentEquals(java.lang.CharSequence)
public boolean java.lang.String.contentEquals(java.lang.StringBuffer)
public boolean java.lang.String.equalsIgnoreCase(java.lang.String)
public int java.lang.String.compareToIgnoreCase(java.lang.String)
public boolean java.lang.String.regionMatches(int,java.lang.String,int,int)
public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int)
public boolean java.lang.String.startsWith(java.lang.String)
public boolean java.lang.String.startsWith(java.lang.String,int)
public boolean java.lang.String.endsWith(java.lang.String)
public int java.lang.String.lastIndexOf(java.lang.String,int)
public int java.lang.String.lastIndexOf(java.lang.String)
public int java.lang.String.lastIndexOf(int,int)
public int java.lang.String.lastIndexOf(int)
public java.lang.String java.lang.String.substring(int,int)
public java.lang.String java.lang.String.substring(int)
public java.lang.CharSequence java.lang.String.subSequence(int,int)
public java.lang.String java.lang.String.concat(java.lang.String)
public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence)
public java.lang.String java.lang.String.replace(char,char)
public boolean java.lang.String.matches(java.lang.String)
public boolean java.lang.String.contains(java.lang.CharSequence)
public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String)
public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String)
public java.lang.String[] java.lang.String.split(java.lang.String)
public java.lang.String[] java.lang.String.split(java.lang.String,int)
public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[])
public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable)
public java.lang.String java.lang.String.toLowerCase(java.util.Locale)
public java.lang.String java.lang.String.toLowerCase()
public java.lang.String java.lang.String.toUpperCase(java.util.Locale)
public java.lang.String java.lang.String.toUpperCase()
public java.lang.String java.lang.String.trim()
public java.util.stream.IntStream java.lang.String.chars()
public char[] java.lang.String.toCharArray()
public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[])
public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[])
public static java.lang.String java.lang.String.copyValueOf(char[])
public static java.lang.String java.lang.String.copyValueOf(char[],int,int)
public native java.lang.String java.lang.String.intern()
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

// getDeclaredMethods()Enthält private Methoden, enthält jedoch keine Definitionen, die von der übergeordneten Klasse geerbt wurden
jshell> Arrays.asList($1.getDeclaredMethods()).forEach(System.out::println)
byte java.lang.String.coder()
public boolean java.lang.String.equals(java.lang.Object)
public int java.lang.String.length()
public java.lang.String java.lang.String.toString()
public int java.lang.String.hashCode()
public void java.lang.String.getChars(int,int,char[],int)
public int java.lang.String.compareTo(java.lang.Object)
public int java.lang.String.compareTo(java.lang.String)
public int java.lang.String.indexOf(int)
static int java.lang.String.indexOf(byte[],byte,int,java.lang.String,int)
public int java.lang.String.indexOf(java.lang.String)
public int java.lang.String.indexOf(java.lang.String,int)
public int java.lang.String.indexOf(int,int)
static void java.lang.String.checkIndex(int,int)
public static java.lang.String java.lang.String.valueOf(int)
public static java.lang.String java.lang.String.valueOf(char)
public static java.lang.String java.lang.String.valueOf(boolean)
public static java.lang.String java.lang.String.valueOf(float)
public static java.lang.String java.lang.String.valueOf(double)
public static java.lang.String java.lang.String.valueOf(java.lang.Object)
public static java.lang.String java.lang.String.valueOf(long)
public static java.lang.String java.lang.String.valueOf(char[])
public static java.lang.String java.lang.String.valueOf(char[],int,int)
private static java.lang.Void java.lang.String.rangeCheck(char[],int,int)
public java.util.stream.IntStream java.lang.String.codePoints()
public boolean java.lang.String.isEmpty()
public char java.lang.String.charAt(int)
public int java.lang.String.codePointAt(int)
public int java.lang.String.codePointBefore(int)
public int java.lang.String.codePointCount(int,int)
public int java.lang.String.offsetByCodePoints(int,int)
public byte[] java.lang.String.getBytes(java.nio.charset.Charset)
void java.lang.String.getBytes(byte[],int,byte)
public byte[] java.lang.String.getBytes()
public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException
public void java.lang.String.getBytes(int,int,byte[],int)
public boolean java.lang.String.contentEquals(java.lang.CharSequence)
public boolean java.lang.String.contentEquals(java.lang.StringBuffer)
private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder)
public boolean java.lang.String.equalsIgnoreCase(java.lang.String)
public int java.lang.String.compareToIgnoreCase(java.lang.String)
public boolean java.lang.String.regionMatches(int,java.lang.String,int,int)
public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int)
public boolean java.lang.String.startsWith(java.lang.String)
public boolean java.lang.String.startsWith(java.lang.String,int)
public boolean java.lang.String.endsWith(java.lang.String)
public int java.lang.String.lastIndexOf(java.lang.String,int)
public int java.lang.String.lastIndexOf(java.lang.String)
public int java.lang.String.lastIndexOf(int,int)
public int java.lang.String.lastIndexOf(int)
static int java.lang.String.lastIndexOf(byte[],byte,int,java.lang.String,int)
public java.lang.String java.lang.String.substring(int,int)
public java.lang.String java.lang.String.substring(int)
public java.lang.CharSequence java.lang.String.subSequence(int,int)
public java.lang.String java.lang.String.concat(java.lang.String)
public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence)
public java.lang.String java.lang.String.replace(char,char)
public boolean java.lang.String.matches(java.lang.String)
public boolean java.lang.String.contains(java.lang.CharSequence)
public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String)
public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String)
public java.lang.String[] java.lang.String.split(java.lang.String)
public java.lang.String[] java.lang.String.split(java.lang.String,int)
public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[])
public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable)
public java.lang.String java.lang.String.toLowerCase(java.util.Locale)
public java.lang.String java.lang.String.toLowerCase()
public java.lang.String java.lang.String.toUpperCase(java.util.Locale)
public java.lang.String java.lang.String.toUpperCase()
public java.lang.String java.lang.String.trim()
public java.util.stream.IntStream java.lang.String.chars()
public char[] java.lang.String.toCharArray()
public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[])
public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[])
public static java.lang.String java.lang.String.copyValueOf(char[])
public static java.lang.String java.lang.String.copyValueOf(char[],int,int)
public native java.lang.String java.lang.String.intern()
private boolean java.lang.String.isLatin1()
static void java.lang.String.checkOffset(int,int)
static void java.lang.String.checkBoundsOffCount(int,int,int)
static void java.lang.String.checkBoundsBeginEnd(int,int,int)
static byte[] java.lang.String.access$100(java.lang.String)
static boolean java.lang.String.access$200(java.lang.String)

// getFields()Enthält keine privaten Felder, aber auch Definitionen, die von der übergeordneten Klasse geerbt wurden
jshell> Arrays.asList($1.getFields()).forEach(System.out::println)
public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER

// getDeclaredFields()Enthält private, enthält jedoch keine Definitionen, die von der übergeordneten Klasse geerbt wurden
jshell> Arrays.asList($1.getDeclaredFields()).forEach(System.out::println)
private final byte[] java.lang.String.value
private final byte java.lang.String.coder
private int java.lang.String.hash
private static final long java.lang.String.serialVersionUID
static final boolean java.lang.String.COMPACT_STRINGS
private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields
public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER
static final byte java.lang.String.LATIN1
static final byte java.lang.String.UTF16

Typdefinition für Reflexion

Andere Definitionen des Reflexionstyps als die Klassenklasse sind hauptsächlich ** java.lang.reflect. html) ** Unter dem Paket definiert. Dies ist auch ein Auszug aus dem in dieser Probe verwendeten.

# Klasse Rückgabewert Methode Überblick
1 Field boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) Gibt true zurück, wenn ein Kommentar des angegebenen Typs zu diesem Element vorhanden ist, andernfalls false.
2 void setAccessible(boolean flag) Setzt das Zugriffsflag für dieses Objekt auf den angegebenen booleschen Wert.
3 void set(Object obj, Object value) Setzt das Feld des angegebenen Objektarguments, das von diesem Feldobjekt dargestellt wird, auf den neuen angegebenen Wert.
4 Method boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) Gibt true zurück, wenn ein Kommentar des angegebenen Typs zu diesem Element vorhanden ist, andernfalls false.
5 <T extends Annotation> T getAnnotation(Class<T> annotationClass) Gibt eine Annotation des angegebenen Typs für dieses Element zurück, falls vorhanden, oder andernfalls null.
6 Object invoke(Object obj, Object... args) Ruft die zugrunde liegende Methode auf, die von diesem Methodenobjekt mit den angegebenen Parametern für das angegebene Objekt dargestellt wird.

Die zu beachtenden Punkte sind wie folgt.

  • Sie müssen ** setAccessible (true) ** aufrufen, um auf private Informationen zugreifen zu können.
  • Um einen Wert in einem Feld festzulegen, rufen Sie ** field.set (Zielinstanz, Wert, den Sie festlegen möchten) ** auf.
  • Wenn Sie eine Methode ausführen möchten, rufen Sie ** method.invoke (Zielinstanz, Argument ...) ** auf.

Vorsichtsmaßnahmen bei der Verwendung von Reflexion

  • Nur in der Framework-Ebene verwenden
  • Wie oben gezeigt, werden sogar private Inhalte neu geschrieben. Wenn also ein allgemeiner Entwickler anfängt, Reflexion zu verwenden, können das ursprünglich vorgesehene Klassendesign und die ursprünglich geplanten Ideen zerstört werden. In der tatsächlichen Entwicklung halte ich es für besser, die Mitglieder, die die Framework-Ebene und die gemeinsame Ebene verwalten, zu bitten, Korrekturen vorzunehmen.
  • Seien Sie vorsichtig mit der Leistung
  • Reflection liest die Definitionsinformationen zur ** Laufzeit ** und verarbeitet sie, sodass die Leistung nicht so gut ist wie bei der normalen Verarbeitung. Infolgedessen verwenden einige Frameworks die Kompilierung als Auslöser, um .classes und .javas zu generieren, um die Auswirkungen auf die Laufzeit zu verringern.
  • Wenn Sie es weiterhin verwenden müssen, sollten Sie die durch Reflektion gelesenen Feld- und Methodeninformationen zwischenspeichern.
  • Achten Sie auf die Sicherheit, um keine Sicherheitslücken zu schaffen
  • Da die Reflektion über eine Zeichenfolge instanziiert werden kann, führt die Übergabe der Anforderungsparameter vom Client an die Reflection-API zu unerwarteten Sicherheitslücken. Achten Sie daher auf den externen Zugriff. ..

Rahmen

Im Folgenden werden wir die Konfiguration von EasyFramework.java vorstellen, die den Kern des Frameworks bildet. Die erste ist die run () -Methode, die die einzige öffentliche Methode dieses Frameworks ist.

src/main/java/easyframework/EasyApplication.java


package easyframework;

public class EasyApplication {

...
      public static void run(Class<?> clazz, String... args) {
          scanComponents(clazz);
          injectDependencies();
          registerHTTPPaths();
          startHTTPServer();
      }
...

}

Es werden die folgenden vier Methoden aufgerufen.

  • scanComponents (clazz) …… ** Komponenten suchen und registrieren **.
  • injectDependencies () …… Setzt die registrierte Komponente auf das entsprechende Feld ** DI **.
  • registerHTTPPaths () …… Registriert die Pfade, die die HTTP-Aufrufpunkte unter den registrierten Komponenten sein werden.
  • startHTTPServer () …… Startet den Server und wartet auf Anforderungen.

Ich werde die Details von jedem beschreiben.

Komponentenscan

Dieses Framework verwaltet den Ausführungspunkt (Controller) der später beschriebenen HTTP-Anforderung und die Instanzgruppe </ font>, die innerhalb des Frameworks zur Realisierung von DI erforderlich ist. Hier werden diese ** Instanzen unter der Kontrolle des Frameworks als "Komponenten" ** bezeichnet. Ähnliches gilt für Spring Boot @ComponentScan. Durch die Verwendung der Java) -Anmerkung wird automatisch die zu verwaltende Komponente gefunden. Zu diesem Zeitpunkt wird bestimmt, welche Klasse als Komponente verwaltet wird, ob die Annotation "@Controller" oder "@Component" hinzugefügt wird.

Hier wird das gesuchte Ergebnis im Komponentenfeld von EasyApplication registriert.

src/main/java/easyframework/EasyApplication.java


public class EasyApplication {

    /** KEY=the class of the component, VALUE= A instance of the component */
    private final static Map<Class<?>, Object> components = new HashMap<>();

...

src/main/java/easyframework/EasyApplication.java


    private static List<Class<?>> scanClassesUnder(String packageName) {
        //Paketnamen.Brechen/In Trennzeichen konvertieren.
        String packagePath = packageName.replace('.', '/');
        //Suchen Sie im Klassenpfader nach Ressourcen im Zielpfad.
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        URL root = cl.getResource(packagePath);

        // .Holen Sie sich die Klassendatei.
        File[] files = new File(root.getFile())
            .listFiles((dir, name) -> name.endsWith(".class"));
        return Arrays.asList(files).stream()
                .map(file -> packageName + "." + file.getName().replaceAll(".class$", ""))
                //Paketnamen+Rufen Sie basierend auf dem Klassennamen die Klassenklasse sofort ab und geben Sie sie als Liste zurück.
                .map(fullName -> uncheck(() -> Class.forName(fullName)))
                .collect(Collectors.toList());
    }

src/main/java/easyframework/EasyApplication.java


    // @Mit Komponentenanmerkung@Behandeln Sie den Typ mit der Controller-Annotation als Komponente
    private static boolean hasComponentAnnotation(Class<?> clazz) {
        return clazz.isAnnotationPresent(Component.class)
                || clazz.isAnnotationPresent(Controller.class);
    }

    private static void scanComponents(Class<?> source) {
        List<Class<?>> classes = scanClassesUnder(source.getPackage().getName());
        classes.stream()
            .filter(clazz -> hasComponentAnnotation(clazz))
            .forEach(clazz -> uncheck(() -> {
                //Erstellen und registrieren Sie eine Instanz des Komponentenzieltyps
                Object instance = clazz.newInstance();
                components.put(clazz, instance);
            }));
        System.out.println("Registered Components => " + components);
    }

DI(Dependency Injection)

Der Grund, warum ich beim Aufrufen einer Methode keine NullPointerException erhalten habe, obwohl ich im ersten Beispiel keine neue Instanz vom Typ SampleService erstellt habe, ist, dass ich diesen ** DI ** -Mechanismus verwendet habe.

src/main/java/hello/SampleController.java


    @Resource
    private SampleService service; //← Es sollte null sein, weil es nicht neu ist?

    @RequestMapping("/hello")
    public String home() {
        return service.hello(); //← Warum keine NullPointerException auftritt! ??
    }

Es kann übersetzt werden als DI = Abhängigkeitsinjektion, aber was ist ** Abhängigkeit **?

Stellen Sie es sich hier als Abhängigkeit = Objekt (Instanz) vor. Daher ist DI ein Mechanismus zum Injizieren von Objekten, und es ist möglich, lose zwischen Klassen zu koppeln. FactoryMethod ist ein Entwurfsmuster für die lose Kopplung zwischen Klassen, aber DI erfordert nicht einmal die Verwendung von Factory-Methoden oder des neuen Schlüsselworts, sodass es einen Schritt weiter lose gekoppelt werden kann.

Die Teile, die DI in diesem Framework sind, sind die folgenden Prozesse.

src/main/java/easyframework/EasyApplication.java


    private static void injectDependencies() {
        //Vorgescannte und registrierte Komponenten(components)Überprüfen Sie alle Felder.
        components.forEach((clazz, component) -> {
            Arrays.asList(clazz.getDeclaredFields()).stream()
                // @Rufen Sie nur die Felder mit der Ressourcenanmerkung ab.
                .filter(field -> field.isAnnotationPresent(Resource.class))
                .forEach(field -> uncheck(() -> {
                    //Machen Sie es zugänglich, damit es auch in privaten Bereichen gehandhabt werden kann.
                    field.setAccessible(true);
                    //Komponente(components)Holen Sie sich eine Instanz dieses Typs von und injizieren Sie(Aufbau)Machen.
                    field.set(component, components.get(field.getType()));
                }));
        });
    }

Die Definition von SampleService, die dieses Mal eingefügt werden soll, ist bereits als Komponente registriert, da @Component wie unten gezeigt hinzugefügt wird.

src/main/java/hello/impl/SampleServiceImpl.java


@Component
public class SampleServiceImpl implements SampleService {
...

Da @Resource zum Feld des zu injizierenden sampleControllers hinzugefügt wird und der Typ übereinstimmt, wird die Instanz von der zuvor gesehenen Methode injizierenDependencies () injiziert.

src/main/java/hello/SampleController.java


@Controller
public class SampleController {

    @Resource
    private SampleService service; //← Framework-verwaltete Instanzen werden hier eingefügt

...

Mit diesem Mechanismus führt der Aufruf von service.hello () nicht zu einer NullPointerException.

Das Framework, das den DI-Mechanismus einführt, wird als "DI-Container" bezeichnet. SpringFramework, Google Guice, Seasar2 usw. sind DI-Container.

Die Einführung von DI bietet folgende Vorteile.

  • Der Instanzgenerierungscode (neues Schlüsselwort) des Join-Teils zwischen Klassen wird entfernt, und die erforderliche Instanz kann zum erforderlichen Zeitpunkt eingefügt (zugewiesen) werden.
  • Durch Definieren des Objekts, das in eine externe Datei (XML usw.) eingefügt werden soll, kann es ein Punkt sein, der später erweitert werden soll.
  • Das Framework kann den Umfang der zu injizierenden Instanz steuern.
  • Umfang ist der Umfang einer Instanz.
  • singleton: Teilen Sie eine Instanz als Ganzes.
  • Prototyp: Erstellen Sie eine Instanz für jede Injektion.
  • Sitzung: Teilen Sie dieselbe Instanz während derselben Sitzung.
  • Anfrage: Teilen Sie dieselbe Instanz während derselben Anfrage.
  • Insbesondere wenn Singleton ausgewählt ist, wird eine Instanz von mehreren Anforderungen gemeinsam genutzt (Multithread-Umgebung). Vermeiden Sie daher variable Felder in den Feldern von Objekten, die von Singleton verwaltet werden. Machen wir das. </ font> Wenn Sie ein Variablenfeld behalten möchten, müssen Sie es mit einem Prototyp behandeln.
  • Es wird einfacher, AOP aus Framework anzuwenden.
  • Das Framework verwaltet Instanzen und erleichtert so das Eingreifen in die Querschnittsverarbeitung.

HTTP-Anfrage

Dieser Abschnitt behandelt den Callpoint-Registrierungsprozess, wenn "http : // localhost: 8080 / hello" von einem HTTP-Client aufgerufen wird. Die Teile, in denen die entsprechende Verarbeitung im Framework ausgeführt wird, sind wie folgt.

Registrieren Sie das Ergebnis im Controller-Feld von EasyApplication.

src/main/java/easyframework/EasyApplication.java


public class EasyApplication {

...
    /** KEY=path, VALUE= A instance and methods */
    private final static Map<String, HTTPController> controllers = new HashMap<>();

...

src/main/java/easyframework/EasyApplication.java


    private static void registerHTTPPaths() {
        //Überprüfen Sie alle Komponenten unter Framework-Verwaltung.
        components.entrySet().stream()
            //Unter ihnen@Beschränkt auf Klassen mit angeschlossenem Controller.
            .filter(kv -> kv.getKey().isAnnotationPresent(Controller.class))
            .forEach(kv ->
                //Holen Sie sich die Controller-Methode.
                Arrays.asList(kv.getKey().getMethods()).stream()
                    // @Beschränkt auf Methoden mit RequestMapping-Annotation
                    .filter(m -> m.isAnnotationPresent(RequestMapping.class))
                    .forEach(m -> {
                        //Informationen zur RequestMapping-Annotation abrufen
                        RequestMapping rm = m.getAnnotation(RequestMapping.class);
                        //Ruft das Attributwertfeld ab, das den HTTP-Aufrufpunkt festlegt.( rm.value() )
                        HTTPController c = new HTTPController(rm.value(), kv.getValue(), m);
                        //Registrieren Sie sich als Controller.
                        controllers.put(rm.value(), c);
                        System.out.println("Registered Controller => " + rm.value() + " - " + c);
                    })
            );
    }

Der Ort, an dem Sie von einem HTTP-Client auf eine Verbindung warten und die Methode des entsprechenden Controllers aufrufen können, ist wie folgt.

    private static class EasyHttpServer implements Closeable {
...
        public void start() {
            while (true) {
                acceptRequest:
                //Warten Sie auf eine Anfrage von einem Client.( server.accept() )
                try (Socket socket = server.accept();
                        BufferedReader br = new BufferedReader(
                            new InputStreamReader(socket.getInputStream(), "UTF-8"))) {

                    // br.readLine() => GET /hello HTTP/1.1
                    String path = br.readLine().split(" ")[1];
                    try (PrintStream os = new PrintStream(socket.getOutputStream())) {
                        //Pfad anfordern/Extrahieren Sie den registrierten Controller basierend auf Hallo.
                        HTTPController c = controllers.get(path);
                        if (c == null) {
                            os.println("404 Not Found (path = " + path + " ).");
                            break acceptRequest;
                        }
                        //Rufen Sie die Controller-Methode auf.( method.invoke )
                        os.println(c.method.invoke(c.instance));
                    }

                } catch (IOException | IllegalAccessException | InvocationTargetException e) {
                    throw new IllegalStateException(e);
                }
            }
        }
...
    }

AOP

AOP steht für Aspect Oriented Programming und ist eine Programmiermethode, die sich auf "Querschnittsthemen" konzentriert. Bei der Objektorientierung wird die für dieses Objekt spezifische Verarbeitung zusammen in einzelnen Definitionen (Klassen usw.) behandelt, es kann jedoch eine Verarbeitung erforderlich sein, die sie überspannt (Querschnittsthemen). Zum Beispiel ...

  • Protokollausgabe
  • Transaktionsmanagement
  • Authentifizierungsprozess
  • Token Check
  • Messung der Bearbeitungszeit
  • Validierungsprüfung

Etc.

Mit AOP ist es möglich, die Verarbeitung auf der ganzen Linie einzufügen, ohne das Client-Programm zu bearbeiten, das die Business-Programmschicht darstellt.

Um AOP zu realisieren, wird in Java standardmäßig "[java.lang.reflect.Proxy](https://docs.oracle.com/javase/jp/8/docs/api/java/" bereitgestellt lang / reflektieren / Proxy.html) "und" java.lang.reflect.InvocationHandler ) "Ist vorgestellt.

Wenn Sie das Quellobjekt und den Querschnittsprozess (InvocationHandler) angeben, den Sie in die Methode Proxy # newProxyInstance () einfügen möchten, wird eine neue Instanz zurückgegeben, die diesen Prozess enthält. Hier wird AOP realisiert, indem die erstellte Instanz für DI sofort überschrieben wird.

Wenn Sie Proxy für AOP verwenden möchten, muss der Zieltyp die Schnittstelle implementieren. Hier wollen wir die SampleService-Schnittstelle abfangen.

src/main/java/easyframework/EasyApplication.java


    private static void registerAOP() {
        components.entrySet().stream()
            .filter(kv -> kv.getKey().isInterface())
            .forEach(kv -> components.put(kv.getKey(),
                Interceptor.createProxiedTarget(kv.getValue())));
        System.out.println("Registered AOP => " + components);
    }

src/main/java/easyframework/EasyApplication.java


package easyframework;

public class EasyApplication {

...
      public static void run(Class<?> clazz, String... args) {
          scanComponents(clazz);
          registerAOP(); //← Diesen Teil hinzufügen
          injectDependencies();
          registerHTTPPaths();
          startHTTPServer();
      }
...

}

src/main/java/easyframework/Interceptor.java


package easyframework;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

class Interceptor implements InvocationHandler {

    private final Object target;

    public static Object createProxiedTarget(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new Interceptor(target));
    }

    private Interceptor(Object obj) {
        this.target = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
    }

}
  • In Proxy muss eine Schnittstelle für das abzufangende Teil vorbereitet werden. Daher ist es besser, die folgende Bibliothek im Geschäftsprogramm zu verwenden.
    • AspectJ: https://eclipse.org/aspectj/
    • Spring AOP: https://eclipse.org/aspectj/

Hands on

Zugabe eines Abfangjägers

  1. Erstellen Sie ein easyframework.Transactional-Annotation.
  2. Fügen Sie die Annotation @Transactional zur Schnittstelle hello.SampleService hinzu.
  • Wenn an den Typ selbst angehängt
  • Wenn an eine öffentliche Methode angehängt
  1. Implementieren Sie die Aufrufmethode von easyframework.Interceptor gemäß den folgenden Spezifikationen.
$ git checkout -b handsonAOP remotes/origin/handsonAOP

src/main/java/easyframework/Interceptor.java



    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(target, args);
        }

        return invoke(method, args);
    }

    private Object invoke(Method method, Object[] args) throws Throwable {
        /*
         *In der Zielmethode oder Typdefinition@Wenn Transaktionsanmerkung hinzugefügt wird
         *Fangen Sie den Prozess ab, der wie folgt abläuft.
         * 1.Vor dem Ausführen der entsprechenden Methode"Starts transaction."Wird in der Standardausgabe angezeigt.
         * 2.Wenn die entsprechende Methodenausführung normal endet"Commit transaction."Wird in der Standardausgabe angezeigt.
         * 3.Wenn die Ausführung der entsprechenden Methode mit einer Ausnahme endet"Rollbak transaction."Wird in der Standardausgabe angezeigt und die Ausnahme wird oben ausgelöst.
         *
         * ※ @Wenn Transactional nicht hinzugefügt wird, geben Sie nur das Ausführungsergebnis der entsprechenden Methode zurück.
         * ※ @Transaktionsanmerkungen sind einfach.Transactional.Erstellen Sie eine neue als Java.
         * ※ ./Führen Sie mit gradlew clean build run aus und locken Sie http://localhost:8080/Erkundigen Sie sich bei Hallo.
         */
         return null;
    }

Die Antwort befindet sich im Zweig addAOP.

$ git checkout remotes/origin/addAOP
$ less src/main/java/easyframework/Interceptor.java

Spring Boot-Abhängigkeiten

Als Referenz das Spring Boot-Beispiel [Spring-Boot-Sample-Tomcat](https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples/spring-boot-sample- tomcat) Überprüfen Sie, aus welchen Bibliotheken das Projekt besteht.

$ git clone https://github.com/spring-projects/spring-boot.git
$ cd spring-boot/spring-boot-samples/spring-boot-sample-tomcat/
$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Spring Boot Tomcat Sample 2.0.0.BUILD-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.10:tree (default-cli) @ spring-boot-sample-tomcat ---
[INFO] org.springframework.boot:spring-boot-sample-tomcat:jar:2.0.0.BUILD-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter:jar:2.0.0.BUILD-SNAPSHOT:compile
[INFO] |  +- org.springframework.boot:spring-boot:jar:2.0.0.BUILD-SNAPSHOT:compile
[INFO] |  +- org.springframework.boot:spring-boot-autoconfigure:jar:2.0.0.BUILD-SNAPSHOT:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter-logging:jar:2.0.0.BUILD-SNAPSHOT:compile
[INFO] |  |  +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] |  |  |  \- ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO] |  |  +- org.slf4j:jul-to-slf4j:jar:1.7.25:compile
[INFO] |  |  \- org.slf4j:log4j-over-slf4j:jar:1.7.25:compile
[INFO] |  +- org.springframework:spring-core:jar:5.0.0.RC3:compile
[INFO] |  |  \- org.springframework:spring-jcl:jar:5.0.0.RC3:compile
[INFO] |  \- org.yaml:snakeyaml:jar:1.18:runtime
[INFO] +- org.springframework.boot:spring-boot-starter-tomcat:jar:2.0.0.BUILD-SNAPSHOT:compile
[INFO] |  +- org.apache.tomcat.embed:tomcat-embed-core:jar:8.5.16:compile
[INFO] |  +- org.apache.tomcat.embed:tomcat-embed-el:jar:8.5.16:compile
[INFO] |  \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:8.5.16:compile
[INFO] +- org.springframework:spring-webmvc:jar:5.0.0.RC3:compile
[INFO] |  +- org.springframework:spring-aop:jar:5.0.0.RC3:compile
[INFO] |  +- org.springframework:spring-beans:jar:5.0.0.RC3:compile
[INFO] |  +- org.springframework:spring-context:jar:5.0.0.RC3:compile
[INFO] |  +- org.springframework:spring-expression:jar:5.0.0.RC3:compile
[INFO] |  \- org.springframework:spring-web:jar:5.0.0.RC3:compile
[INFO] \- org.springframework.boot:spring-boot-starter-test:jar:2.0.0.BUILD-SNAPSHOT:test
[INFO]    +- org.springframework.boot:spring-boot-test:jar:2.0.0.BUILD-SNAPSHOT:test
[INFO]    +- org.springframework.boot:spring-boot-test-autoconfigure:jar:2.0.0.BUILD-SNAPSHOT:test
[INFO]    +- com.jayway.jsonpath:json-path:jar:2.4.0:test
[INFO]    |  +- net.minidev:json-smart:jar:2.3:test
[INFO]    |  |  \- net.minidev:accessors-smart:jar:1.2:test
[INFO]    |  |     \- org.ow2.asm:asm:jar:5.0.4:test
[INFO]    |  \- org.slf4j:slf4j-api:jar:1.7.25:compile
[INFO]    +- junit:junit:jar:4.12:test
[INFO]    +- org.assertj:assertj-core:jar:3.8.0:test
[INFO]    +- org.mockito:mockito-core:jar:2.8.47:test
[INFO]    |  +- net.bytebuddy:byte-buddy:jar:1.6.14:test
[INFO]    |  +- net.bytebuddy:byte-buddy-agent:jar:1.6.14:test
[INFO]    |  \- org.objenesis:objenesis:jar:2.5:test
[INFO]    +- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO]    +- org.hamcrest:hamcrest-library:jar:1.3:test
[INFO]    +- org.skyscreamer:jsonassert:jar:1.5.0:test
[INFO]    |  \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO]    \- org.springframework:spring-test:jar:5.0.0.RC3:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.507 s
[INFO] Finished at: 2017-08-20T17:50:33+09:00
[INFO] Final Memory: 21M/309M
[INFO] ------------------------------------------------------------------------

Einschränkungen, die mit diesem Framework nicht realisiert werden können

  • Alle DI-Komponenten sind eine Tonne
  • DI ist nur Feldinjektion
  • DI-Komponenten werden nur mit dem Standardkonstruktor generiert
  • Anforderungsparameter wird nicht empfangen und gültige Daten sind nicht möglich
  • Die HTTP-Methode ist nur GET, nicht POST oder PUT
  • Komplexes AOP ist nicht möglich
  • HotDeploy (Hot Reloading) nicht möglich

Es gibt viele andere.

Das Buch, das ich als Referenz verwendet habe

Weitere Details, wie die in diesem Artikel erwähnten, finden Sie in den folgenden Büchern. Wenn Sie interessiert sind, lesen Sie es bitte.

** <Einführung in die Java Framework-Entwicklung> **

Die Seite, die ich als Referenz verwendet habe

  • Erstellen Sie einen einfachen HTTP-Server: http://qiita.com/opengl-8080/items/ca152658a0e52c786029
  • Verschiedene Möglichkeiten, um die Liste der Klassen unter Paket abzurufen: http://etc9.hatenablog.com/entry/2015/03/31/001620
  • TECHSCORE-Anmerkung: http://www.techscore.com/tech/Java/JavaSE/JavaLanguage/7/
  • Unas Tagebuch- @ geerbte Funktionsprüfung: http://unageanu.hatenablog.com/entry/20100712/1278946999

Recommended Posts