Wie funktionieren Servlets? Instanziierung, Sitzungen, gemeinsam genutzte Variablen und Multithreading

1144

Angenommen, ich habe einen Webserver, der zahlreiche Servlets enthält. Für Informationen, die zwischen diesen Servlets übertragen werden, setze ich Sitzungs- und Instanzvariablen.

Wenn nun zwei oder mehr Benutzer eine Anfrage an diesen Server senden, was passiert dann mit den Sitzungsvariablen?
Werden sie alle für alle Benutzer gemeinsam sein oder werden sie für jeden Benutzer unterschiedlich sein?
Wenn sie unterschiedlich sind, wie konnte der Server dann zwischen verschiedenen Benutzern unterscheiden?

Eine weitere ähnliche Frage: Wenn nBenutzer auf ein bestimmtes Servlet zugreifen, wird dieses Servlet nur beim ersten Zugriff des ersten Benutzers instanziiert oder wird es für alle Benutzer separat instanziiert?
Mit anderen Worten, was passiert mit den Instanzvariablen?

Ku Jon
quelle

Antworten:

1821

ServletContext

Wenn der Servlet-Container (wie Apache Tomcat ) gestartet wird, werden alle Webanwendungen bereitgestellt und geladen. Wenn eine Webanwendung geladen wird, erstellt der Servlet-Container die ServletContexteinmalige Anwendung und speichert sie im Speicher des Servers. Der Web - App web.xmlund alle enthaltenen web-fragment.xmlDateien wird analysiert, und jeder <servlet>, <filter>und <listener>gefunden (oder jede Klasse kommentierte mit @WebServlet, @WebFilterund@WebListener jeweils) instanziiert einmal und auch in der Server-Speicher gehalten. Für jeden instanziierten Filter wird seine init()Methode mit einem neuen aufgerufen FilterConfig.

Wenn a Servleteinen <servlet><load-on-startup>oder @WebServlet(loadOnStartup)Wert größer als hat 0, wird seine init()Methode auch beim Start mit einem neuen aufgerufen ServletConfig. Diese Servlets werden in derselben Reihenfolge initialisiert, die durch diesen Wert angegeben wird ( 1ist 1., 2ist 2. usw.). Wenn für mehr als ein Servlet derselbe Wert angegeben wird, wird jedes dieser Servlets in derselben Reihenfolge geladen, in der sie im web.xml, angezeigt werden.web-fragment.xml oder @WebServletClassloading. init()Wenn der Wert "Load-on-Startup" nicht vorhanden ist, wird die Methode immer dann aufgerufen, wenn die HTTP-Anforderung das Servlet zum ersten Mal trifft.

Wenn der Servlet-Container mit allen oben beschriebenen Initialisierungsschritten fertig ist, wird der ServletContextListener#contextInitialized() wird der aufgerufen.

Wenn die Servlet - Container heruntergefahren wurde , es alle Webanwendungen leert, ruft die destroy()Methode aller seiner initialisiert Servlets und Filter, und alle ServletContext, Servlet, FilterundListener Instanzen im Papierkorb sind. Schließlich ServletContextListener#contextDestroyed()wird das aufgerufen.

HttpServletRequest und HttpServletResponse

Der Servlet-Container ist an einen Webserver angeschlossen, der auf HTTP-Anforderungen an einer bestimmten Portnummer wartet (Port 8080 wird normalerweise während der Entwicklung und Port 80 in der Produktion verwendet). Wenn ein Client (zB Benutzer mit einem Web - Browser oder mit programmatischURLConnection ) einer HTTP - Anforderung sendet, erstellt die Servlet - Container neu HttpServletRequestund HttpServletResponseObjekte und leitet sie durch definierte jeder Filterin der Kette und schließlich dieServlet Instanz.

Bei Filtern ist diedoFilter() Methode aufgerufen. Wenn der Code des Servlet-Containers aufgerufen wird chain.doFilter(request, response), fahren die Anforderung und die Antwort mit dem nächsten Filter fort oder treffen das Servlet, wenn keine Filter mehr vorhanden sind.

Bei Servlets wird die service()Methode aufgerufen. Standardmäßig bestimmt diese Methode, von welcher der doXxx()aufzurufenden Methoden basierend auf request.getMethod() . Wenn die ermittelte Methode im Servlet nicht vorhanden ist, wird in der Antwort ein HTTP 405-Fehler zurückgegeben.

Das Anforderungsobjekt bietet Zugriff auf alle Informationen zur HTTP-Anforderung, z. B. URL, Header, Abfragezeichenfolge und Text. Das Antwortobjekt bietet die Möglichkeit, die HTTP-Antwort nach Ihren Wünschen zu steuern und zu senden, indem Sie beispielsweise die Header und den Body festlegen (normalerweise mit generiertem HTML-Inhalt aus einer JSP-Datei). Wenn die HTTP-Antwort festgeschrieben und beendet ist, werden sowohl das Anforderungs- als auch das Antwortobjekt recycelt und zur Wiederverwendung verfügbar gemacht.

HttpSession

Wenn ein Client die Webanwendung zum ersten Mal besucht und / oder die HttpSessionzum ersten Mal über abgerufen wird request.getSession(), erstellt der Servlet-Container ein neues HttpSessionObjekt, generiert eine lange und eindeutige ID (die Sie erhalten können session.getId()) und speichert sie auf dem Server Erinnerung. Der Servlet-Container setzt auch ein Cookiein den Set-CookieHeader der HTTP-Antwort mitJSESSIONID seinem Namen und der eindeutigen Sitzungs-ID als Wert fest.

Gemäß der HTTP-Cookie-Spezifikation (ein Vertrag, den jeder anständige Webbrowser und Webserver einhalten muss) muss der Client (der Webbrowser) dieses Cookie bei nachfolgenden Anforderungen im CookieHeader zurücksenden, solange das Cookie gültig ist ( dh die eindeutige ID muss sich auf eine nicht abgelaufene Sitzung beziehen und die Domäne und der Pfad sind korrekt). Mit dem integrierten HTTP-Verkehrsmonitor Ihres Browsers können Sie überprüfen, ob das Cookie gültig ist (drücken Sie F12 in Chrome / Firefox 23+ / IE9 + und überprüfen Sie die Registerkarte Netz / Netzwerk ). Der Servlet-Container überprüft den CookieHeader jeder eingehenden HTTP-Anforderung auf das Vorhandensein des Cookies mit dem Namen JSESSIONIDund verwendet seinen Wert (die Sitzungs-ID), um das zugehörige HttpSessionaus dem Serverspeicher abzurufen.

Das HttpSessionbleibt am Leben, bis es für mehr als den in <session-timeout>einer Einstellung angegebenen Zeitlimitwert inaktiv war (dh nicht in einer Anforderung verwendet wurde)web.xml . Der Timeout-Wert beträgt standardmäßig 30 Minuten. Wenn der Client die Web-App nicht länger als die angegebene Zeit besucht, wird die Sitzung vom Servlet-Container verworfen. Jede nachfolgende Anforderung hat auch mit dem angegebenen Cookie keinen Zugriff mehr auf dieselbe Sitzung. Der Servlet-Container erstellt eine neue Sitzung.

Auf der Clientseite bleibt das Sitzungscookie so lange aktiv, wie die Browserinstanz ausgeführt wird. Wenn der Client die Browserinstanz schließt (alle Registerkarten / Fenster), wird die Sitzung auf der Clientseite verworfen. In einer neuen Browserinstanz wäre das der Sitzung zugeordnete Cookie nicht vorhanden, sodass es nicht mehr gesendet wird. Dies führt zu einer völlig neuenHttpSession Cookie erstellt, wobei ein völlig neues Sitzungscookie verwendet wird.

In einer Nussschale

  • Das ServletContextLeben so lange wie die Web-App lebt. Es wird von allen Anforderungen in allen Sitzungen gemeinsam genutzt.
  • Das HttpSessionLeben dauert so lange, wie der Client mit derselben Webinstanz mit der Web-App interagiert und die Sitzung auf der Serverseite nicht abgelaufen ist. Es wird von allen Anforderungen in derselben Sitzung gemeinsam genutzt.
  • Das HttpServletRequestund HttpServletResponselive von dem Zeitpunkt an, an dem das Servlet eine HTTP-Anforderung vom Client empfängt, bis die vollständige Antwort (die Webseite) eingetroffen ist. Es wird nicht anderswo geteilt.
  • Alle Servlet, Filterund ListenerInstanzen leben, solange der Web - App lebt. Sie werden von allen Anforderungen in allen Sitzungen gemeinsam genutzt.
  • Alles attribute, was in definiert ServletContextist HttpServletRequestund HttpSessionso lange lebt, wie das betreffende Objekt lebt. Das Objekt selbst stellt den "Bereich" in Bean-Management-Frameworks wie JSF, CDI, Spring usw. dar. Diese Frameworks speichern ihre Scoped-Beans als einen attributeder am besten passenden Bereiche .

Gewindesicherheit

Ihr Hauptanliegen ist jedoch möglicherweise die Thread-Sicherheit . Sie sollten jetzt wissen, dass Servlets und Filter von allen Anforderungen gemeinsam genutzt werden. Das ist das Schöne an Java, es ist Multithread-fähig und verschiedene Threads (sprich: HTTP-Anforderungen) können dieselbe Instanz verwenden. Es wäre sonst zu teuer, sie neu zu erstellen, init()und zwar destroy()für jede einzelne Anfrage.

Sie sollten auch erkennen , dass Sie sollten nie irgendwelche Anfrage oder Sitzungs - Zielbereichsdaten als zuweisen Instanz Variable eines Servlets oder Filter. Es wird für alle anderen Anforderungen in anderen Sitzungen freigegeben. Das ist nicht threadsicher! Das folgende Beispiel veranschaulicht dies:

public class ExampleServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}

Siehe auch:

BalusC
quelle
25
Wenn ich also irgendwie die JSessionId herausfinden kann, die an einen Client gesendet wird, kann ich seine Sitzung stehlen?
Toskan
54
@Toskan: das stimmt. Es ist als Session Fixation Hack bekannt . Bitte beachten Sie, dass dies nicht spezifisch für JSP / Servlet ist. Alle anderen serverseitigen Sprachen, die die Sitzung über ein Cookie verwalten, sind ebenfalls vertraulich, z. B. PHP mit PHPSESSIDCookie, ASP.NET mit ASP.NET_SessionIDCookie usw. Das ist auch der Grund, warum das Umschreiben von URLs, ;jsessionid=xxxwie es einige JSP / Servlet MVC-Frameworks automatisch tun, verpönt ist. Stellen Sie einfach sicher, dass die Sitzungs-ID niemals in URLs oder auf andere Weise auf Webseiten verfügbar gemacht wird, damit der ahnungslose Endbenutzer nicht angegriffen wird.
BalusC
11
@Toskan: Stellen Sie außerdem sicher, dass Ihre Webanwendung nicht für XSS-Angriffe empfindlich ist. Dh keine benutzergesteuerten Eingaben in ungeklärter Form erneut anzeigen. XSS öffnete Türen, um Sitzungs-IDs aller Endbenutzer zu sammeln. Siehe auch Was ist das allgemeine Konzept hinter XSS?
BalusC
2
@ BalusC, Entschuldigung für meine Dummheit. Dies bedeutet, dass alle Benutzer auf dieselbe Instanz von thisIsNOTThreadSafe zugreifen, oder?
überschatten
4
@TwoThumbSticks 404 wird zurückgegeben, wenn das gesamte Servlet selbst fehlt. 405 wird zurückgegeben, wenn ein Servlet vorhanden ist, die gewünschte doXxx () -Methode jedoch nicht implementiert ist.
BalusC
428

Sitzungen

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

Kurz gesagt: Der Webserver gibt jedem Besucher bei seinem ersten Besuch eine eindeutige Kennung . Der Besucher muss diesen Ausweis mitbringen, damit er beim nächsten Mal erkannt wird. Mit dieser Kennung kann der Server auch Objekte einer Sitzung ordnungsgemäß von denen einer anderen Sitzung trennen.

Servlet-Instanziierung

Wenn load-on-startup ist falsch :

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

Wenn load-on-startup ist wahr :

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

Sobald er sich im Servicemodus und im Groove befindet, bearbeitet dasselbe Servlet die Anforderungen aller anderen Clients.

Geben Sie hier die Bildbeschreibung ein

Warum ist es nicht eine gute Idee, eine Instanz pro Client zu haben? Denken Sie darüber nach: Werden Sie für jede Bestellung einen Pizzaboten einstellen? Wenn Sie das tun, sind Sie in kürzester Zeit aus dem Geschäft.

Es ist jedoch mit einem geringen Risiko verbunden. Denken Sie daran: Dieser einzelne Mann hat alle Bestellinformationen in der Tasche. Wenn Sie also bei der Thread-Sicherheit von Servlets nicht vorsichtig sind , gibt er möglicherweise einem bestimmten Kunden die falsche Bestellung.

Jops
quelle
26
Ihr Bild ist sehr gut für mein Verständnis. Ich habe eine Frage: Was wird dieses Pizzarestaurant tun, wenn zu viele Pizzabestellungen eingegangen sind? Warten Sie einfach auf einen Pizzaboten oder stellen Sie mehr Pizzaboten ein? Vielen Dank .
zh18
6
Er wird eine Nachricht mitto many requests at this moment. try again later
Please_Dont_Bully_Me_SO_Lords
3
Servlets können im Gegensatz zu Pizzaboten mehr als eine Lieferung gleichzeitig ausführen. Sie müssen nur besonders darauf achten, wo sie die Adresse des Kunden und den Geschmack der Pizza
aufschreiben
42

Die Sitzung in Java-Servlets entspricht der Sitzung in anderen Sprachen wie PHP. Es ist einzigartig für den Benutzer. Der Server kann dies auf verschiedene Arten verfolgen, z. B. durch Cookies, Umschreiben von URLs usw. Dieser Java-Dokumentartikel erläutert dies im Kontext von Java-Servlets und gibt an, dass die genaue Verwaltung der Sitzung ein Implementierungsdetail ist, das den Designern des Servers überlassen bleibt. Die Spezifikation sieht nur vor, dass sie für einen Benutzer über mehrere Verbindungen zum Server hinweg eindeutig sein muss. In diesem Artikel von Oracle finden Sie weitere Informationen zu Ihren beiden Fragen.

Bearbeiten Es gibt ein ausgezeichnetes Tutorial hier , wie mit Sitzung innerhalb von Servlets zu arbeiten. Und hier ist ein Kapitel von Sun über Java-Servlets, was sie sind und wie man sie verwendet. Zwischen diesen beiden Artikeln sollten Sie in der Lage sein, alle Ihre Fragen zu beantworten.

Chris Thompson
quelle
Dies wirft für mich eine weitere Frage auf: Da es nur einen Servlet-Kontext für die gesamte Anwendung gibt und wir über diesen Servlet-Kontext Zugriff auf die Sitzungsvariablen erhalten. Wie können die Sitzungsvariablen für jeden Benutzer eindeutig sein? Danke ..
Ku Jon
1
Wie greifen Sie über den servletContext auf die Sitzung zu? Sie beziehen sich nicht auf servletContext.setAttribute (), oder?
Matt B
4
@ KuJon Jede Web-App hat ein ServletContextObjekt. Dieses Objekt hat null, ein oder mehrere Sitzungsobjekte - eine Sammlung von Sitzungsobjekten. Jede Sitzung wird durch eine Art Identifizierungszeichenfolge identifiziert, wie in Cartoons bei anderen Antworten zu sehen ist. Diese Kennung wird auf dem Client entweder durch Cookies oder durch Umschreiben von URLs verfolgt. Jedes Sitzungsobjekt hat seine eigenen Variablen.
Basil Bourque
33

Wenn der Servlet-Container (wie Apache Tomcat) gestartet wird, liest er aus der Datei web.xml (nur eine pro Anwendung), wenn etwas schief geht oder ein Fehler an der Containerseite angezeigt wird. Andernfalls wird das gesamte Web bereitgestellt und geladen Anwendungen mithilfe von web.xml (so genannt als Deployment-Deskriptor).

Während der Instanziierungsphase des Servlets ist die Servlet-Instanz bereit, kann jedoch die Clientanforderung nicht bedienen, da sie mit zwei Informationen fehlt:
1: Kontextinformationen
2: Informationen zur Erstkonfiguration

Die Servlet-Engine erstellt ein servletConfig-Schnittstellenobjekt, in das die oben genannten fehlenden Informationen eingekapselt sind. Die Servlet-Engine ruft init () des Servlets auf, indem sie servletConfig-Objektreferenzen als Argument angibt. Sobald init () vollständig ausgeführt wurde, ist das Servlet bereit, die Clientanforderung zu bedienen.

F) Wie oft erfolgt in der Lebensdauer des Servlets die Instanziierung und Initialisierung?

A) Nur einmal (für jede Clientanforderung wird ein neuer Thread erstellt) bedient nur eine Instanz des Servlets eine beliebige Anzahl der Clientanfragen, dh nachdem ein Clientanforderungsserver bedient wurde, stirbt er nicht ab. Es wartet auf andere Clientanforderungen, dh welche CGI-Einschränkung (für jede Clientanforderung wird ein neuer Prozess erstellt) mit dem Servlet überwunden wird (die interne Servlet-Engine erstellt den Thread).

F) Wie funktioniert das Sitzungskonzept?

A) Wann immer getSession () für das HttpServletRequest-Objekt aufgerufen wird

Schritt 1 : Das Anforderungsobjekt wird auf die ID der eingehenden Sitzung ausgewertet.

Schritt 2 : Wenn die ID nicht verfügbar ist, wird ein brandneues HttpSession-Objekt erstellt und die entsprechende Sitzungs-ID (dh der HashTable) generiert. Die Sitzungs-ID wird im httpservlet-Antwortobjekt gespeichert und die Referenz des HttpSession-Objekts wird an das Servlet zurückgegeben (doGet / doPost). .

Schritt 3 : Wenn keine ID verfügbar ist, wird kein brandneues Sitzungsobjekt erstellt. Die Sitzungs-ID wird aus der Anforderung übernommen. Die Objektsuche wird in der Sitzungssammlung unter Verwendung der Sitzungs-ID als Schlüssel durchgeführt.

Nach erfolgreicher Suche wird die Sitzungs-ID in HttpServletResponse gespeichert und die vorhandenen Sitzungsobjektreferenzen werden an doGet () oder doPost () von UserDefineservlet zurückgegeben.

Hinweis:

1) Wenn die Steuerung vom Servlet-Code zum Client wechselt, vergessen Sie nicht, dass das Sitzungsobjekt vom Servlet-Container, dh der Servlet-Engine, gehalten wird

2) Multithreading bleibt den Servlet-Entwicklern für die Implementierung überlassen, dh sie müssen die mehrfachen Anforderungen des Clients bearbeiten, ohne sich um Multithread-Code zu kümmern

Kurzform:

Ein Servlet wird erstellt, wenn die Anwendung gestartet wird (sie wird auf dem Servlet-Container bereitgestellt) oder wenn beim ersten Instanziieren des Servlets (abhängig von der Einstellung beim Laden beim Start) zum ersten Mal darauf zugegriffen wird, wird die init () -Methode des Servlets aufgerufen Dann verarbeitet das Servlet (seine einzige Instanz) alle Anforderungen (seine service () -Methode wird von mehreren Threads aufgerufen). Aus diesem Grund ist eine Synchronisierung nicht ratsam. Sie sollten Instanzvariablen des Servlets vermeiden, wenn die Anwendung nicht bereitgestellt wird (der Servlet-Container stoppt). Die Methode destroy () wird aufgerufen.

Ajay Takur
quelle
20

Sitzungen - was Chris Thompson gesagt hat.

Instanziierung - ein Servlet instanziiert wird , wenn der Behälter die erste Anforderung an das Servlet kartiert empfängt (es sei denn die Servlet Last beim Start mit dem so konfiguriert ist , <load-on-startup>Element in web.xml). Dieselbe Instanz wird verwendet, um nachfolgende Anforderungen zu bedienen.

Lauri Lehtinen
quelle
3
Richtig. Zusätzlicher Gedanke: Jede Anforderung erhält einen neuen (oder recycelten) Thread, der auf dieser einzelnen Servlet-Instanz ausgeführt wird. Jedes Servlet hat eine Instanz und möglicherweise viele Threads (wenn viele gleichzeitige Anforderungen vorliegen).
Basil Bourque
13

Die Servlet-Spezifikation JSR-315 definiert das Verhalten des Webcontainers in den Dienstmethoden (und doGet, doPost, doPut usw.) klar (2.3.3.1 Multithreading-Probleme, Seite 9):

Ein Servlet-Container kann gleichzeitig Anforderungen über die Servicemethode des Servlets senden. Um die Anforderungen zu verarbeiten, muss der Servlet-Entwickler angemessene Vorkehrungen für die gleichzeitige Verarbeitung mit mehreren Threads in der Servicemethode treffen.

Obwohl dies nicht empfohlen wird, besteht eine Alternative für den Entwickler darin, die SingleThreadModel-Schnittstelle zu implementieren, bei der der Container sicherstellen muss, dass die Dienstmethode jeweils nur einen Anforderungsthread enthält. Ein Servlet-Container kann diese Anforderung erfüllen, indem er Anforderungen an ein Servlet serialisiert oder einen Pool von Servlet-Instanzen verwaltet. Wenn das Servlet Teil einer Webanwendung ist, die als verteilbar markiert wurde, verwaltet der Container möglicherweise einen Pool von Servlet-Instanzen in jeder JVM, über die die Anwendung verteilt ist.

Für Servlets, die die SingleThreadModel-Schnittstelle nicht implementieren, kann der Servlet-Container den Instanzpool-Ansatz nicht verwenden, wenn die Servicemethode (oder Methoden wie doGet oder doPost, die an die Servicemethode der abstrakten Klasse HttpServlet gesendet werden) mit dem synchronisierten Schlüsselwort definiert wurde , muss aber Anfragen dadurch serialisieren. Es wird dringend empfohlen, dass Entwickler die Servicemethode (oder die an sie gesendeten Methoden) unter diesen Umständen nicht synchronisieren, da sich dies nachteilig auf die Leistung auswirkt

tharindu_DG
quelle
2
Zu Ihrer Information, die aktuelle Servlet-Spezifikation (2015-01) ist 3.1, definiert durch JSR 340 .
Basil Bourque
1
Sehr nette Antwort! @ tharindu_DG
Tom Taylor
0

Wie aus den obigen Erläuterungen hervorgeht, kann durch die Implementierung des SingleThreadModel ein Servlet durch den Servlet-Container die Thread-Sicherheit gewährleisten. Die Container-Implementierung kann dies auf zwei Arten tun:

1) Serialisieren von Anforderungen (Warteschlangen) an eine einzelne Instanz - dies ähnelt einem Servlet, das SingleThreadModel NICHT implementiert, ABER die Service / doXXX-Methoden synchronisiert. ODER

2) Erstellen eines Pools von Instanzen - eine bessere Option und ein Kompromiss zwischen dem Start- / Initialisierungsaufwand / der Zeit des Servlets und den restriktiven Parametern (Speicher- / CPU-Zeit) der Umgebung, in der sich das Servlet befindet.

Mahesh Balasubramanian
quelle
-1

Nr Servlets sind nicht Thread - sicher

Auf diese Weise können Sie auf mehrere Threads gleichzeitig zugreifen

Wenn du es Servlet als Thread-sicher machen willst, kannst du gehen

Implement SingleThreadInterface(i) Das ist eine leere Schnittstelle gibt es keine

Methoden

oder wir können Methoden synchronisieren

Wir können die gesamte Servicemethode synchronisieren, indem wir synchronisiert verwenden

Schlüsselwort vor Methode

Beispiel::

public Synchronized class service(ServletRequest request,ServletResponse response)throws ServletException,IOException

oder wir können den put-Block des Codes in den synchronisierten Block setzen

Beispiel::

Synchronized(Object)

{

----Instructions-----

}

Ich bin der Meinung, dass der synchronisierte Block besser ist als die gesamte Methode

Synchronisiert

Ved Prakash
quelle