ถั่ว Java แบบรัฐวิสาหกิจและไร้สัญชาติ


94

ฉันกำลังอ่านบทช่วยสอน Java EE 6 และฉันกำลังพยายามทำความเข้าใจความแตกต่างระหว่างเซสชันถั่วไร้สัญชาติและสถานะ หากเซสชันถั่วไร้สัญชาติไม่คงสถานะระหว่างการเรียกเมธอดเหตุใดโปรแกรมของฉันจึงทำงานในลักษณะที่เป็นอยู่

package mybeans;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;

@LocalBean
@Stateless
public class MyBean {

    private int number = 0;

    public int getNumber() {
        return number;
    }

    public void increment() {
        this.number++;
    }
}

ลูกค้า

import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
import mybeans.MyBean;
import java.io.PrintWriter;

@WebServlet(name = "ServletClient", urlPatterns = { "/ServletClient" })
public class ServletClient extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @EJB
    MyBean mybean;

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

        PrintWriter out = response.getWriter();
        mybean.increment();
        out.println(mybean.getNumber());
    }

}

ฉันคาดหวังว่า getNumber จะส่งคืน 0 ทุกครั้ง แต่มันกำลังส่งคืน 1 และการโหลด servlet ซ้ำในเบราว์เซอร์ของฉันจะเพิ่มมากขึ้น ปัญหาเกิดจากความเข้าใจของฉันเกี่ยวกับวิธีการทำงานของเซสชันถั่วที่ไม่มีสถานะไม่ใช่กับไลบรารีหรือแอปพลิเคชันเซิร์ฟเวอร์แน่นอน ใครช่วยยกตัวอย่างแบบง่ายๆของ hello world ของเซสชั่นบีนไร้สัญชาติที่ทำงานแตกต่างออกไปเมื่อคุณเปลี่ยนเป็น stateful ได้ไหม


6
ที่เกี่ยวข้อง: stackoverflow.com/questions/8887140/…คำตอบนี้อาจจะเข้าใจง่ายกว่า โปรดทราบว่าโดยทั่วไปแล้ว servlets จะถูกกำหนดขอบเขตของแอปพลิเคชัน (มีเพียง 1 แอปพลิเคชัน servlet ทั่วโลกซึ่งแชร์ / ใช้ซ้ำกับคำขอ / เซสชัน HTTP ทั้งหมด
BalusC

สวัสดีคุณเพิ่มครั้งแรกแล้วจึงได้ค่า .... ดังนั้นคุณไม่สามารถคาดหวังมูลค่า 0 ได้
rzur2004

ฉันแค่อยากจะขอบคุณที่ถามสิ่งนี้ซึ่งช่วยแก้ปัญหาของฉันได้ในขณะนี้ ฉันไม่สามารถขอให้ดีกว่านี้ได้
kholofelo Maloma

คำตอบ:


94

ความแตกต่างที่สำคัญไม่ใช่ตัวแปรของสมาชิกส่วนตัว แต่เป็นการเชื่อมโยงสถานะกับผู้ใช้บางราย (คิดว่า "ตะกร้าสินค้า")

ชิ้นส่วนของ stateful session bean เหมือนกับเซสชันใน servlets เซสชันถั่วที่มีสถานะช่วยให้แอปของคุณยังคงมีเซสชันนั้นแม้ว่าจะไม่มีเว็บไคลเอ็นต์ก็ตาม เมื่อเซิร์ฟเวอร์แอปดึงข้อมูลเซสชันแบบไม่ระบุสถานะออกจากพูลอ็อบเจ็กต์เซิร์ฟเวอร์จะรู้ว่าสามารถใช้เพื่อตอบสนองคำขอใด ๆ เนื่องจากไม่ได้เชื่อมโยงกับผู้ใช้รายใดรายหนึ่ง

เซสชั่น bean ที่มีสถานะจะต้องแจ้งให้ผู้ใช้ทราบตั้งแต่แรกเนื่องจากข้อมูลรถเข็นช็อปปิ้งของพวกเขาควรเป็นที่รู้จักสำหรับพวกเขาเท่านั้น เซิร์ฟเวอร์แอปช่วยให้มั่นใจได้ว่าเป็นเช่นนั้น ลองนึกภาพว่าแอปของคุณจะได้รับความนิยมแค่ไหนหากคุณสามารถเริ่มช็อปปิ้งได้จากนั้นเซิร์ฟเวอร์แอปก็ให้เซสชันบีนสถานะของคุณกับฉันเมื่อฉันมา!

ดังนั้นสมาชิกข้อมูลส่วนตัวของคุณจึงเป็น "รัฐ" แต่ไม่ใช่ "ตะกร้าสินค้า" พยายามทำซ้ำตัวอย่าง (ที่ดีมาก) ของคุณเพื่อให้ตัวแปรที่เพิ่มขึ้นเชื่อมโยงกับผู้ใช้เฉพาะ เพิ่มมูลค่าสร้างผู้ใช้ใหม่และดูว่าพวกเขายังสามารถเห็นมูลค่าที่เพิ่มขึ้นได้หรือไม่ หากทำอย่างถูกต้องผู้ใช้ทุกคนควรเห็นเฉพาะเวอร์ชันของตัวนับ


คุณสามารถให้คำตอบที่ชัดเจนในความคิดเห็นได้หรือไม่? เหตุใดถั่วไร้สัญชาติในตัวอย่างนี้จึงมีมูลค่าและเพิ่มขึ้นทุกครั้ง เพราะมีผู้ใช้เพียงคนเดียว?
arjacsoh

2
ตัวนับจะเพิ่มขึ้นโดยไม่คำนึงถึงจำนวนผู้ใช้ ดังนั้นหาก user1 เข้ามาและเพิ่มตัวนับเป็น 1 และพร้อมกันที่ user2 เข้ามาและเพิ่มขึ้นค่าจะเป็น 2 จริง ๆ แล้วควรแสดงว่า user1 มี 1 และ user2 มี 1 (หากเป็นสิ่งที่คุณตั้งใจจะทำตะกร้าสินค้า ตัวอย่างข้างต้น)
กฤษณะ

138

Stateless Session Beans (SLSB) ไม่ได้เชื่อมโยงกับไคลเอนต์หนึ่งรายและไม่มีการรับประกันว่าไคลเอนต์หนึ่งรายจะได้รับอินสแตนซ์เดียวกันกับการเรียกแต่ละวิธี (คอนเทนเนอร์บางตัวอาจสร้างและทำลายถั่วในแต่ละเซสชันการเรียกใช้วิธีนี้เป็นการตัดสินใจเฉพาะการใช้งาน แต่โดยทั่วไปแล้วอินสแตนซ์จะรวมกัน - และฉันไม่ได้พูดถึงสภาพแวดล้อมคลัสเตอร์) กล่าวอีกนัยหนึ่งแม้ว่าถั่วไร้สัญชาติอาจมีตัวแปรอินสแตนซ์ แต่ฟิลด์เหล่านี้ไม่ได้เจาะจงสำหรับไคลเอนต์หนึ่ง ๆ ดังนั้นอย่าพึ่งพาระหว่างการโทรระยะไกล

ในทางตรงกันข้ามถั่ว Stateful เซสชัน (SFSB) จะทุ่มเทเพื่อลูกค้าคนหนึ่งสำหรับชีวิตของพวกเขาทั้งไม่มีการแลกเปลี่ยนหรือร่วมกันของอินสแตนซ์ (มันอาจจะถูกขับไล่ออกจากหน่วยความจำหลังจากทู่เพื่อประหยัดทรัพยากร แต่ที่เรื่องอื่น) และการรักษาสถานะการสนทนา ซึ่งหมายความว่าตัวแปรอินสแตนซ์ของ bean สามารถเก็บข้อมูลที่สัมพันธ์กับไคลเอนต์ระหว่างการเรียกใช้เมธอด และสิ่งนี้ทำให้มีการเรียกเมธอดแบบพึ่งพาซึ่งกันและกัน (การเปลี่ยนแปลงที่ทำโดยวิธีเดียวจะส่งผลต่อการเรียกเมธอดในภายหลัง) กระบวนการหลายขั้นตอน (กระบวนการลงทะเบียนตะกร้าสินค้ากระบวนการจอง ... ) เป็นกรณีการใช้งานทั่วไปสำหรับ SFSB

อีกหนึ่งสิ่ง. หากคุณกำลังใช้ SFSB คุณต้องหลีกเลี่ยงการแทรกลงในคลาสที่มีลักษณะเป็นแบบมัลติเธรดเช่น Servlets และถั่วที่มีการจัดการ JSF (คุณไม่ต้องการให้ไคลเอ็นต์ทั้งหมดแชร์) หากคุณต้องการใช้ SFSB ในเว็บแอปพลิเคชันของคุณคุณต้องทำการค้นหา JNDI และจัดเก็บอินสแตนซ์ EJB ที่ส่งคืนในHttpSessionออบเจ็กต์สำหรับกิจกรรมในอนาคต อะไรแบบนั้น:

try {
    InitialContext ctx = new InitialContext();
    myStateful = (MyStateful)ctx.lookup("java:comp/env/MyStatefulBean");
    session.setAttribute("my_stateful", myStateful);
} catch (Exception e) {
    // exception handling
}

ขอบคุณสำหรับข้อมูลที่ชัดเจน เมื่อฉันใช้โปรแกรมบรรทัดคำสั่งแบบสแตนด์อโลนสำหรับไคลเอนต์จะเห็นความแตกต่างอย่างชัดเจน
Stanley kelly

ขอบคุณสำหรับความคิดเห็นพวกเขาให้ความกระจ่างมากขึ้น ก่อนอื่นคุณต้องให้คำจำกัดความเชิงนามธรรมจากนั้นระบุกรณีการใช้งานบางอย่างสำหรับแต่ละสถานการณ์จากนั้นชี้ให้เห็นข้อผิดพลาดบางประการ +1 ที่ยิ่งใหญ่
arthur

ส่วนที่หลีกเลี่ยงการฉีดออกไปสำหรับ EJB 3.1 ด้วยหรือไม่?
jacktrades

7
@Pascal หาก "Stateful Session Beans (SFSB) ทุ่มเทให้กับลูกค้ารายเดียวตลอดชีวิต" นั่นคือความสามารถนี้สร้างขึ้นใน SFSB แล้วทำไมต้องเก็บไว้ในวัตถุ HttpSession
user1169587

2
ทำไมเราต้องถือ stateful bean ไว้ในเซสชันหาก 'เซสชัน' แล้ว? ด้วยวิธีนี้เราสามารถทำให้ทุกออบเจ็กต์เซสชัน กรุณาอธิบาย
Georgy Gobozov

18

คนไร้สัญชาติและไร้สัญชาติในบริบทนี้ไม่ได้หมายถึงสิ่งที่คุณคาดหวัง

statefulness กับ EJBs หมายถึงสิ่งที่ผมเรียกรัฐสนทนา ตัวอย่างคลาสสิกคือการจองเที่ยวบิน หากประกอบด้วยสามขั้นตอน:

  • สำรองที่นั่ง
  • เรียกเก็บเงินจากบัตรเครดิต
  • ออกตั๋ว

ลองนึกภาพว่าแต่ละวิธีเป็นวิธีการเรียกเซสชั่นบีน เซสชันถั่วที่มีสถานะสามารถรักษาการสนทนาประเภทนี้ได้ดังนั้นจึงจดจำสิ่งที่เกิดขึ้นระหว่างการโทร

เซสชันถั่วไร้สัญชาติไม่มีความสามารถดังกล่าวสำหรับสถานะการสนทนา

ตัวแปรส่วนกลางภายใน session bean (stateless หรือ stateful) เป็นอย่างอื่นทั้งหมด เมล็ดถั่วเซสชันที่มีสถานะจะมีกลุ่มของถั่วที่สร้างขึ้น (เนื่องจากถั่วสามารถใช้ได้ในการสนทนาครั้งละหนึ่งรายการเท่านั้น) ในขณะที่ถั่วเซชั่นไร้สัญชาติมักจะมีเพียงอินสแตนซ์เดียวซึ่งจะทำให้ตัวแปรส่วนกลางทำงานได้ แต่ฉันไม่คิดว่า สิ่งนี้จำเป็นต้องรับประกัน


5

คำถามที่ดี,

ลองใช้รหัสนี้ (เปลี่ยน MyBean Stateful / Stateless):

import javax.ejb.LocalBean;
import javax.ejb.Stateful;
import javax.ejb.Stateless;

@LocalBean 
@Stateless 
public class MyBean {

    private int number = 0;

    public int getNumber() {
        return number;
    }

    public void increment() {
        this.number++;
    }
}

Servlet_1

 import java.io.IOException;
    import javax.ejb.EJB;
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.WebServlet;

    import java.io.PrintWriter;

    @WebServlet(name = "ServletClient", urlPatterns = { "/ServletClient" })
    public class ServletClient extends HttpServlet {

        private static final long serialVersionUID = 1L;

        @EJB
        MyBean mybean;

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

            PrintWriter out = response.getWriter();
            mybean.increment();
            out.println(mybean.getNumber());
        }

    }

Servlet_2

import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;

import java.io.PrintWriter;

@WebServlet(name = "NewServletClient", urlPatterns = { "/NewServletClient" })
public class NewServletClient extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @EJB
    MyBean mybean;

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

        PrintWriter out = response.getWriter();
        mybean.increment();
        out.println(mybean.getNumber());
    }

}

กรณี: MyBean - @ Stateless

http: // localhost: 8080 / MYServletDemo / ServletClient

1

http: // localhost: 8080 / MYServletDemo / ServletClient

2

http: // localhost: 8080 / MYServletDemo_war_exploded / newServletClient

3

http: // localhost: 8080 / MYServletDemo / ServletClient

4

กรณี: MyBean - @ Stateful

http: // localhost: 8080 / MYServletDemo / ServletClient

1

http: // localhost: 8080 / MYServletDemo / ServletClient

2

http: // localhost: 8080 / MYServletDemo / newServletClient

1

http: // localhost: 8080 / MYServletDemo / ServletClient

3


1
ใช่มันได้ผล! คำอธิบายง่ายมากขอบคุณ!
Nesquik27

5

ความแตกต่างที่สำคัญระหว่างถั่วเซสชั่นสองประเภทหลักคือ:

ถั่วไร้สัญชาติ

  1. ถั่วเซสชันไร้สัญชาติเป็นถั่วที่ไม่มีการสนทนากับลูกค้าซึ่งเรียกว่าวิธีการ ด้วยเหตุนี้พวกเขาจึงสามารถสร้างกลุ่มวัตถุที่สามารถใช้เพื่อโต้ตอบกับ ไคลเอนต์หลาย ๆ
  2. ถั่วไร้สัญชาติที่มีประสิทธิภาพจะดีกว่าเนื่องจากไม่มีสถานะต่อลูกค้า
  3. พวกเขาสามารถจัดการคำขอหลายรายการจากไคลเอนต์หลายรายพร้อมกันได้

ถั่วที่มีสถานะ

  1. เซสชันถั่วที่มีสถานะสามารถรักษาสถานะการสนทนากับไคลเอนต์หลายรายพร้อมกันและงานจะไม่ถูกแชร์ระหว่างไคลเอนต์
  2. หลังจากเซสชันเสร็จสิ้นสถานะจะไม่ถูกเก็บไว้
  3. คอนเทนเนอร์สามารถทำให้เป็นอนุกรมและจัดเก็บสถานะเป็นสถานะเก่าสำหรับการใช้งานในอนาคต สิ่งนี้ทำเพื่อประหยัดทรัพยากรของแอ็พพลิเคชันเซิร์ฟเวอร์และเพื่อรองรับความล้มเหลวของ bean

4

สิ่งนี้เกิดขึ้นเนื่องจากคอนเทนเนอร์มีอินสแตนซ์ bean เดียวในพูลที่ถูกนำมาใช้ซ้ำสำหรับการโทรทั้งหมด หากคุณรันไคลเอ็นต์พร้อมกันคุณจะเห็นผลลัพธ์ที่แตกต่างออกไปเนื่องจากคอนเทนเนอร์จะสร้างอินสแตนซ์ bean เพิ่มเติมในพูล


4

มันมีคำตอบที่ดี ฉันขอเพิ่มคำตอบเล็ก ๆ ไม่ควรใช้ Stateless Bean เพื่อเก็บข้อมูลไคลเอนต์ใด ๆ ควรใช้ "เพื่อจำลองการดำเนินการหรือกระบวนการที่สามารถทำได้ในภาพเดียว"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.