Vorwort
Beachten Sie, dass HttpServletRequest, um Code zu sparen, in der Methode -Körperschaft nicht deklariert wurde und die gebohrte Grube direkt mit Autowire injiziert wurde.
Schlussfolgerung: Für diejenigen, die ängstlich sind. Deklarieren Sie HttpServletRequest direkt in der Mitgliedsvariable des Controllers mit @AutoWire, was Thread Safe ist!
@ControllerPublic Class TestController {@autoWire httpServletRequest Anfrage; @RequestMapping ("/") public void test () {request.getAttribute ("uid"); }}Die Schlussfolgerung ist wie oben.
Hintergrund
Das ist wahr. Da ich dem Leiter der Anfrage im Projekt Authentifizierungsinformationen hinzugefügt habe und nach dem Interceptor die Informationen abfängt und die Überprüfung übergeben habe, werde ich die Identität des aktuellen Benutzers zum Attribut der Anfrage hinzufügen, was für die Wiederverwendung in der Controller -Ebene bequem ist.
Frage: Warum nicht @Requestheader verwenden, um ihn direkt auf dem Controller abzurufen? Da der Header verschlüsselte Daten enthält und eine komplexe Authentifizierung und Beurteilung erfordert, wird dieser Schritt zur Ausführung direkt in den Interceptor geworfen.
Nach der Entschlüsselung setze ich die Benutzerinformationen (wie UID) in request.setAttribute() ein, um sie im Controller zu extrahieren.
Wenn Sie eine Anfrage verwenden müssen, müssen Sie sie im Allgemeinen für die Methode deklarieren, z. B.:
öffentliches Ergebnis Save (httpServletRequest Anfrage) {// doSomething ();}Wenn ich also UID für jede Methode verwende, würde nicht jede Methode einen Anforderungsparameter deklarieren, um einen redundanten Schritt zu speichern. Ich habe eine Basisklasse geschrieben.
public class CommonController {@autowire httpServletReqeuch Anfrage; public String getUid () {return (String) request.getAttribute ("uid"); }}Später war ich besorgt, dass der Controller ein Singleton ist, dies dazu führt, dass der nachfolgende Rückstand die vorherige Anfrage abdeckt und dass es unter Gleichberiefungsbedingungen die Sicherheitsprobleme von Threads gibt. Also stellte ich eine Frage zu Segmentfault und die meisten Internetnutzer sagten, dass es tatsächlich ein Threading -Problem gibt! Segmentfault Problemadresse Die begeisterten Programmierer haben mir mehrere Lösungen zur Verfügung gestellt. Da ich mich bemüht habe, es zu beweisen, werde ich die Ergebnisse hier einsetzen und sie mit Ihnen teilen.
Methode 1
Die erste Methode besteht darin, die Deklaration httpServletReqeuch in der Controller -Methode anzuzeigen. Der Code lautet wie folgt:
@RequestMapping ("/test")@rastControllerPublic class ctest {logger logger = loggerfactory.getLogger (getClass ()); @RequestMapping ("/iiii") öffentlicher String -Test (httpServletRequest -Anforderung) {logger.info (request.hashCode () + ""); null zurückkehren; }}Drücken Sie F5 im Browser
Ausgabe
Ich war zu dieser Zeit verwirrt, ** Ich stimmte zu, die Sicherheit von Threads zu sein! ** Ist das nicht die gleiche Anfrage? Willst du mich verarschen! Ich habe lange gesucht, um den HashCode () umzuschreiben!
Ah, das ist die Wahrheit, weil ich F5 mit meinem Browser drücke und egal wie schwer ich sie drücke, ich kann die Parallelität nicht simulieren. Dies entspricht dem Server, der denselben Thread verwendet, um meine Anfrage zu verarbeiten. Was den HashCode dieser Anfrage betrifft, so wird er laut JDK auf der Grundlage der virtuellen Adresse von OBJ in JVM berechnet. Ich denke, was kommt als nächstes. Wenn Sie wissen, was Sie wirklich wollen, werde ich es mir sagen!
erraten
Der Speicherplatz der Anforderung, die jeweils auf jedem Thread im Server angewendet wird, wird festgelegt, wenn der Server gestartet wird. Dann erstellt er jedes Mal, wenn ich anforderte, eine neue Anfrage im von ihm angeforderten Speicherplatz (möglicherweise eine Struktur ähnlich wie ein Array). (Ähnlich wie der Ausgangspunkt des Arrays ist immer dieselbe Speicheradresse). Dann initiiere ich eine Anfrage, und er wird eine neue Anfrage an der Startposition erstellen und an das Servlet weitergeben und mit der Bearbeitung beginnen. Nach Abschluss der Verarbeitung wird sie zerstört. Dann wird die neue Anfrage, die durch seine nächste Anfrage erstellt wurde, zerstört, sodass sie mit der Startadresse beginnt. Auf diese Weise wird alles erklärt!
Die Vermutung ist fertig
Überprüfen Sie die Vermutung:
Ich werde ihn nicht Zeit haben, es zu zerstören. Kann ich den Code testen?
@RequestMapping ("/test")@rastControllerPublic class ctest {logger logger = loggerfactory.getLogger (getClass ()); @RequestMapping ("/oooo") public String testa (httpServletRequest -Anforderung) löst eine Ausnahme aus {Thread.sleep (3000); logger.info (request.hashcode () + ""); logger.info (reqeust.getheader ("uid"); return null;} @RequestMapping ("/iiii") public String test (httpServletRequest) {logger.info (Request.HashCode () + "");Wie oben erwähnt, schlafe ich 3 Sekunden lang in der Schnittstelle/oooo. Wenn es sich über einen Rückstand teilt, überschreibt die nachfolgende Anfrage die Requestation im Schlaf, und die eingehende UID ist die Schnittstellenadresse. Starten Sie zuerst /oooo und dann starten Sie /iiii
Ausgabe
Controller.Ctest: 33 - 364716268Controller.Ctest: 34 - IIIicontroller.Ctest: 26 - 1892130707Controller.Ctest: 27 - Ooooo
Schlussfolgerung: 1. Die später eingeleitete /iii -initiierte überschreibt die vorherigen /oooo -Daten nicht und es gibt kein Problem mit der Sicherheit von Threads. 2. Der HashCode der Anfrage ist unterschiedlich. Da /oooo -Blockierung dazu führt, dass ein weiterer Thread ihn verarbeiten muss, erzeugt er eine neue Anfrage anstelle des gleichen HashCode wie zuvor.
Zweite Überprüfungsrunde
public class httptest {public static void main (String [] args) löst eine Ausnahme aus {für (int i = 300; i> 0; i--) {Final int Finali = i; neuer Thread () {@Override public void run () {System.out.println ("v ###" + Finali); Httprequest.get ("http: // localhost: 8080/test/iiii?") .Header ("uid", "v ###" + finali) .send (); } }.Start(); }}}Unter simulierten Parallelitätsbedingungen akzeptiert UID300 in Header ohne Überschreibung vollständig
Auf diese Weise gibt es also kein Problem mit Fadensicherheit.
Methode 2
Verwenden Sie bei CommonController @ModelATTRIBUTE, um sie zu verarbeiten.
public class CommonController {// @autowired Protected HttpServletRequest Request; @ModelAttribute public void bindreq (httpServletRequest -Anfrage) {this.Request = Anfrage; } Protected String getuid () {System.out.println (request.toString ()); return request.getAttribute ("uid") == null? null: (String) request.getAttribute ("uid"); }}Dies hat Fadensicherheitsprobleme! Die nachfolgende Anfrage kann die vorherige abdecken!
Bestätigungscode
@RastController@requestMapping ("/test") öffentliche Klasse Ctest erweitert CommonController {logger logger = loggerfactory.getLogger (getClass ()); @RequestMapping ("/iiii") public String test () {logger.info (request.getheader ("uid")); null zurückkehren; }} public class httptest {public static void main (String [] args) löst eine Ausnahme aus {für (int i = 100; i> 0; i--) {Final int Finali = i; neuer Thread () {@Override public void run () {System.out.println ("v ###" + Finali); Httprequest.get ("http: // localhost: 8080/test/iiii") .header ("uid", "v ###" + finali) .send (); } }.Start(); }}}Die partiellen Ausgangsergebnisse wurden abgefangen
Controller.Ctest: 26 - V ### 52Controller.Ctest: 26 - V ### 13Controller.Ctest: 26 - V ### 57Controller.Ctest: 26 - V ### 57Controller.Ctest: 26 - V ### 21Controller. v###82controller.CTest:26 - v###93controller.CTest:26 - v###71controller.CTest:26 - v###71controller.CTest:26 - v###71controller.CTest:26 - v###71controller.CTest:26 - v###71controller.CTest:26 - V ### 71Controller.Ctest: 26 - V ### 85Controller.Ctest: 26 - V ### 85Controller.Ctest: 26 - V ### 14Controller.Ctest: 26 - V ### 47Controller.Ctest: 26 - V ### 47Controller. V ### 2 22Controller.Ctest: 26 - V ### 55Controller.Ctest: 26 - V ### 61
Sie können sehen, dass 57, 71, 85 und 47 abgedeckt sind und einige Anfragen verloren gehen!
Dies ist Thread-Unsicherheit!
Methode 3
Verwenden Sie CommonController als Basisklasse, um Autowire anzufordern.
öffentliche Klasse CommonController {@autowired Protected httpServletRequest Anfrage; Protected String getuid () {System.out.println (request.toString ()); return request.getAttribute ("uid") == null? null: (String) request.getAttribute ("uid"); }}Die Testoberfläche ist die gleiche wie oben und die Ergebnisse sind erfreulich! Es gibt keine Deckung für 100 Anfragen. Ich erhöhte den Umfang und habe es fünf- oder sechs Mal getestet und keine Abdeckung für Tausende von Anfragen. Dies kann beweisen, dass in dieser Schreibmethode kein Problem mit Thread -Sicherheit vorhanden ist!
Eine andere interessante Sache ist, dass der HashCode der Anfrage immer gleich ist, egal wie viel Parallelität verwendet wird. Darüber hinaus ist es beim Testen verschiedener Schnittstellen im selben Controller dieselbe. Bei der Verwendung von Schlaf, um Block zu erzwingen, ist der HashCode gleich. Der HashCode ist jedoch unterschiedlich, wenn ich auf verschiedene Controller zugreift, sodass ich mich nicht weiter in die Umsetzung eingebaut habe.
Aber die Schlussfolgerung ist herausgekommen, genau wie der Artikel zu Beginn sagte.
Zusammenfassen
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.