Vorwort
In diesem Artikel werden verschiedene Methoden eingeführt, um Anforderungsobjekte im von Spring MVC entwickelten Websystem zu erhalten und die Sicherheit der Threads zu erörtern. Ich werde unten nicht viel sagen. Schauen wir uns die detaillierte Einführung zusammen an.
Überblick
Bei der Entwicklung eines Websystems unter Verwendung von Spring MVC müssen Sie häufig ein Anforderungsobjekt verwenden, wenn die Anforderungen bearbeitet, z. B. das Erhalten der Client-IP-Adresse, die angeforderte URL, die Eigenschaften im Header (z. B. Cookies, Autorisierungsinformationen), Daten im Körper usw., da im Spring MVC, der Controller, der Controller, des Dienstes, und andere Objekte, die Anfragen verarbeiten, ein TRABE-INDERT-Objekt. Eine große Anzahl von gleichzeitigen Anforderungen. Kann es garantiert werden, dass verschiedene Anforderungsobjekte in verschiedenen Anforderungen/Threads verwendet werden?
Hier gibt es eine andere Frage: Wo benutze ich das Anforderungsobjekt "Bei der Verarbeitung einer Anfrage", die zuvor erwähnt wurde? In Anbetracht der Tatsache, dass es in den Methoden zur Erlangung von Anforderungsobjekten geringfügige Unterschiede gibt, können sie in zwei Kategorien grob unterteilt werden:
1) Verwenden Sie Anforderungsobjekte in Frühlingsbohnen: Geben Sie beide MVC -Bohnen wie Controller, Service, Repository und gewöhnliche Federbohnen wie Komponenten ein. Aus Erläuterung werden die Bohnen im Frühling im folgenden Text alle kurz als Bohnen bezeichnet.
2) Verwenden Sie Anforderungsobjekte in Nicht-BEANS: beispielsweise in Methoden gewöhnlicher Java-Objekte oder in statischen Klassenmethoden.
Darüber hinaus wird in diesem Artikel um das Anforderungsobjekt, das die Anforderung darstellt, erläutert. Die verwendete Methode ist jedoch auch für das Antwortobjekt, InputStream/Reader, OutputStream/Writer usw.; wobei InputStream/Reader die Daten in der Anforderung lesen und OutputStream/Writer Daten in die Antwort schreiben können.
Schließlich bezieht sich die Methode zum Erhalten des Anforderungsobjekts auch mit der Version von Spring und MVC. Dieser Artikel wird basierend auf Frühjahr 4 erörtert, und die durchgeführten Experimente werden alle verwendet, Version 4.1.1.
So testen Sie die Sicherheit der Fadensicherheit
Da die Fadensicherheitsprobleme des Anforderungsobjekts besondere Aufmerksamkeit benötigen, um die folgende Diskussion zu erleichtern, erläutern wir zunächst, wie das Anforderungsobjekt thread-sicher ist.
Die Grundidee des Tests besteht darin, eine große Anzahl von gleichzeitigen Anforderungen des Clients zu simulieren und dann festzustellen, ob die Anforderungen auf dem Server verwendet werden.
Der intuitivste Weg, um festzustellen, ob das Anforderungsobjekt gleich ist, besteht darin, die Adresse des Anforderungsobjekts auszudrucken. Wenn es gleich ist, bedeutet dies, dass dasselbe Objekt verwendet wird. In fast allen Webserver -Implementierungen werden jedoch Thread -Pools verwendet, was zu zwei Anforderungen führt, die nacheinander eintreten, die durch denselben Thread verarbeitet werden können: Nach der Verarbeitung der vorherigen Anforderung rebobt der Thread -Pool den Thread und weist den Thread in die nachfolgende Anforderung zu. Im selben Thread ist das verwendete Anforderungsobjekt wahrscheinlich gleich (die Adresse ist die gleiche, die Attribute sind unterschiedlich). Daher können auch bei Thread-Safe-Methoden die von verschiedenen Anforderungen verwendeten Anforderungsobjektadressen gleich sein.
Um dieses Problem zu vermeiden, besteht eine Methode darin, den Thread während des Anforderungsverarbeitungsprozesses für einige Sekunden schlafen zu lassen, wodurch jeder Thread lange genug funktioniert, um denselben Thread zu vermeiden, dass sich verschiedene Anforderungen zuweisen. Die andere Methode besteht darin, andere Attribute der Anforderung (z. B. Parameter, Header, Körper usw.) als Grundlage zu verwenden, ob die Anforderung thread-sicher ist, da auch wenn verschiedene Anforderungen denselben Thread nacheinander verwenden (die Adresse des Anforderungsobjekts ist gleich), sofern das Anforderungsobjekt zweimal konstruiert wird, wob In diesem Artikel wird die zweite Methode zum Testen verwendet.
Der Client -Testcode ist wie folgt (erstellen Sie 1000 Threads, um Anforderungen separat zu senden):
public class test {public static void main (String [] args) löst eine Ausnahme aus {String prefix = uUid.randomuuid (). toString (). Ersatz ("-", "") + "::"; für (int i = 0; i <1000; i ++) {Final String value = Präfix+i; neuer Thread () {@Override public void run () {try {clodleableHttpclient httpclient = httpclients.CreateFault (); Httpget httpget = new httpget ("http: // localhost: 8080/test? Key =" + value); httpclient.execute (httpget); httpclient.close (); } catch (ioException e) {e.printstacktrace (); } } } } } }.Start(); }}}Der Controller -Code auf dem Server ist wie folgt (der Code zum Abholen des Anforderungsobjekts wird vorübergehend weggelassen):
@ControllerPublic Class TestController {// Vorhandene Parameter speichern, um festzustellen, ob die Parameter dupliziert sind, wodurch festgelegt wird, ob der Thread sicher ist, dass der Thread eine sichere öffentliche statische Set <string> set = new Hashset <> () ist; @RequestMapping ("/test") public void test () löscht InterruptedException {// aus ………………………………………………………………………………………………………………………………………. …………………………………………………………………………………………………………………………………………. ………………………………………………………………………………………………………………………………………. …………………………………………………………………………………………………………………………………………. "/t erscheint wiederholt, die Anfrage ist nicht sicher!"); } else {System.out.println (Wert); set.add (Wert); } // Das Simulationsprogramm wurde über einen bestimmten Zeitraum thread.sleep (1000) ausgeführt; }}Wenn das Anforderungsobjekt threadsicher ist, ist das Druckergebnis des Servers wie folgt:
Wenn ein Thread -Sicherheitsproblem vorliegt, kann das Druckergebnis des Servers so aussehen:
Wenn es keine Sonderbeschreibung gibt, wird der Testcode später in diesem Artikel in den Codes weggelassen.
Methode 1: Fügen Sie dem Controller Parameter hinzu
Codebeispiel
Diese Methode ist am einfachsten zu implementieren und direkt den Controller -Code einzugeben:
@ControllerPublic Class TestController {@RequestMapPing ("/test") public void test (httpServletRequest -Anforderung) löst InterruptedException aus {// Das Simulationsprogramm wurde für einen Zeitraum Thread ausgeführt. Sleep (1000); }}Das Prinzip dieser Methode ist, dass Spring, wenn die Controller -Methode mit der Verarbeitung der Anforderung beginnt, den Methodenparametern das Anforderungsobjekt zuweist. Zusätzlich zum Anforderungsobjekt gibt es viele Parameter, die durch diese Methode erhalten werden können. Weitere Informationen finden Sie unter: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-Nethods
Nachdem Sie das Anforderungsobjekt im Controller erhalten haben, müssen Sie das Anforderungsobjekt beim Aufrufen dieser Methoden als Parameter als Parameter übergeben, wenn Sie das Anforderungsobjekt in anderen Methoden (z. B. Servicemethoden, Methoden der Werkzeugklasse usw.) verwenden möchten.
Fadensicherheit
Testergebnisse: Fadensicherheit
Analyse: Zu diesem Zeitpunkt ist das Anforderungsobjekt ein Methodeparameter, der einer lokalen Variablen entspricht und zweifellos mit einem Thread-Safe verteilt ist.
Für und Wider
Der Hauptnachteil dieser Methode besteht darin, dass das Anforderungsobjekt zu überflüssig ist, um zu schreiben, was sich hauptsächlich in zwei Punkten widerspiegelt:
1) Wenn das Anforderungsobjekt in mehreren Controller -Methoden erforderlich ist, muss für jede Methode der Anforderungsparameter hinzugefügt werden.
2) Der Erwerb des Anforderungsobjekts kann nur vom Controller starten. Wenn sich der Ort, an dem das Anforderungsobjekt verwendet wird, an einem tieferen Ort der Funktionsaufrufebene befindet, müssen alle Methoden der gesamten Anrufkette den Anforderungsparameter hinzufügen.
Tatsächlich wird während des gesamten Anforderungsverarbeitungsprozesses das Anforderungsobjekt die gesamte Anfrage durchlaufen. Das heißt, außer für Sonderfälle wie Timer entspricht das Anforderungsobjekt einer globalen Variablen im Thread. Diese Methode entspricht dieser globalen Variablen.
Methode 2: Automatische Injektion
Codebeispiel
Laden Sie zuerst den Code hoch:
@ControllerPublic Class TestController {@autowired private httpServletRequest Request; // AutoWired Request @RequestMapping ("/test") public void test () löscht InterruptedException {// Das Simulationsprogramm wurde für einen Zeitraum Thread ausgeführt. Sleep (1000); }} Fadensicherheit
Testergebnisse: Fadensicherheit
Analyse: Im Frühjahr ist der Umfang des Controllers Singleton (Singleton), was bedeutet, dass es im gesamten Websystem nur einen Testcontroller gibt. Die injizierte Anfrage ist jedoch mit Thread-Sicherheit, weil:
Auf diese Weise wird die Feder nicht ein Anforderungsobjekt, sondern einen Proxy injiziert, wenn die Bean (Testcontroller in diesem Beispiel) initialisiert wird. Wenn die Bean das Anforderungsobjekt verwenden muss, wird das Anforderungsobjekt über den Proxy erhalten.
Das Folgende ist eine Beschreibung dieser Implementierung über einen bestimmten Code.
Fügen Sie dem obigen Code Breakpoints hinzu und sehen Sie sich die Eigenschaften des Anforderungsobjekts an, wie in der folgenden Abbildung gezeigt:
Wie in der Abbildung zu sehen ist, ist die Anfrage tatsächlich ein Proxy: Die Implementierung des Proxy ist in der internen Klasse von AutoWireTils dargestellt
ObjectFactoryDelegatingInvocationHandler: /*** Reflective InvocationHandler für den faulen Zugriff auf das aktuelle Zielobjekt. */@SuppressWarnings ("Serial") private statische Klasse ObjectFactoryDelegatingInvocationHandler implementiert InvocationHandler, serialisierbar {private endgültige Objektfaktory <?> ObjectFactory; public ObjectFactoryDelegatingInvocationHandler (ObjectFactory <?> ObjectFactory) {this.ObjectFactory = ObjectFactory; } @Override public Object Invoke (Object Proxy, Methode Methode, Object [] args) löscht Throwable {// ... irrelevante Code -Versuche aus {return methode.invoke (this.ObjectFactory.getObject (), args); // Agent -Implementierung Core Code} catch (invocationTargetException ex) {throw ex.gettargetException (); }}}Mit anderen Worten, wenn wir die Methode der Anforderung aufrufen, nennen wir tatsächlich die Methode des von ObjectFactory.getObject () generierten Objekts. Das von ObjectFactory.getObject () generierte Objekt ist das reale Anforderungsobjekt.
Beobachten Sie weiterhin die obige Abbildung und stellen Sie fest, dass der ObjectFactory -Typ die interne Klasse RequestObjectFactory von WebApplicationContextutils ist. und der RequestObjectFactory -Code lautet wie folgt:
/*** Fabrik, das das aktuelle Anforderungsobjekt auf Bedarf aufdeckt. */ @Unterdrückewarnings ("Serielle") private statische Klasse RequestObjectFactory implementiert ObjectFactory <ServletRequest>, serialisierbar {@Override public ServLetRequest getObject () {return currentRequestAttributes (). GetRequest (); } @Override public String toString () {return "aktuelle httpServletRequest"; }}Um das Anforderungsobjekt zu erhalten, müssen Sie die CurrentRequestAttributes () -Methode aufrufen, um das Objekt des RequestAttributes zu erhalten. Die Implementierung dieser Methode ist wie folgt:
/*** Geben Sie die aktuelle Antragstellungsinstanz als ServletRequestAttributes zurück. */private statische servletRequestattributes currentRequestAttributes () {RequestAttributes RequestAttr = RequestContexTHolder.CurrentRequestAttributes (); if (! (RequestAttrinStance von ServletRequestAttributes)) {neue IllegalStateException werfen ("Aktuelle Anfrage ist keine Servlet -Anfrage"); } return (servletRequestattributes) Antrag der Rückgabe;}Der Kerncode, der das Objekt des RequestAttributes generiert, befindet sich in der Klasse RequestContexTHolder, wobei der entsprechende Code wie folgt ist (der nicht verwandte Code in der Klasse wird weggelassen):
public abstract class requestContextHolder {public static requestAttributes currentRequestAttributes () löst illegalStateException aus {RequestAttributes Attributes = getRequestAttributes (); // irrelevante Logik wird hier weggelassen ...... Rückgabeattribute; } public static requestAttributes getRequestAttributes () {RequestAtTributes Attributes = RequestAttributSholder.get (); if (attribute == null) {attribute = inheritableRequestattributSholder.get (); } Rückgabeattribute; } private statische endgültige ThreadLocal <FunschTributes> RequestAttributSoDer = new namedThreadLocal <FunschTributes> ("Request Attribute"); private statische endgültige ThreadLocal <FunschTributes> InheritableRequestAttributSoDer = new NamedInheritableAltheadLocal <FunschTributes> ("Anfragekontext");}Aus diesem Code können wir feststellen, dass das generierte Antragstellungsobjekt eine lokale Thread -Variable (ThreadLocal) ist, sodass das Anforderungsobjekt auch eine lokale Thread -Variable ist. Dadurch wird die Fadensicherheit des Anforderungsobjekts gewährleistet.
Für und Wider
Die Hauptvorteile dieser Methode:
1) Die Injektion ist nicht auf Controller beschränkt: In Methode 1 kann nur der Anforderungsparameter zum Controller hinzugefügt werden. Für Methode 2 kann es nicht nur in den Controller, sondern auch in eine Bohne injiziert werden, einschließlich Dienst, Repository und gewöhnliche Bohnen.
2) Das injizierte Objekt ist nicht auf Anfrage beschränkt: Zusätzlich zur Injize des Anforderungsobjekts kann diese Methode auch andere Objekte mit Umfang als Anforderung oder Sitzung wie Antwortobjekten, Sitzungsobjekte usw. injizieren; und Gewährleistung der Gewindesicherheit.
3) Redundanz der Code reduzieren: Geben Sie das Anforderungsobjekt einfach in die Bean ein, für die das Anforderungsobjekt erforderlich ist, und kann in verschiedenen Methoden der Bean verwendet werden, wodurch die Code -Redundanz im Vergleich zu Methode 1 stark reduziert wird.
Diese Methode hat jedoch auch eine Code -Redundanz. Betrachten Sie dieses Szenario: Es gibt viele Controller im Websystem, und jeder Controller verwendet ein Anforderungsobjekt (dieses Szenario ist tatsächlich sehr häufig). Zu diesem Zeitpunkt müssen Sie Code schreiben, um eine Anfrage mehrmals zu injizieren. Wenn Sie auch die Antwort einfügen müssen, ist der Code noch umständlicher. Das Folgende beschreibt die Verbesserung der automatischen Injektionsmethode und analysiert die Sicherheit und die Vor- und Nachteile des Fadens.
Methode 3: Automatische Injektion in die Basisklasse
Codebeispiel
Legen Sie im Vergleich zu Methode 2 den injizierten Teil des Codes in die Basisklasse ein.
Basisklassencode:
öffentliche Klasse BaseController {@autowired Protected HttpServletRequest Request; }Der Controller -Code ist wie folgt; Hier sind zwei abgeleitete Klassen von Basecontroller. Da der Testcode derzeit unterschiedlich ist, wird der Server -Testcode nicht weggelassen. Der Client muss auch entsprechende Änderungen vornehmen (senden Sie gleichzeitig eine große Anzahl von gleichzeitigen Anforderungen an die beiden URLs).
@ControllerPublic Class testController erweitert BaseController {// Vorhandene Parameter speichern, um festzustellen, ob der Parameterwert wiederholt wird, wodurch festgestellt wird, ob der Thread sicher ist, dass der Thread für öffentliche statische Set <string> set = new Hashset <> () ist; @RequestMapping ("/test") public void test () löscht InterruptedException {String value = Request.getParameter ("Key"); // Überprüfen Sie die Thread -Sicherheit if (set.contains (value)) {system.out.println (Wert + "/t erscheint wiederholt, Anforderung ist nicht sicher!"); } else {System.out.println (Wert); set.add (Wert); } // Das Simulationsprogramm wurde für einen Zeitraum von Zeit Thread ausgeführt. Sleep (1000); }} @ControllerPublic class test2Controller erweitert BaseController {@RequestMapping ("/test2") public void test2 () löscht InterruptedException {String value = Request.GetParameter ("Schlüssel"); // Richter Thread -Sicherheit (Verwenden Sie einen Set mit testcontroller, um zu richten) if (testcontroller.set.contains (value)) {System.out.println (Wert + "/T wird wiederholt angezeigt, die Anfrage ist nicht sicher!"); } else {System.out.println (Wert); Testcontroller.set.add (Wert); } // Das Simulationsprogramm wurde über einen bestimmten Zeitraum thread.sleep (1000) ausgeführt; }} Fadensicherheit
Testergebnisse: Fadensicherheit
Analyse: Basierend auf dem Verständnis der Thread-Sicherheit von Methode 2 ist es leicht zu verstehen, dass die Methode 3 Thread-Safe ist: Beim Erstellen verschiedener abgeleiteter Klassenobjekte belegen die Domänen in der Basisklasse (hier ist die injizierte Anforderung) unterschiedliche Speicherplatz in verschiedenen abgeleiteten Klassenobjekten, dh die Eingabe der mit Code eingerichteten Anforderung in der Basisklasse hat keinen Einfluss auf die Gewindesicherheit. Die Testergebnisse beweisen dies ebenfalls.
Für und Wider
Im Vergleich zu Methode 2 wird eine wiederholte Injektion von Anforderungen in verschiedenen Controllern vermieden. Wenn man bedenkt, dass Java jedoch nur die Vererbung einer Basisklasse ermöglicht, ist diese Methode, wenn der Controller andere Klassen erben muss, nicht mehr einfach zu bedienen.
Unabhängig davon, ob es sich um Methode 2 oder Methode 3 handelt, können Sie nur Anfragen in die Bean injizieren. Wenn andere Methoden (z. B. statische Methoden in der Werkzeugklasse) Anforderungsobjekte verwenden müssen, müssen Sie die Anforderungsparameter übergeben, wenn Sie diese Methoden aufrufen. Die unten eingeführte Methode 4 kann direkt in statischen Methoden wie Werkzeugklassen verwendet werden (natürlich kann es auch in verschiedenen Bohnen verwendet werden).
Methode 4: Rufen Sie manuell an
Codebeispiel
@ControllerPublic Class TestController {@RequestMapPing ("/test") public void test () löst InterruptedException aus {httpServletRequest Request = ((ServletRequestatTributes) (RequestContexther.CurentRequestatTribute ()). GetRequest (); // Das Simulationsprogramm wurde für einen Zeitraum von Zeit Thread ausgeführt. Sleep (1000); }} Fadensicherheit
Testergebnisse: Fadensicherheit
Analyse: Diese Methode ähnelt der Methode 2 (automatische Injektion), mit der Ausnahme, dass sie durch automatische Injektion in Methode 2 implementiert wird und diese Methode durch manuelle Methodenaufruf implementiert wird. Daher ist diese Methode auch thread-sicher.
Für und Wider
Vorteile: Kann direkt in Nicht-Becken erhalten werden. Nachteile: Wenn Sie mehr Orte verwenden, ist der Code sehr umständlich. Daher kann es in Verbindung mit anderen Methoden verwendet werden.
Methode 5: @modelattribute Methode
Codebeispiel
Die folgende Methode und ihre Varianten (Mutation: Anforderung und BindRequest in Unterklassen) werden häufig online gesehen:
@ControllerPublic Class TestController {private httpServletRequest Request; @ModelAttribute public void BindRequest (httpServletRequest -Anfrage) {this.Request = Anfrage; } @RequestMapping ("/test") public void test () löscht InterruptedException aus {// Das Simulationsprogramm wurde für einen Zeitraum von Zeit Thread.Sleep (1000) ausgeführt; }} Fadensicherheit
Testergebnis: Thread ist nicht sicher
Analyse: Wenn @Modelattribute Annotation verwendet wird, um die Methode im Controller zu ändern, wird die Methode ausgeführt, bevor jede @RequestMapping -Methode im Controller ausgeführt wird. In diesem Beispiel besteht die Funktion von BindRequest () darin, dem Anforderungsobjekt vor dem Test () einen Wert zuzuweisen. Obwohl die Parameteranforderung in BindRequest () selbst Thread-Safe ist, kann TestController als Singleton-Anfrage als Feld des TestController nicht garantieren.
Zusammenfassen
Zusammenfassend lässt sich sagen, dass das Hinzufügen von Parametern (Methode 1), die automatische Injektion (Methode 2 und Methode 3) und manuelle Aufrufe (Methode 4) im Controller alle thread-sicher sind und alle verwendet werden können, um Anforderungsobjekte zu erhalten. Wenn das Anforderungsobjekt im System weniger verwendet wird, kann eine der beiden Methoden verwendet werden. Wenn es mehr verwendet wird, wird empfohlen, die automatische Injektion (Methode 2 und Methode 3) zu verwenden, um die Code -Redundanz zu reduzieren. Wenn Sie ein Anforderungsobjekt in einer Nicht-Bean verwenden müssen, können Sie es entweder über Parameter übergeben, wenn Sie die obere Ebene aufrufen, oder Sie können diese direkt über manuelle Anrufe erhalten (Methode 4).
Okay, das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Referenzwert für das Studium oder die Arbeit eines jeden hat. Wenn Sie Fragen haben, können Sie eine Nachricht zur Kommunikation überlassen. Vielen Dank für Ihre Unterstützung bei Wulin.com.
Referenzen