Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Zwei Mal dieselbe Anwendung in 2 verschiedenen Browsern starten
ich habe eine SpringBoot-Anwendung, einen Rechner, den man auf localhost:8080 erreichen kann. Wenn man aber diese Adresse mit zwei verschiedenen Browsern aufruft, kommen die sich "in die Quere", d.h. eine 4, die ich in einem Browser eingegeben habe, erscheint auch im zweiten. Wie kann ich das umgehen?
Kannst Du das einmal im Detail ausführen? Wenn Du zwei unterschiedliche Browser hast (also z.B. Edge und Firefox), dann hast Du auf jeden Fall zwei unterschiedliche Sessions.
Wenn Du nur einen Browser verwendest, aber mehrere Tabs oder Fenster öffnest, dann hast Du nur eine Session. Dies kannst Du vermeiden, in dem Du unterschiedliche Profile verwendest. Dies kann z.B. über die Verwendung von InPrivate Fenstern erfolgen.
Generell ist aber auch die Frage, was Du genau machst. Was genau machst Du in der Spring Boot Anwendung? Was ist im Browser? Wenn Du im Browser eine 4 eingibst: Was machst Du damit genau? Evtl. kannst Du hier einmal mehr Details geben. Hier kommt dann vermutlich der Scope ins Spiel: Wenn Du in Spring Boot einen @Controller hast, dann ist das ein Bean mit Scope Singleton. Das bedeutet, es gibt genau eine Instanz eines solchen Controllers. Wenn Du also eine 4, die von einem Client gekommen ist, in dem Controller speicherst, dann wäre das geteilt zwischen allen Clients (Unabhängig von Browsern, Sessions, Systemen, ....)
Daher wäre meine Bitte, doch einmal mehr Details zu nennen, was Du genau machst und dann können wir die Details erläutern.
Kannst Du das einmal im Detail ausführen? Wenn Du zwei unterschiedliche Browser hast (also z.B. Edge und Firefox), dann hast Du auf jeden Fall zwei unterschiedliche Sessions.
Wenn Du nur einen Browser verwendest, aber mehrere Tabs oder Fenster öffnest, dann hast Du nur eine Session. Dies kannst Du vermeiden, in dem Du unterschiedliche Profile verwendest. Dies kann z.B. über die Verwendung von InPrivate Fenstern erfolgen.
Generell ist aber auch die Frage, was Du genau machst. Was genau machst Du in der Spring Boot Anwendung? Was ist im Browser? Wenn Du im Browser eine 4 eingibst: Was machst Du damit genau? Evtl. kannst Du hier einmal mehr Details geben. Hier kommt dann vermutlich der Scope ins Spiel: Wenn Du in Spring Boot einen @Controller hast, dann ist das ein Bean mit Scope Singleton. Das bedeutet, es gibt genau eine Instanz eines solchen Controllers. Wenn Du also eine 4, die von einem Client gekommen ist, in dem Controller speicherst, dann wäre das geteilt zwischen allen Clients (Unabhängig von Browsern, Sessions, Systemen, ....)
Daher wäre meine Bitte, doch einmal mehr Details zu nennen, was Du genau machst und dann können wir die Details erläutern.
Klar. Also es ist ein Webtaschenrechner, ich hab genau einen Controller und wenn ich mit dem Button "4" (0-9 sind möglich) klicke, erscheint im oberen Textfeld eine 4. Dann der Operator und dann die zweite Zahl, ganz simpel gehalten. Für das Ergebnis muss man "=" drücken und das erscheint dann im zweiten Textfeld.
Die Änderung wäre sehr klein und überschaubar: Zu dem @Controller kommt einfach noch ein @SessionController @SessionScope
Das geht recht gut, so lange es mehrere Session sind. Also zwei unterschiedliche Browser oder so. Die Probleme sind aber wieder da, wenn jemand in zwei Tabs arbeiten würde.
Das ist aber eine Lösung, die so eher unüblich geworden ist. In modernen Anwendungen versucht man, es zu vermeiden, so einen Status zu merken oder zu speichern. Man spricht von Stateless. Damit das funktioniert, gibst Du alle Daten von der Seite als Parameter mit. Neben der Eingabe also auch die Anzeige. Wenn Du irgendwas zwischenspeichern willst, dann kannst Du das über unsichtbare Felder machen. Und im Controller schreibst Du dann auch alles in das model damit es in der Seite gespeichert wird.
So vermeidest Du, dass unnötige Instanzen aufgebaut werden. Du hast auch nicht das Problem, dass Daten verloren gehen, wenn eine Session auf einen Timeout läuft und so eine Controller Instanz aus dem Speicher entfernt wird. Und das Interface ist auch gut testbar.
@Edit: Hatte mich bei der Annotation verschrieben - es muss natürlich SessionScope sein und nicht SessionController! Der Link beschreibt die Scopes aber sehr gut und gibt auch Beispiele ...
Wie sieht das denn aus, so eine Implementation einer Session Bean? Session Bean erstellen, in den Controller injizieren und dann mit Gettern/Settern Variablen aus dem Controller in der Session-Klasse speichern?
Wenn Du die erste Version nutzen willst, dann sollte es wirklich nur eine zusätzliche Annotation sein:
Java:
@Controller
@SessionScope
public class Controller1 {
(Ich habe gerade gesehen, dass ich beim Tippen mich verschrieben hatte - wenn man nicht gut genug schaut und man zu schnell tippt. Aus SessionScope beim Controller ist da irgendwie "SessionController" geworden .... Sorry dafür - ich habe keine Ahnung, wie es dazu gekommen ist und habe es noch einmal editiert.)
Der gegebene Link erklärt die verschiedenen Scopes aber sehr gut und gibt auch entsprechende Beispiele.
@Target(TYPE)
@Retention(RUNTIME)
@Documented
@Component
public @interface Controller
Das kannst Du als eine Art "Vererbung" ansehen. Ein @Controller ist damit auch eine @Component und damit kann man diesen Scope auch entsprechend auf dem Controller direkt setzen.
Das Szenario von Dir ginge natürlich auch - Du kannst eine eigene Component bauen, welche SessionScope hat und diese dann in den Controller mit Singleton Scope injecten.
ABER: Vermutlich hast du zu Recht Bauchschmerzen, denn das scheint ja erst einmal nicht zu passen: Es wird ja beim Controller z.B. per Konstruktor Injection nur einmalig eine Instanz eingefügt und nicht jedes Mal pro Session. Hier muss man also noch etwas mehr machen:
a) Statt direkt die @Component CalculatorSessionData (einfach mal als Name für die Komonente gewählt) zu Injecten kann man hier die Komponente jedes Mal holen. Das ginge z.B. über eine ObjectFactory<T>, sprich: Du hast eine Variable ObjectFactory<CalculatorSessionData> calculatorSessionDataFactory; die Du injectest. Und immer, wenn Du in der Verarbeitung eines Requests damit arbeiten willst, holst Du Dir diese per get() Aufuf.
b) Spring Boot arbeitet viel mit Proxies. Auch hier hat Spring Boot eine Lösung, die es für Dich löst. Dazu schauen wir uns einfach einmal SessionScope an:
Man erkennt: Man kann eine proxyMode setzen. Und wenn man da einen TARGET_CLASS proxyMode setzt, dann ist sicher gestellt, dass es einen Proxy gibt, der dann auch u.a. die Scope Problematik lösen kann.
Das findet sich dann z.B. unter https://www.logicbig.com/tutorials/spring-framework/spring-core/scoped-proxy.html mit einem Beispiel.
Was man an der SessionScope Dokumentation auch sehen kann: Die @SessionScope Annotation entspricht auch der @Scope("session") Annotation. Das kann hilfreich sein zu wissen, damit Du bei Recherchen nicht verwirrt wirst. Denn das findet man auch öfters mal.
a) hat aus meiner Sicht den Vorteil, dass man dies sehr gut im Code nachvollziehen kann.
b) macht eine Art Magie, welche man nicht direkt an der Stelle der Nutzung erkennen kann, was ich nicht gut finde (Ein @Controller mit einer Injection von vermutlichen Session Daten führt erst einmal zu Bauchschmerzen bei mir!) aber dafür stellt es sicher, dass so ein Fehler eben nicht auftreten kann.
Wenn Du hier tiefer einsteigen willst: Ich kann dir nur das Buch von Christian Ullenboom "Spring Boot 3 und Spring Framework 6" empfehlen. Er gibt aus meiner Sicht eine extrem gute Übersicht über die einzelnen Bean Annotations und deren Verhalten. U.a. auch mit Fragestellungen wie: Wann wird ein Proxy erstellt und wann nicht ... Und sehr schön: Das Buch ist in Deutsch (gibt es auch als Englische Übersetzung, aber in der Mutterspache lässt sich sowas ja besser lesen....).