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.
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!
Diejenigen, die die folgenden Punkte anhand der bisherigen Operation verstehen können, haben die in diesem Artikel beschriebene Stufe bereits verstanden. Daher besteht die Möglichkeit, dass es keine neuen Entdeckungen gibt, selbst wenn Sie weiterlesen.
Stellen Sie sich vor, wie verschiedene Anmerkungen von der Reflection-API verarbeitet werden
Verstehen Sie, warum service.hello () erfolgreich ist, ohne eine NullPointerException zu erhalten, obwohl das Servicefeld nicht mit new instanziiert wird
Sie können sehen, warum "/ hello" die home () -Methode aufruft, obwohl die Hauptmethode nur run ausführt
Hinweis: Dieses Beispiel hat keine weiteren Funktionen und ist nicht gemäß den HTTP-Spezifikationen implementiert. Daher ist es als Framework überhaupt nicht praktikabel **. Wie bereits erwähnt, besteht das Ziel darin, ein wenig zu verstehen, was innerhalb des Frameworks geschieht.
(Zusätzliche Bemerkungen)
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
#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>
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
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.
** 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.
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.
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.
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.
Es gibt drei Hauptmethoden, um eine Instanz des Klassentyps abzurufen.
//<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 "
Logger LOG = LoggerFactory.getLogger(String.class);
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.
# | 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
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.
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.
Ich werde die Details von jedem beschreiben.
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.
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 ...
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 {
...
}
}
$ 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
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] ------------------------------------------------------------------------
Es gibt viele andere.
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> **
Recommended Posts