Servlet

Màu nền
Font chữ
Font size
Chiều cao dòng

Web server làm nhiệm vụ gì?

- 1 web server nhận 1 request từ client và trả về 1 thứ gì đó cho client.

- 1 web browser cho phép 1 user request a resource. Web server nhận các yêu cầu, tìm resource và trả về thứ gì đó cho client. Những resource có thể là html page, pictures hoặc sound files. Nó cũng có thể là 1 file PDF. Đừng bận tâm, client yêu cầu thứ gì(resouce) thì server sẽ gửi lại thứ đó....Trừ khi thứ đó không có trên server. Hoặc là nó không tồn tại. "404 Not Found" là lỗi mà server sẽ phản hồi cho bạn khi server không thể tìm thấy thứ mà bạn yêu cầu.

Khi chúng tôi nói đến server thì nó có thể là máy vật lý(Hardware) hoặc web server application(software). Xuyên suốt cuốn sách này, nếu khi nào chúng ta nói đến server mà cần phải phân biệt nó thì tôi sẽ nói nó rõ ra.

Request của client thường chứa tên và địa chỉ (url) của thứ mà họ tìm kiếm.


Response của server chứa các document mà người dùng yêu cầu (Hoặc là mã lỗi nếu request không thể xử lý)

Server thường chứa rất nhiều các nội dung để gửi cho client.. Nội dung này có thể là các web pages, jpeg và các resource khác.

2. Web client làm gì?

1 web client cho phép 1 user gửi request(web browser) tới server và show cho user kết quả của yêu cầu đó. Tức là 1 web client đã bảo gồm 1 web browser.

Khi chúng ta nói về clients, chúng ta thường dùng(both) nghĩa là human user và browser application. 1 trình duyệt là 1 phần của 1 phần mềm(Giống cốc cốc), nó cho phép kết nối tới server. 1 công việc chính nữa của trình duyệt là thông dịch các mã html và render nội dung của trang cho người dùng. 1 web client là 1 web browser.

Kể từ giờ khi chúng ta sử dụng khái niệm client thì chúng ta không cần quan tâm về human user hay là browser app. Nói theo 1 cách nào đó thì user với browser app cùng làm 1 việc.

3. Client và server đều biết html và http

- Html : khi 1 server trả lời 1 request, server sẽ thường sử dụng 1 trong vài loại nội dung(content) mà browser có thể hiển thị được. Server thường sẽ trả về cho browser 1 tập các instruction được viết bằng html. Và html sẽ nói cho trình duyệt biết làm thế nào để hiển thị nội dung lên.

Tất cả các web browser đều biết phải làm gì với html.

- Http: Phần lớn sự kết nối giữa client và server là thông qua giao thức http, nó cho phép việc kết nối trở nên đơn giản hơn. "Client gửi 1 http request, và server gửi trả lại 1 http response". Tóm lại để gửi 1 html page to client thì cần phải thông qua http.

Để có 1 kết nối (communicate) thì cần 1 ngôn ngữ chung. Trên web thì clients và server đều "speak" http. Mà browser phải biết html.

Html nói cho trình duyệt biết làm thế nào để hiển thị nội dung

http là giao thức mà client và server sử dụng trên web để kết nối

Server sử dụng http để gửi html tới clients

4. Hai phút hướng dẫn với HTML

Khi phát triển 1 web page, bạn sử dụng html để mô tả page(giao diện)

Html có hang chục tags và hàng trăm các thuộc tính. Mục đích chính của html là lưu trữ văn bản đi kèm với format của nó.

Chỉ cần html cơ bản là đủ.

Browser đọc các html code và tạo ra web pages bằng cách render html code.

5. Cấu trúc của 1 cuộc hội thoại http trên web là 1 tập các request/response. 1 browser request và 1 server response.

Thành phần chính của 1 request: http method (Post/get), trang truy nhập(URL), tham số của phương thức.

Thành phần chính của 1 response: Mã trạng thái status(404, 1), thành phần quy định loại nội dung(text, picture, html) và nội dung của nó.

Http là 1 giao thức chuẩn của quốc tế.

6. Html là 1 phần của http response. 1 html response có thể chứa html. http sẽ add thêm vào http header info thông tin để browser xác định loại nội dung response sẽ trả về (html, text,..)

7. Vậy thì cái gì là request?

Điều đầu tiên mà bạn cần hiểu Request là 1 http method. Đây không phải là java method, nhưng ý tưởng thì giống nhau. Tên phương thức nói cho server biết loại request nào được tạo và phần còn lại của message sẽ được format như thế nào. HTTP protocol có 1 vài phương thức, nhưng chúng ta sẽ chỉ tập trung vào Get và post.

8. Get và Post

Get là HTTP Request đơn giản nhất. Post thì có thể gửi dữ liệu user(nhạy cảm).

Get là phương thức đơn giản nhất trong các phương thức của http method. Nhiệm vụ chính của Get là gửi yêu cầu tới server và nhận lại 1 resouce(html, jpeg)(bằng cách web Container add nó vào trong response) và Get http được dùng để gửi response lại cho client. Điểm quan trọng của GET là dùng để get cái gì đó trả về từ server. Get đóng vai trò là kênh truyền dẫn để gửi các Request từ browser lên Servlet và Servlet add response vào nó để gửi lại client.

Post thì là một yêu cầu mạnh hơn. Nó giống như GET++. Với post, bạn có thể request 1 cái gì đó ở cùng thời điểm send form data tới server.

9. HTTP GET

Sự thật là bạn chỉ có thể gửi 1 lượng nhỏ data với http get. Vậy lí do mà sử dụng post thay cho get là gì?

- Tổng số kí tự của Get bị giới hạn(Tùy thuộc vào server). Nếu user type(say) 1 đoạn văn quá dài vào bên trong 1 ô input box thì get có thể sẽ không làm việc

- Data mà bạn gửi bằng phương thức Get sẽ được appended vào URL hiển thị trên thanh browser bar, vì thế bất kì cái gì bạn gửi sẽ bị lộ. Tốt hơn là không nên put password và những dữ liệu nhạy cảm khi dùng GET

- Chính vì 2 điều trên, user không thể bookmark 1 form submission nếu bạn dùng POST. Tùy vào app, bạn có thể muốn hoặc không user của mình bookmark kết quả của request.

10. Phân tích 1 http get request

Hurry it up: Làm nhanh lên

FYI: Nghĩa là: thông tin cho bạn

11. Phân tích 1 http post request.

Data sẽ được add vào message body. Vì thế data nhiều hay ít cũng không ảnh hưởng gì

1. Phân tích 1 http respose và thật sự thì MIMI type là cái quái gì thế?

1 http response có 1 header và 1 body. Header info nói cho browser về giao thức được sử dụng. Nói về request có thành công hay không và content-type của reponse body... Và body chứa content(html, jpeg...) để trình duyệt hiển thị.

Content-type trong header info của response header = MIME type

2. Tất cả các phần, trong 1 page.

3. Hỏi nhỏ

Với các công việc sau đây, bạn sẽ quyết định dùng get hay post

POST/GET A user is returning a login name and password.

POST/GET A user is requesting a new page via a hyperlink

POST/GET A chat room user is sending a written response.

POST/GET A user hits the 'next' button to see the next page

POST/GET A user hits the 'log out' button on a secure banking site

POST/GET A user hits the 'back' button on the browser.

POST/GET A user sends a name and address form to the server.

POST/GET A user makes a radio button selection.

4. Hỏi nhỏ

Với các công việc sau đây, bạn sẽ quyết định dùng get hay post

5. URL. Bất kì cái gì bạn cũng có thể làm với nó. Nhưng đừng phát âm nó là "earl"

URL: Uniform resource locators. Mọi resource trên web đều có 1 địa chỉ riêng biệt, trong dạng thức url

Tên server: tên duy nhất của máy server vật lý. Tên ánh xạ với 1 địa chỉ IP duy nhất. IP address là 1 số mà có dạng xxx.yyy.zzz.aaa. Có thể sử dụng IP address thay cho server name, nhưng nếu nhớ bằng server name thì dễ nhớ hơn nhiều.

Port: Đây là 1 tùy chọn, 1 server có thể support nhiều cổng. 1 server app được xác định bởi 1 cổng. Nếu không chỉ ra cổng cụ thể thì mặc định sẽ là cổng 80.

Path: Path chỉ ra vị trí trên server app, của resource được yêu cầu. Phần lớn web server chạy trên Unix.

Hãy nhớ rằng. Nếu phương thức gửi đi là Get request thì thông tin params sẽ được append vào cuối của url. Bắt đầu bởi dấu ?, và mỗi cặp tham số name/value sẽ được cách nhau bới dấu "&"

Tất cả các cổng đều là 1 định danh duy nhất. 1 cổng đại diện cho 1 kết nối logical tới 1 phần cụ thể của phần mềm chạy trên server phần cứng. Nếu không có số cổng server sẽ không có cách nào biết được ứng dụng nào mà client muốn kết nối đến. Nếu mỗi app không có cổng riêng của mình, hãy nghĩ về những vẫn đề mà bạn có thể gặp phải: Điều gì sẽ xảy ra nếu web browser của bạn hạ cánh nhầm vào POP3 server mà đáng ra nó phải là http server. Mail server sẽ không thể biết làm như thế nào để parse 1 http request. Mà giả sử nó làm được đi chăng nữa thì POP3 server cũng không thể phục vụ được các html page.

1. Static pages.

Web server rất thích các trang static web pages.

Nhưng thỉnh thoảng ta cần nhiều hơn ở 1 web server. Web server không thể tạo ra 1 trang html dạng Just in time được. Nó không thể generate html được. Nó cần 1 help app(Servlet) để có thể giúp nó làm điều này.

I. Kiến trúc của Web app

1. What is a container?

Servlet không có phương thức main(). Chúng được đặt dưới sự điều khiển của một java app khác gọi là container.

Tomcat là 1 ví dụ của container. Khi web server app(ví dụ apache) của bạn nhận 1 request yêu cầu 1 servlet( hay 1 static old html page) thì server sẽ xử lý request chứ không phải là bản thân servlet. Nhưng để làm được điều đó thì servlet is deployed. Khi đó Container sẽ cung cấp cho servlet http request và http response. Và container sẽ gọi ra phương thức tương ứng của servlet(doPost hoặc doGet)

2. Life without servlet

Điều gì sẽ xảy ra nếu như có java mà không có servlet hoặc container?

Điều gì sẽ xảy ra nếu bạn cần phải viết 1 java program để xử lý các dynamic request to a web servet app(like Apache) mà không có 1 container giống như Tomcat. Nói theo cách khác, tưởng tượng là bạn sẽ không có servlet, và tất cả những gì bạn có sẽ chỉ là các thư viện j2se. (Dĩ nhiên là bạn có thể nói là bạn có khả năng cấu hình web servet để nó gọi trực tiếp các java program). Điều đó là đúng nếu bạn chưa biết gì về việc container làm. Chỉ cần tưởng tượng bạn cần 1 hỗ trợ phía server cho ứng dụng web của mình. Mà lại chỉ có plain old Java.

Ví dụ các công việc phải làm như: Tạo ra 1 socket kết nối tới server, và tạo 1 listener ở server....

Vậy thì sau đây là những điều mà container sẽ cung cấp cho bạn?

Chúng ta đã biết rằng Container quản lí và chạy servlet, nhưng như thế nào?

- Hỗ trợ giao tiếp: Container cung cấp 1 cách dễ dàng để Servlet của bạn nói chuyện với web server của bạn. Bạn không cần phải build 1 server socket, lắng nghe port nào đó, tạo stream nào đó... Container biết rõ giao thức giữa web server và bản thân nó. Vì thế Servlet của bạn không cần phải lo lắng về API giao tiếp giữa chúng(Chúng ở đây được hiểu là web server Apache và web app code của bạn. Và tất cả những điều bạn cần bận tâm lúc này chỉ là nghiệp vụ logic của bạn sẽ xử lý trong servlet như thế nào(Accept 1 đơn hàng)).

- Quản lý vòng đời: Container điều khiển sự sống và chết với Servlet của bạn. Nó đảm bảo việc load các class, tạo ra các thể hiện và khởi tạo Servlet, gọi ra các phương thức Servlet và đảm bảo các thể hiện này sẽ được thu dọn bởi bộ thu dọn rác garbage collection. Với Container thì bạn không cần phải lo lắng nhiều về việc quản lí resource.

- Hỗ trợ multi thread: Container tự động tạo ra 1 thread với servlet nếu client yêu cầu. Khi mà Servlet chạy xong http service đối với yêu cầu của client thì thread sẽ được hoàn thành. Tuy nhiên bạn vẫn có thể gặp các vấn đề về synchronization. Mà có thằng quản lý cái này cho thì công việc bạn phải làm sẽ ít đi.

- Khai báo security. Security sẽ được cấu hình trong xml configuration.

- Hỗ trợ jsp: Bạn đã biết jsp page tuyệt vời như nào chưa nhỉ. Đúng đó, vậy thì ai đảm bảo jsp sẽ dịch ra code java. Dĩ nhiên, Container đấy

3. The Container

Container xử lý 1 Request như thế nào. Chúng ta sẽ làm rõ ở chương sau, nhưng đây là 1 cái nhìn sơ lược.

Người dung click vào 1 link có url tới Servlet chứ ko phải 1 static page

è The Container nhìn thấy Request này là cho 1 servlet, vì thế Container tạo ra 2 objectectect:

o HttpServletResponse

o HttpServletRequest

Container tìm ra Servlet dựa vào url, tạo hoăc cấp phát 1 thread cho Request, và truyền Request và response object tới Servlet thread

Phương thức doGet() tạo ra các trang động và đưa nó vào response obj

Thread thực hiện xong, Container chuyển response object thành http response rồi gửi nó cho client, tiếp đó Container sẽ xóa Request và response object .

4. Servlet code

Code Servlet sẽ trông như thế nào?

- Trong thực tế thì 99.99% Servlet được override hoặc doGet() hoặc doPost().

99.9999% Servlet là extends từ HttpServlet

- Không có hàm main(). Vòng đời của Servlet là vòng đời của phương thức(doGet()) được gọi bởi Container.

- Request và response object truyền vào các phương thức doPost() hoặc doGet() dưới dạng params. Đó là các reference mà container đã tạo và truyền vào các phương thức này.

- Bạn có thể get PrintWriter từ response object mà Servlet của bạn nhận được từ Container. Sử dụng printWrite này để viết html text to response object . Ngoài ra bạn cũng có thể output ra các picture thay cho các html text.

url và Servlet liên quan đến nhau như thế nào? -> làm rõ ở chương sau(chapter Deployment)

tuy nhiên đoạn sau đây sẽ cung cấp 1 cái nhìn sơ lược cho bạn.

1. Bạn đang băn khoăn là container tìm ra servlet như thế nào.

Bằng cách nào đó, URL (như 1 phần request của client) được mapped với 1 servlet xác định trên server. Sự mapping của url với servlet có thể được xử lý qua 1 số cách khác nhau. Và vấn đề này sẽ là 1 trong những vấn đề cơ bản mà bạn phải đối mặt. request của user phải map với 1 servlet cụ thể. Và để map được thì bạn phải configure. Bạn nghĩ ntn? Ai sẽ chứa các configure này.

Vậy container map servlet với urls như thế nào.

2. Mapping urls tới servlet

1 servlet có thể có 3 tên.

1 servlet có thể có 1 tên file path, tức là 1 đường dẫn tới 1 class file thực tế. Kiểu SignUpServlet.class . Và người lập trình viên có thể làm điều này(Vì chỉ ông mới biết cấu trúc thư mục). Nhưng 1 ai đó deploy servlet có thể tạo ra 1 tên(fake name) và tên này ko cần phải map với public url hoặc real file path của 1 servlet

1 tên nữa là url name. tên mà client biết về nó. Người dùng không biết là servlet name này sẽ map với class trong directory như thế nào. Tên này cũng là 1 fake name để làm thân thiện với người dùng.

Mapping servlet names sẽ cải thiện tính mềm dẻo và bảo mật.l

Nghĩ về điều này.

3. Sử dụng deployement descriptor để map urls tới servlets

Khi bạn deploy servlet của bạn vào bên trong web container, bạn sẽ tạo ra 1 tài liệu xml đơn giản gọi là deployment descriptor(dd) để nói với container bạn sẽ chạy servlet và jsp của mình như thế nào. Mặc dù bạn sẽ sử dụng DD cho nhiều việc nữa ngoài việc mapping names, thì bạn cũng sẽ sử dụng 2 elements xml để map urls tới servlets- 1 để map public url mà client biết với internal name(deployed name) và 1 cái dùng để map internal name với cái class name đầy đủ tiêu chuẩn.

2 thành phần trong DD mà bạn sử sử dụng để mapping.

- <servlet>

Maps internal name(deployed name) tới fully-qualified class name(tên servlet)_ Không cần phải add đuôi mở rộng ".class"

- <servlet-mapping>

Maps internal name(deployed name) với public url name

Có thể sử dụng kí hiệu trong thành phần <url-pattern> . Thành phần <servlet-mapping> là thành phần mà container sẽ dùng lúc runtime khi 1 request đến và hỏi "tao sẽ được thằng servlet nào phục vụ khi tao đã nó gọi bằng đường dẫn request url này?"

Nhưng chờ đã. Có nhiều thứ mà bạn có thể làm với DD nữa đấy.

Ngoài việc mapping urls với servlet thì thực tế bạn có thể dùng DD để customize những khía cạnh khác nhau với web app của bạn: security roles, error pages, tag libraries, thông tin cấu hình khởi tạo và nếu là 1 J2EE server còn có thể cấu hình truy cập vào các javabeans.

Đừng lo lắng về những chi tiết này. Điểm quan trọng bây giờ là bạn biết được DD sẽ cho bạn 1 cách để modify app của bạn mà không cần phải thay đổi source code. Điều này có nghĩa là không cần phải là 1 java programmer vẫn có thể customize web app của bạn mà không cần kéo bạn ra khỏi kì nghỉ của mình.

Q: Tôi vẫn băn khoăn. Nhìn vào DD bạn vẫn không có bất kì cái gì chỉ ra name đường dẫn thực tế của servlet. Nó chỉ nói về class name. Và điều này vẫn chưa trả lời cho câu hỏi làm như thế nào để container sử dụng class name này để tìm ra servlet class file tương ứng. Còn chỗ nào khác mapping class name với class file ở 1 location nào đó không?

A: Bạn đã nhận ra điều đó rồi. Trong thực tế container sử dụng 1 tập các luật cho việc tìm ra a match giữa url client request và actual java class ở 1 nơi nào đó trên server. Chúng ta sẽ giới thiệu nó ở chương sau đó(Deployment). Giờ đây điều cần nhớ nhất là bạn có thể mapping điều đó.

DD benefits

- Giảm thiểu việc chỉnh sửa code khi đã test xong

- Giúp bạn tinh chỉnh khả năng app của mình, kể cả bạn không có source code.

- Cho phép bạn thích ứng app của mình với những sự thay đổi về resource(Ví dụ thay đổi về Database) mà không cần phải recompile và test bất kì

- Dễ dàng hơn trong việc duy trì bảo mật security roles

- Cho phép những người non-programer có thể chỉnh sửa và deploy web app thông qua config ở DD.

Câu truyện: Bob build 1 website mai mối.

Ngày nay thì việc hẹn hò khá là khó khăn. Thời gian rảnh thì lại vào những khoảng thời gian khác nhau. Vì thế Bob xây dựng 1 cái gì đó để kết nối họ lại với nhau.

Và anh ấy bắt đầu xây dựng: Anh ấy cân nhắc là có lẽ chỉ nên có 1 servlet duy nhất. Nhưng rồi anh ấy lại quyết định sẽ chia ra các servlet, mỗi servlet chỉ nên có 1 chức năng như đăng nhập, đăng ký, search..etc

Mỗi servlet sẽ có tất cả các business logic mà nó cần để chỉnh sửa hoặc read từ database, và ghi html tới response stream để phản hồi lại khách hàng.

Túc là mỗi servlet sẽ có business logic đặt trong đó để thực hiện các công việc, thực hiên xong sẽ ghi ra html để back lại client.

Theo hình trên có thể thấy servlet làm bất kì cái gì mà nó cần để xử lý request (like insert or search từ database ) và nó trả về html page in the response obj. Tất cả các business logic và client html page response đều nằm trong servlet code.

Nhưng điều này trông thật ngu xuẩn. Vì vậy anh ấy đã thêm vào jsps.

Những câu lệnh out.println(...); thật sự rất rất ngủ xuẩn. Sẽ như thế nào nếu phải dùng css vào trong nó :D. Vì thế anh ấy đã tức tốc nghiên cứu về jsp và đã quyết định là mỗi servlet sẽ làm tất cả các business logic mà nó cần phải làm(query tới database, insert...) sau đó forward tới 1 trang jsp to do the html cho response. Điều này cũng sẽ chia ra thành 2 tầng : business logic và presentation... Và a ấy lại học tức tốc về design rồi biết rằng phân biết sự quan tâm(các tầng) sẽ là 1 thứ tuyệt vời.

Jsp dược thiết kế tốt hơn nhiều. Giờ đây code servlet thật rõ ràng... Mỗi servlet chạy business logic của riêng nó và sau đó gọi 1 trang jsp cụ thể để xử lý html for response. Chia tầng business logic và presentation.

Client điền vào form query và click vào button "Do it". Ngay lập tức có 1 http post request gửi đến server. Web server gọi servlet , servlet chạy query trên database , sau đó request được forwarded tới 1 trang jsp xác định. The jsp build vào trong html response và send it back.

Nghe thật tuyệt vời phải không nhỉ. Kim, bạn của Bob đến và nói "Bạn của tôi, cậu vẫn đang sử dụng MVC, đúng không?"

Kim thắc mắc là liệu có thể sử dụng dịch vụ này bằng cách truy nhập thông qua swing gui app. Bob nói: Tôi chưa nghĩ về điều đó.

Kim: Tốt mà, nhưng tôi chắc là bạn vẫn sử dụng mvc chứ nhỉ. Để chúng ta chỉ có thể dùng swing GUI client để truy nhập đến các class business logic.

Bob: Nuốt nước miếng :D

Kim: Đừng nói là cậu không sử dụng MVC nhé? Mỉm cười J

Bob: a..ừm.. Tôi đã chia ra tầng presentation từ tầng business logic.

Kim: Đó là khởi đầu,. Nhưng để tôi đoán nhé, bạn vứt tất cả các xử lý của mình vào bên trong servlet hả?

Bob: Chả nói gì cả, muốn chui hố.

Nhưng bob đã nhận ra làm điều đó là đúng, và Bob đã hỏi Kim nhưng điều tổng quan về MVC.

Với MVC tầng business logic không chỉ được tách thành tầng presentation... Nó dường như không biết đến khái niệm is a presentation

Bản chất của mvc là bạn tách tầng business logic từ tầng presentation. Nhưng đưa cái gì vào giữa chúng để business logic có thể chứa các đoạn code reuse. Và phải không biết gì về view. Tầng business logic không được nằm ở servlet ... Nói theo cách khác, không được trộn business logic vào trong servlet , vì điều này sẽ làm cho phần business logic không thể reuse từ những view khác(Ví dụ swing gui app)

1. MVC design pattern

MVC : Model – View – Controller design pattern sẽ sửa điều này. Nếu Bob mà hiểu về MVC thì anh ấy đã hiểu là không thể đề phần xử lý business logic nằm trong servlet.

Chương 2:

1. Mini MVC tutorial

- Tạo và deploy 1 MVC web app. Đây là thời điểm để đặt bàn tay bẩn thỉu của bạn vào viết 1 html form, servlet controller, a model(plain old java class), 1 xml deployment descriptor và 1 jsp view rồi sau đó build nó, deploy nó và test nó.

Web application deployment

1. Xây dựng file và cấu trúc thư mục của 1 web app, có thể chứa (a) static content, jsp pages, servlet class, deployment descriptor, tag libraries, jar files, và java class file. Mô tả cách làm như thế nào để bảo vệ resource file từ http access.

2. Mô tả mục đích và ý nghĩa của mối thành phần trong deployment descriptor: error-page, init-param, mime-mapping, servlet, servlet-class, servlet-mapping, servlet-name, and welcome-file.

3. Xây dựng cấu trúc đúng với mỗi thành phần mô tả trong deployment descriptor: error-page, init-param, mime-mapping, servlet, servlet-class, servlet-mapping, servlet-name, and welcome-file.

Tất cả các object trong chương này đều được cover ở chương deployment. Vì thế đây chỉ là cái nhìn đầu tiên. Chương này chỉ là 1 phần nhỏ. Tuy nhiên cũng ko nên được bỏ qua vì nó liên quan đến nhau.

2. Chúng ta sẽ build 1 web app thực tế(Nhỏ)

Chúng ta đã thấy được vai trò của 1 container, chúng ta đã biết 1 chút về deployment descriptor và 1 chút về Model 2 MVC architecture. Nhưng bạn không thể chỉ ngồi đây và đọc cả ngày – Giờ là lúc để thực sự làm gì đó.

Chúng ta sẽ làm theo 4 bước sau đây:

1. Xem user's view(Cái mà trình duyệt sẽ hiển thị) và kiến trúc bậc cao(high level architecture)

2. Tạo môi trường deployment mà chúng ta sẽ sử dụng cho project này(Và có thể sử dụng ở với ví dụ khác trong cuốn sách)

3. Lặp đi lặp lại quá trình test về development.

User views:

User's view của web app – Cố vấn beer. Web app của chúng ta là beer advisor. User sẽ có thể lướt app, trả lời câu hỏi và phản hồi sự hiệu quả của beer advice.

Trang trên sẽ được viết bằng html, và sẽ khởi tạo 1 http post request để gửi màu của user đã chọn tới server(Giống như 1 tham số)

Trang dưới sẽ là 1 trang JSP đưa ra nhưng lời khuyên dựa trên lựa chọn của người dùng.

Và sau đây là architecture.

Mặc dù là 1 tiny app, nhưng chúng ta sẽ build nó sử dụng kiến trúc MVC đơn giản. Theo cách này thì dù sau này nó có trở thành 1 hottest site trên web, thì nó cũng sẽ dễ dàng extend.

1. User request a static page

B1: Client tạo ra 1 request với trang form.html

B2: Container lấy ra trang form.html

B3: Container return page tới browser, ở đó user trả lời câu hỏi trên form và...

2. User request a dynamic page base on user's answer.

B4: Browser gửi request data tới container

B5: Container gọi ra correct servlet dựa trên url, và passes request và response object tới servlet.

B6: Servlet gọi ra BeerExpert

B7: Expert class trả về câu trả lời, và servlet add answer tới request obj.

B8: Servlet forward request tới jsp

B9: Jsp get the answer từ request object

B10: JSP khởi tạo page html cho Container

B11: Container trả page về cho user(Đang phán đoán là trang jsp khởi tạo rồi Container sẽ biên dịch nó sang servlet, và servlet này có phần out.println() để gửi cho client)

Creating your developtment environment.

Có rất nhiều cách để bạn có thể tổ chức cấu trúc thư mục deployment, nhưng cái sau đây chúng tôi sẽ xây dựng giống như nó là 1 project nhỏ hoặc vừa. Khi đến thời điểm deploy app, chúng ta sẽ copy 1 phần của project vào 1 thư mục của 1 container cụ thể(web container). Ở đây là Tomcat

Deploying 1 web app phụ thuộc vào Container cụ thể và thông số Servlet và Jsp. Nếu bạn không deploying tới Tomcat, bạn sẽ cần cấu hình chính xác web app của mình có liên hệ với Container như thế nào. Trong ví dụ của chúng ta, mọi thứ sẽ đều được đặt dưới "Beer-v1" directory bất kể Container của bạn.

Building the app

Lộ trình for building the app

Khi chúng ta bắt đầu chương này chúng ta đã phác thảo 4 bước cho việc phát triển web-app của mình. Đó là:

1- Reviewd user views cho web app của chúng ta

2- Nhìn vào kiến trúc tổng thể

3- Thiết lập môi trường developtment and deployment cho việc tạo và deploying app

Và giờ là bước 4, creating the app

Chúng ta sẽ mượn 1 số phương pháp phổ biến để áp dụng cho công việc của mình.

Sẽ có 5 bước mà chúng ta cần theo sau đây(5 bước này nằm trong bước 4 ở trên)

4a- Build và test cái form mà người dùng sẽ request lần đầu

4b- Build và test version 1 của controller servlet với html form đã tạo ở trên. Version này sẽ được gọi thông qua HTML form và in ra parameters mà nó nhận được.

4c- Build 1 lớp test để test cho expert/model class, sau đó build và test expert/ model class dựa trên lớp vừa tạo

4d- Upgrade servlet to version 2: Version này sẽ add vào khả năng gọi model class để nhận lời khuyên từ beer advisor.

4e- Build the JSP, upgrade servlet lên version 3(Nó sẽ cho phép thêm khả năng dispatching tới JSP) và test app

4a- HTML cho trang khởi tạo, html này rất đơn giản, nó chỉ có 1 title, 1 drop-down list mà người dùng có thể chọn màu beer, và submit button.

Action = "selectBeer.do" Đây là cái mà HTML nghĩ servlet được gọi. Không có bất cứ cái gì trong cấu trúc thư mục của bạn có tên là "selectBeer.do", đó chỉ là logical name.

Q: Tại sao form lại summitting to "selectBeer.do" khi không có thằng servlet nào với tên như này. Trong cấu trúc thư mục mà chúng ta đã thấy ở trước đo, tớ không nhìn thấy thằng nào có tên là "selectBeer.do" mà nhỉ. Và cái đuôi mở rộng ".do" để làm cái gì?

A: selectBeer.do là tên logical, không phải là tên thực sự của file. Nó đơn giản chỉ là tên mà chúng ta muốn client sử dụng. Trong thực tế client sẽ không bao giờ có quyền truy nhập trực tiếp vào servlet class file. Vì thế ban không nên tạo 1 html page với link hoặc action có đường dẫn trực tiếp tới 1 servlet class file. Thủ thuật ở đây là chúng ta sẽ sử dụng XML Deployment Descriptor(web.xml) để map cái request của client với actual servlet class file. Giờ đây bạn chỉ cần nghĩ ".do" là 1 phần của logical name(Ko phải là 1 file type). Phần sau của cuốn sách, bạn sẽ học về cách sử dụng extension đó trong servlet mapping.

Deploying and testing page đang mở. Để test nó, bạn cần phải deploy nó vào trong thư mục Container(Tomcat), start Tomcat và mang trang đó đến trình duyệt.

1. Tạo 1 html trong development environment.

Tạo ra 1 file html gọi là form.html, lưu nó vào bên dưới thư mục /beerV1/web.

2. Copy file vào html vào trong tomcat/webapps/Beer-v1/. Chú ý là thư mục home của tomcat có thể khác nhau.

3. Tạo ra dd trong development environment. Tạo ra file xml, đặt tên là web.xml và lưu nó trong môi trường phát triển dưới thư mục beerV1/etc/

3. Servlet mapping

Mapping logical name tới 1 file servlet class

1. Diane điền vào form và ấn submit. Browser sẽ khởi tạo ra 1 request URL:

Trong html, "beer-v1/" không phải là 1 phần của đường dẫn được tạo trong html. Trong html chúng ta chỉ tạo

<form>

Nhưng trình duyệt prepends "/Beer-v1" vào trước cái request , bởi vì "Beer-v1" là nơi mà request của client sẽ được gửi đến. Nói theo cách khác, "SelectBeer.do" trong HTML có liên quan đến URL của page mà nó nằm trên đó. Tức cái đường dẫn của mình nằm trên 1 file, file đó lại nằm trên 1 cái context-root rồi. Nên nó sẽ được prepend vào.

2. Web Container sẽ tìm kiếm trong DD và ra thành phần <servlet-mapping> có <url-pattern> matches với /SelectBeer.do , ở đây thì "/" đại diện cho context root của web-app, và SelectBeer.do là tên logical name được đặt trong resource(html, jsp)

3. The Container nhìn thấy <servlet-name> với <url-pattern> tương ứng. <servlet-name> tương ứng là "Ch3 Beer". Nhưng đây, nó ý không phải là tên của 1 servlet class file nào cả. "Ch3 Beer" chỉ là tên của servlet, mình đặt để map thôi, chứ ko phải là 1 servlet class.

Đối với 1 thằng Container thì servlet là bất kì cái gì được đặt tên trong DEPLOYMENT DESCRIPTOR dưới tag <servlet>. Tên của servlet đơn giản chỉ là tên sử dụng trong deployment descriptor vì thế những phần khác trong deployment descriptor có thể map với nó.

4. Container nhìn vào trong các tag <servlet> xem có thằng nào có thành phần <servlet-name> là "Ch3 Beer" hay không

5. Nếu thấy Container sẽ sử dụng <servlet-class> trong <servlet> tag để biết thằng lớp servlet class nào sẽ chịu trách nhiệm cho việc sử lý request này. Nếu servlet chưa được khởi tạo, class sẽ được load và servlet sẽ được khởi tạo.

6. Container bắt đầu 1 thread mới để xử lý request, và truyền request to thread(to the servlet' service() method)

7. Container gửi response (Thông qua web server, dĩ nhiên) trở lại cho client

II. Servlet controller version one

Phiên bản đầu tiên của controller servlet. Kế hoạch của chúng ta là build servlet theo từng giao đoạn. Testing các kết nối. Và hay nhớ là servlet nhận param từ request, gọi 1 phương thức trên model, lưu thông tin vào 1 nới nào đó để JSP có thể tìm. Và forward request tới JSP. Nhưng ở phiên bản đầu tiền này, mục đích của chúng ta chỉ là đảm bảo HTML page có thể gọi chính xác servlet và servlet đã nhận đúng parameter.

- Httpservlet extends GeneriContainerervlet, GeneriContainerervlet implements Servlet interface...

- Chúng ta sẽ sử dụng doPost() ở đây để xử lý http request, bới vì HTML form say: method = Post

I. Compiling, deploying, và testing container servlet

Chúng ta đã build, deployed, và test HTML, deployment descriptor,. Để chỉnh sửa trong web.xml được nhận thì cần restart Tomcat. Bây giờ là thời điểm để compile first version của servlet, deploy nó, và test nó thông qua html form. Giờ đây chúng ta sẽ restart Tomcat để đảm bảo web.xml và servlet class được sees.

II. Model class

Building và testing model class. Trong mô hình MVC, thì model là back-end của app.

Mô tả cho phần model:

- Đặt trong package com.example.model

- Cấu trúc thư mục của nó lên là /WEB-INF/classes/com/example/model.

- Nó chỉ có 1 phương thức, getBrands(), nhận vào beer color(String) và trả ra 1 arrayList các nhãn hàng beer.(String)

Xây dựng lớp test cho model

Tạo ra lớp test cho model, model chỉ là 1 lớp java như bất kì lớp khác. Vì vậy bạn có thể test nó mà không cần Tomcat.

Model có thể rất phức tạp. Thường là có liên quan đến các business logic phức tạp và các kết nối tới database.

Bây giờ servlet sẽ gọi model để lấy lời khuyên thực sự

Trong version 2 của servlet , chung ta sẽ gọi model từ doPost() method để lấy lời khuyên(Version 3 thì sẽ chuyển lời khuyên -> jsp)

Servlet nâng cao, version 2.

Hãy quên servlet đi. Chỉ nghĩ về java.

1. Xây dựng việc gọi model từ doPost() method

2. Compile servlet

3. Deploy và test updated web app.

Code của servlet version 2

Hãy nhớ là: model chỉ là plain old java, vì thế chúng ta gọi nó giống như gọi bất kì cái nào khác.

Các bước quan trọng với servlet version 2

Chúng ta có 2 thứ phải làm: recompile servlet và deploy web-app

Review lại các phần đã làm được. MVC beer advice web application

1. Trình duyệt gửi 1 request data tới container

2. Container tìm ra correct servlet dựa trên URL, truyền request tới servlet

3. Servlet gọi BeerExpert để nhờ sự giúp đỡ

4. Servlet output ra response. Ở đây là prints ra lời khuyên

5. Container trả về page cho người dùng.

Nhưng đây mới là cái mà chúng ta muốn.

1. Trình duyệt gửi 1 request data tới container

2. Container tìm ra correct servlet dựa trên URL, truyền request tới servlet

3. Servlet gọi BeerExpert để nhờ sự giúp đỡ

4. Expert class trả về 1 answer, câu trả lời này sẽ được servlet adds vào request obj

5. Servlet forward request to JSP

6. JSP nhận câu trả lời từ request obj.

7. JSP khởi tạo page cho container

8. Container return page cho người dùng

I. Create JSP "View" để đưa ra lời khuyên

Chúng ta sẽ hiểu về jsp ở các chương sau đó. Thực sự thì đoạn mã jsp sau đây không phải là cái tốt nhất(Bởi vì mã scriptlet, chúng ta sẽ thảo luận ở chương sau). Bây giờ thì nó viết như thế cho dễ đọc., và nếu bạn muốn thì nghiệm gì đó, hãy làm đi, mặc dù không thể test JSP ngay bây giờ(Phải đợi khi có servlet version 3)

Những mã java nằm trong <% %> tag thương được gọi là "scriptlet code"

Deploying JSP

Chúng ta không compile JSP, Container sẽ làm điều đó trong lần request đầu tiên. Nhưng chúng ta sẽ phải làm

1. Đặt tên nó là "result.jsp"

2. Lưu nó ở môi trường development , in : /web/

3. Move 1 bản cop của nó vào môi trường deployment trong /beer-v1/

I. Ditpatching 1 request tới JSP

Servlet nâng cao gọi JSP(Verison 3)

Trong phần này, chúng ta sẽ modify servlet để gọi "JSP"(JSP sẽ thực hiện output(view)). Container cung cấp 1 cái cơ chế gọi là "request ditpatching" cho phép 1 thành phần được Container quản lí gọi tới 1 cái khác. Và đó là cái mà chúng ta sẽ sử dụng. Servlet sẽ get thông tin từ model, lưu nó vào trong request object và ditpatch request tới jsp.

Điểm thay đổi quan trong chúng ta phải làm với servlet.

1- Add câu trả lời của model tới request obj, vì thế jsp có thể truy nhập nó.

2- Yêu cầu container phải forward request tới "result.jsp"

Code cho servlet version 3:

1. Compile, deploy và test

Thực hiện test hoàn chỉnh cho web app này.

Ok, giờ đây anh ấy có thể làm 1 app MVC, nhưng anh ấy vẫn không có đầu mồi nào để sử dụng JSP expression language, hoặc JSTL, hoặc viết custom tag, hoặc sử dụng 1 filter, và vẫn còn rất nhiều thứ để học.

Vẫn còn nhiều thứ phải học. Bữa tiệc vẫn còn nữa. Bạn mới trải qua 3 chapter. Viết 1 chút code, tổng quan về HTTP request/response. Vẫn còn khoảng 200 mock exam chờ bạn trong quyển sách này. Và chúng sẽ được bắt đầu từ chương sau.Vì thế hãy làm xong ví dụ này rồi mới next tới chapter sau.

Ngày hôm nay. Review lại J

Chương 4: Request và response

Servlet sống để phục vụ client. Công việc của Servlet là nhận 1 Request của client và send back a response . Những yêu cầu thì có thể rất đơn giản kiểu như "Đưa cho bố mày trang welcome page" hoặc cũng có thể phức tạp như "Hoàn thành check-out giỏ hàng cho bố". Các Request mang theo các dữ liệu quan trọng và Servlet của bạn thì cần phải biết là làm thế nào để tìm nó và làm thế nào để sử dụng nó. Response mang theo thông tin mà trình duyệt cần để render 1 trang, và Servlet của bạn cần phải send nó như thế nào. Hoặc nếu không Servlet của bạn cần phải quyết định xem sẽ truyền Request tới cái gì(trang khác, Servlet , jsp)

Q&A:

- Thật là khó hiểu, ở bức tranh trên bạn đã chỉ ra 2 Client khác nhau, mỗi cái sẽ có 1 thread riêng. Vậy điều gì sẽ xảy ra nếu cùng 1 Client tạo ra nhiều Request ? -> 1 thread mỗi Request , Container không quan tâm ai tạo ra Request , mỗi Request đến sẽ được tạo 1 thread/stack mới.

- Hãy nhớ mỗi JVM chỉ có 1 thể hiện của Servlet .

- Tớ đã nhận ra rằng HttpServlet nằm khác gói với GenericServlet ...Vậy có bao nhiêu Servlet package ở đó -> Mọi thứ liên quan tới Servlet (ngoại trừ jsp), đều nằm trong javax.servlet hoặc javax.servlet.http. Và thật dễ dàng để nói ra sự khác nhau. Những thứ làm việc với http thì nằm trong javax.servlet.http và phần còn lại(các lớp generic Servlet và interface) thì đều nằm trong javax.Servlet .

Flex your mind.

Init() luôn luôn hoàn thành trước lần gọi service() đầu tiên.

Tại sao lại cần 1 phương thức init(), nói theo cách khác, chả nhẽ constructor chưa đủ cho việc khởi tạo 1 Servlet hay sao?

Code gì nằm trong phương thức init()?

Dấu hiệu: phương thức init() nhận vào 1 đối số object Reference . Bạn nghĩ object Reference này là gì, Và làm như thế nào để có thể "see" nó?

Mục đích của chương:

1.1 Với mỗi phương thức của http(get, post, head,..) mô tả về mục đích của mỗi phương thức và đặc điểm kĩ thuật của giao thức của phương thức http method (the technical characteristic of the http method protocol). Liệt kệ ra các trigger được tác động với 1 client(web browser). Xác định được httpServlet nào tương ứng với http method

1.2 Sử dụng httpServletRequest interface, viết code để lấy param của html form từ request, lấy thông tin http request header hoặc lấy cookies từ request.

1.3 Sử dụng httpServletResponse interface, viết code để set 1 http response hearder, set content type của response, để thu được 1 text stream cho response, thu được 1 binary stream cho response, redirect 1 http request tới url khác hoặc thêm cookies tới response(Phần cookies sẽ được nói nhiều hơn ở chương session)

1.4 Mô tả về tập các mục đích và hành động trong vòng đời của 1 servlet: (1) servlet class loading, (2) servlet instantiation,(3) gọi phương thức init, (4) gọi service method và (5) gọi phương thức destroy().

Tất cả các mục đích sẽ được cover hết trong chương này, chỉ có cookies là sẽ để sau. Phần lớn nội dung của chương này đã được nói đến ở chương 2, nhưng chương 2 chúng ta đã nói "Đừng lo lắng nhiều về việc phải nhớ nó"

Trong chương này, bạn nên ngồi xuống, thật sự học và nhớ nội dung của chương. Sẽ không còn chap nào cover lại các mục đích này.

Làm bài tập trong chương này, review material, sau đó làm mock exam ở cuối chương. Nếu bạn không đúng được 80% trở lên. Quay trở lại chapter này và học lại những gì bạn quên trước khi bắt đầu chương 5.

Một vài câu hỏi trong mock exam chương này sẽ được trả lời ở chương 5, 6 vì nó yêu cầu phải có kiến thức của những chương đó.

I. Servlet được điều khiển bởi container.

Trong chương 2 chúng ta đã thấy được vai trò tổng thể của Container trong vòng đời của servlet – Nó tạo ra các đối tượng request và response . Tạo hoặc cấp phát 1 thread mới cho servlet, gọi phương thức service() của servlet, truyền request và response tới servlet(như là đối số). Tổng thể

(1) User click và 1 link có chưa URL tới 1 servlet

(2) Container "nhìn" thấy request đến yêu cầu 1 servlet, vì thế container tạo ra 2 object

HttpServletResponse

HttpServletRequest

(3) Container tìm ra servlet dựa trên URL trong request, tạo hoặc cấp phát 1 thread cho request và gọi phương thức service() của servlet, truyền vào 2 đối số là request và response(reference object). 2 đối số này nó tạo ra

(4) Phương thức service() hiểu ra phương thức servlet nào được gọi dựa trên http method(get, post...) được gửi bới client.

(5) Servlet sử dụng response object để viết ra phản hồi với client . Phản hồi trở ngược lại Container.

(6) Phương thức service() hoàn thành, vì thế thread hoặc là chết hoặc là trở về Container-managed thread pool. 2 object reference là request và response ra khỏi phạm vi sử dụng và sẵn sàng bị thu dọn bởi bộ thu dọn rác(garbage collection). Khách hàng nhận response.

II. Nhưng có nhiều thứ liên quan tới servlet's life

Chúng ta đã nhẩy thẳng vào phần giữa của vòng đời servlet. Nhưng chúng ta vẫn chưa trả lời được câu hỏi. Khi nào thì constructor của servlet chạy. Đối tượng servlet object sẽ "live" bao nhiêu lâu?. Khi nào servlet khởi tạo resource? Khi nó được clean up resource.

Vòng đời của servlet rất đơn giản. Chỉ có 1 trạng thái chinh – đã khởi tạo(initialized.) . Nếu servlet chưa được khởi tạo, thì hoặc là nó đang được khởi tạo (đang chạy constructor hoặc phương thức init()), hoặc là nó đang được destroyed(đang chạy phương thức destroy() của nó), hoặc đơn giản là nó không tồn tại.

- Constructor không tham số của servlet class chạy(Bạn không nên viết 1 constructor, chỉ sử dụng Constructor mặc định được cung cấp bởi compiler)

- init() chỉ được gọi 1 lần trong cuộc đời của servlet, và nó phải được hoàn thành trước khi Container gọi phương thức service().

- Service(): Nơi mà servlet giành phần lớn thời gian của mình để xử lý request của client. Mỗi request chạy trong 1 thread khác nhau.

- Destroy() Container gọi để cho servlet 1 cơ hội để clean up trước khi servlet bị kill bởi garbage collection. Giống với init() nó chỉ được gọi 1 lần.

III. Servlet API

Đừng cố để nhớ tất cả các cái sau đây, chỉ nên cảm nhận là API này làm việc như thế nào.

- Servlet interface: Servlet interface nói rằng tất cả các servlet đều có 5 phương thức(Trong đó có 3 phương thức là phương thức vòng đời của nó)

- GenericServlet class: là 1 abstract class implents phần lớn các phương thức cơ bản của servlet mà bạn cần. Bao gồm cả những cái trong servlet interface. Có thể bạn sẽ chả bao giờ extends từ class này. Phần lớn "servlet behavior" của servlet sẽ đến từ lớp này.

- HttpServlet(Cũng là 1 lớp abstract): implements phương tức service() để đối chiếu Http method và servlet method

- MyServlet class: Phần lớn servlet được xử lý bới super class. Tất cả những gì bạn cần làm là override http methods mà bạn cần.

I. 3 vòng đời lớn

(1) Init(): Khi nào được gọi: Container gọi init() trên thể hiện của servlet sau khi thể hiện của nó được tạo. Và trước khi servlet có thể đáp ứng bất kì request nào của client.

Ý nghĩa: Cho pép bạn khởi tạo servlet trước khi xử lý bất kì yêu cầu nào.

Có thể override nó?Có thể Nếu bạn muốn get 1 kết nối tới database,...bạn sẽ override phương thức init() trong servlet class của mình.

(2) Service(): Khi nào được gọi: Khi request của khách hàng đến, Container bắt đầu 1 thread mới hoặc cấp phát 1 thread từ pool và phương thức service() của servlet được gọi.

Ý nghĩa: phương thức này "look at" vào trong request , xác định HTTP method nào(Get hay Post) và gọi phương thức doGet() hay doPost() tương ứng trên servlet.

Có thể override nó? Không, rất khó xảy ra. Bạn không nên override phương thức service(). Công việc của bạn là override doGet() hoặc/và doPost() và để cho phương thức service() (được implement từ HttpServlet class ) làm việc gọi ra phương thức nào

(3) doGet() và/hoặc doPost(): Khi nào được gọi: phương thức service() gọi doGet() hoặc doPost() dựa trên phương thức HTTP(Get, post) từ request.

Ý nghĩa: Đây là nơi bạn viết code. Xử lý yêu cầu của khách hàng. Bạn có thể gọi các phương thức khác của những đối tượng khác. Dĩ nhiên, nhưng mọi thứ đều bắt đầu từ đây.

Có thể override nó? Luôn luôn có ít nhất 1 trong số chúng (doGet() hoặc doPost()). Bất cứ cái nào mà bạn override cũng sẽ nói cho Container biết bạn đang support. Nếu bạn không override doPost(), thì bạn cũng nói với Container rằng servlet của mình ko support HTTP post request.(Thực ra là có , nhưng vì ko override nên nó chả hiện ra cái gì cả, code trống)

II. Servlet thread

Tôi nghĩ là tôi hiểu rồi,.. Container gọi phương thức init() của servlet(Nếu tôi không override thì init() sẽ run từ GenericServlet). Tiếp theo khi 1 request đến, Container bắt đầu hoặc cấp phát 1 thread và gọi phương thức service()(Phương thức này tôi ko bao giờ được override), vì thế service() được kế thừa từ HttpServlet chạy. Tiếp theo HttpServlet service() gọi phương thức doGet() hoặc doPost() mà tôi đã override. Mỗi thời điểm dopost() hoặc doget() chạy thì nó sẽ thuộc 1 thread riêng biệt.

Mỗi phương thức service thuộc 1 stack riêng của mình.

(1) Servlet initialization: init(): Container gọi init() trên thể hiện của servlet sau khi thể hiện của servlet được tạo, nhưng trước khi servlet có thể phục vụ bất kì request nào của client.

Nếu bạn không có code init, kiểu lấy 1 database connection. Thì phương thức init() được kế thừa từ GenericServlet sẽ được chạy.

(2) Thread B: Đầu tiên khi yêu cầu của client đến. Container bắt đầu 1 thread và gọi phương thức service(). Service() sẽ tìm ra phương thức nào trong request và gọi ra phương thức doget() hoặc dopost() tương ứng. doget() hoặc dopost() trong httpServlet không làm gì cả, vì thế bạn cần override 1 hoặc 2. Khi service() hoàn thành thì thread cũng sẽ chết.

(3) Thread C: Khi request thứ 2(3,4,...)của client đến. Container tạo lại hoặc tìm 1 thread khác và gọi serivce(). Serivce() lại gọi doget() hoặc dopost(). Có thể sẽ có nhiều thread chạy tương ứng với số request. Nó có thể dược giới hạn bởi resource hoặc policies/configuration của Container. Container cho phép bạn chỉ ra số tối đa thread cùng 1 lúc, Khi số request của client vượt quá số đó, sẽ có 1 vài client phải chờ.

I. Mỗi request chạy trong 1 thread riêng biệt.

Bạn có thể nghe ai đó nói điều gì đó giống như "Mỗi instance của servlet ..." nhưng điều này là sai. Chỉ có 1 thể hiện của servlet mà thôi(instance), chỉ có 1 trường hợp đặc biệt có nhiều instance đó là khi gọi SingleThreadModel. Và chúng ta sẽ không nói đến trường hợp này.

Container chạy nhiều thread dể xử lý nhiều yêu cầu với chỉ 1 servlet duy nhất.

Và với mỗi request của client sẽ tạo ra 1 cặp request và response object.

Mỗi client request nhận 1 thread riêng. Và Container sẽ cấp phát 2 đối tượng request và response mới.

Q: Điều này thật khó hiểu...Trong bức tranh trên bạn chỉ ra 2 client khác nhau, mỗi cái sẽ có 1 thread riêng. Vậy điều gì sẽ xảy ra nếu 1 client tạo ra 2 request. Vẫn là 1 thread/1 request chứ?

A: 1 thread mỗi request . Container không quan tâm ai tạo ra request. Mỗi request đến có nghĩa là 1 thread/stack mới.

1. Khởi tạo servlet

Vào lúc bắt đầu: loading và khởi tạo

Servlet bắt đầu cuộc sống khi Container tìm ra servlet class file. Điều này luôn luôn xảy ra khi Container bắt đầu(Ví dụ, khi bạn chạy Tomcat). Nghĩa là, khi container bắt đầu, start. Nó sẽ tìm kiếm deployed web apps và bắt đầu tìm kiếm servlet class file(trong chương Deployment, chúng ta sẽ đi sâu vào chi tiết và như thế nào, tại sao và ở đâu mà Container tìm kiếm servlet )

Servlet của bạn luôn được loaded và khởi tạo trước khi nó có thể phục vụ request của client lần đầu.

Init() luôn được complete trước lần gọi service() đầu tiên..

Tại sao lại cần phương thức init()? Nói theo cách khác, tạo sao constructor vẫn chưa đủ cho việc khởi tạo 1 servlet ?

Bạn sẽ vứt vào trong phương thức init() đonạ code như thế nào?

Gợi ý: phương thức init()nhận 1 đối số là 1 object reference. Vậy bạn nghĩ đối số này là cái gì và tại sao chúng ta lại sử dụng nó?

I. Servlet initialization: Khi 1 đối tượng trở thành 1 servlet .

Phương thức init chỉ chạy 1 lần trong servlet'life, vì thế đừng blow ip. Và đừng cố gắng làm gì quá sớm... Constructor là quá sớm để làm những thứ cụ thể của servlet.

Điều tuyệt vời nhất trong cuộc sống của tôi là khi Container biến tôi là 1 servlet , bằng cách tạo ra 1 ServletConfig cho tôi, và gọi phương thức init() của tôi. Trước đó tổi chỉ là 1 đối tượng bình thường. Nhưng khi là 1 servlet, t đã có những đặc quyền riêng(giống như việc bắt tay ngầm). Những đặc quyền như log events, nhận tham chiếu từ những resource khác, và lưu trữ nhưng thuộc tính cho các servlet khác.

1 servlet sẽ thay đổi từ không tồn tại đến đã khởi tạo(initizalized)(điều này có ngĩa là nó sẵn sàng để phục vụ các request của client ). Container tạo ra chỉ là 1 object chứ khổng phải là 1 servlet , .Để trở thành servlet, object cần phải được cấp phát những khả năng tối thiểu.

Khi 1 đối tượng trở thành 1 servlet, nó sẽ nhận tất cả các đặc quyền riêng biệt, ví dụ như khả năng sử dụng tham chiếu servletContext của nó để nhận thông tin từ Container

Tại sao chúng ta lại quan tâm về chi tiết khởi tạo?

Bởi vì đâu đó giữa contructor và phương thức init(), servlet là trong 1 Schroedinger's* servlet state . Bạn có thể có code servlet initialization, giống như get thông tin cấu hình web app, hoặc tìm kiếm 1 tham chiếu tới phần khác của app. Điều này sẽ fail nếu nếu bạn chạy qua sớm trong servlet'life. Nó khá là đơn giản, bạn chỉ cần nhớ là không đưa bất kì cái gì vào trong servlet constructor.

Không có gì không thể đợi đến khi init ().

II. ServletConfig và ServletContext.

Là 1 servlet sẽ cho bạn cái gì?

Điều gì sẽ xảy ra khi 1 servlet đi từ đây tới đây: object -> servlet

1. Đối tượng ServletConfig

- Mỗi servlet 1 đối tượng ServletConfig

- Sử dụng nó để truyền thông tin deploy-time tới servlet(1 database hoặc bean gì đó, ví dụ bạn không muốn 1 đoạn code bắt buộc(hard-code) nào đó ở trong servlet của mình)

- Sử dụng nó để truy cập ServletContext.

- Tham số được cấu hình ở trong deployment descriptor.

2. ServletContext

- Mỗi web app có 1 ServletContext(Chúng nên được đặt tên là AppContext)

- Sử dụng nó để truy nhập vào tham số của web app(cũng được cấu hình trong deployment descriptor )

- Sử dụng nó giống như 1 bảng thông tin ứng dụng, Nơi bạn có thể đưa 1 msg gì đó lên(Được gọi là attributes), và các phần khác của app có thể access(Cách truy nhập nằm ở chương kế)

- Sử dụng nó để get servlet info, bao gồm cả tên và version của container , và version của API, được supported.

Nhưng công việc thực sự của servlet là xử lý request. Đó là khi mà cuộc sống của servlet có ý nghĩa.

Ở chương kế chúng ta sẽ nhìn ServletConfig và ServletContext, nhưng bây giờ, chúng ta sẽ đào sâu vào chi tiết của request và response. Bởi vì servletconfig và servletcontext tồn tại chỉ để hỗ trợ 1 nhiệm vụ duy nhất. Xử lý clients request. Vì thế trước khi chúng ta tìm hiểu về đối tượng context và config support công việc của chúng ta ntn thì chúng ta hãy quay lại 1 chút và nhìn vào những nguyên tắc cơ bản của request và response.

Bạn đã biết được rằng bạn xử lý request và response như là nhưng đối số của phương thức doGet() và doPost(), nhưng sức mạnh thực sự mà request và response cung cấp cho bạn là gì? Và bạn có thể làm gì với chúng và tại sao bạn lại phải quan tâm tơi nó.

Bài tập: Điền vào chỗ trống

I. Request và response

Request và response : Chìa khóa đến mọi thứ, và đối số tới service().

Các phương thức HttpServletRequest là mọi thứ về HTTP giống như cookies, headers và session. HttpServletRequest thêm vào các phương thức liên quan tới giao thức http, đó là những cái mà servlet của bạn sử dung để kết nối tới client brower.

Tương tự như thế: HttpServletResponse thêm vào các phương thức mà bạn quan tâm khi sử dụng HTTP: ví dụ errors, cookies, headers.

Request và response cũng là các đối số của phương thức mà bạn viết. doGet() và doPost().

I. HTTP methods

HTTP request method xác định phương thức doGet() hay doPost() sẽ chạy.

Request của client. Luôn nhớ là nó bao gồm 1 đặc tả HTTP method(GET hoặc POST...). Nếu phương thức HTTP truyền đến là GET, thì phương thức service() sẽ gọi doGet(). Nếu HTTP request method là POST, thì phương thức service() sẽ gọi doPost().

Bạn có thể nghĩ là chỉ có doPost() hoặc doGet(). Nhưng tớ biết có tới 8 phương thức trong HTTP 1.1

Bạn có thể chả cần quan tâm gì về các phương thức HTTP ngoại trừ GET và POST. Có rất nhiều các phương thức HTTP khác ngoài GET và POST. Đó có thể là HEAD, TRACE,..

II. Get và Post

Sự khác nhau giữa Get và Post

Post có 1 body. Đó là điểm chính, cả Post và GET đều có thể gửi tham số, nhưng với GET thì tham số bị giới hạn

Việc này nghe như sự khác nhau giữa POST và GET là kích thước(size) của tham số dữ liệu mà bạn gửi đi?

è Không, nó không chỉ là về size. Chúng ta đã nói về vấn đề khác khỉ sử dụng GET ở cháp 1, nhớ không?

Khi bạn sử dụng Get, tham số dữ liệu hiển thị lên thanh trình duyệt(brower input bar), bên phải của URL(và được ngăn cách bởi dấu ?). Tưởng tượng 1 kịch bản khi bạn không muốn tham số hiện lên

Đúng thế, bảo mật là 1 vấn đề khác nữa.

1 Vấn đề nữa: Bạn muốn hoặc cần end-user bookmark. GET request có thể được bookmark, còn POST request thì không thể. Điều này có vẻ quan trọng đấy: Nếu bạn có 1 page cho phép user search theo 1 tiêu chí nào đó. Nếu vài tuần sau, user quay lại cũng tìm kiếm như thế với data mới trên server của bạn. Vậy bạn có muốn họ bookmark lại hay không?

Nhưng bên cạnh size, security và bookmaking. Vẫn còn 1 điểm khác nhau quan trong nữa giữa GET và POST- cách mà chúng được sử dụng. GET có nghĩa là được sử dụng để getting thứ gì đó.Đơn giản, dễ thu hồi. Chắc chắn là, bạn có thể sử dụng tham số để có thể tìm ra cái mà bạn sẽ send back(gửi lại). Nhưng có 1 điểm đó là, bạn không tạo ra bất kì thay đổi nào trên server. POST có nghĩa là được sử dụng để gửi data sẽ được xử lý. Nó có thể là tham số truy vấn được sử dụng để tìm ra cái mà bạn sẽ gửi lại(chỉ như GET) . Nhưng khi bạn nghĩ về POST, hãy nghĩ: update . Hãy nghĩ là: sử dụng data từ POST body để thay đổi thứ gì đó trên server.

Và điều đó đã mạng lại 1 vấn đề khác. Khi nào 1 request là idempotend(không thay đổi)

1. Cậu truyện của non-idempotent request .

Diane có 1 nhu cầu. Cô ấy quyết định mua 1 quyển sách trên Wickedly Smart site. 1 sự lựa chọn mà cô ấy sẽ phải hối tiếc.

Luôn luôn suy nghĩ về sự khác nhau của GET method html và doGet() trong servlet. GET request luôn là idempotent trong HTTP spec, còn trong doGet(), không gì có thể ngăn bạn thực hiện 1 idempotent method.

1. Diane click vào button checkout-> browers gửi 1 http request tới server cùng với thông tin của quyển sách và mã số khách hàng của Diane(thông tin của end-user-customer)-> Container gửi request tới Checkout servlet để xử lý.

2. Servlet rút tiền bằng tài khoản ngân hàng của Diane.

3. Servlet update database( lấy sách ra khỏi kho hàng, tạo ra 1 đơn chuyển hàng mới)

4. Servlet không gửi 1 response và Diane vẫn nhìn thấy cùng giỏ hàng đó và nghĩ "có thể mình đã không click chuẩn, tốt nhất là mình nên click lại button checkout 1 lần nữa"

Trình duyệt gửi 1 http request tới servlet với info như lần trước.

Câu truyện tiếp tục.

5. Container gửi request tới check out servlet để xử lý

6. Servlet không có vấn đề gì với việc Diane mua lại quyển sách mà cô ấy đã mua "Minh đoán là cô ấy thực sự rất thích quyển sách này, nên cô ấy đã mua 2 lần"

7. Servlet sẽ rút tiền từ tài khoản ngân hàng của Diane lần 2.

8. Ngân hàng sẽ chập nhận rút tiền .Nhưng sẽ thu thêm phí thấu chi.

9. Cuối cùng Diane điều ứng đến trang trạng thái đơn hàng và nhìn thấy 2 lệnh đặt mua hàng 1 quyển sách "Điều này không đúng, tôi chỉ mua 1 quyển sách, thằng dev óc cho nào đã tạo ra trang web ngu xuẩn nào vậy?" Nó nên cân nhắc transaction trùng lặp chứ nhỉ.

10. Xin chào ngân hàng.....

Bài tập: Chuẩn bị bút chì của bạn.

Phương thức nào của HTTP mà bạn nghĩ là idempotent(không thay đổi giá trị)

Điều gì đã xảy ra với transaction của Diane? Đó không phải chỉ là 1 vấn đề,. Có vài vấn đề ở đây mà dev phải fix.

Có những cách gì để 1 dev có thể giảm thiểu cái rủi ro này.

Gợi ý: Nó có thể không phải là giải pháp liên quan đến lập trình.

Là idempotent là tốt. Nó có nghĩa là bạn có thể làm lại cùng một thứ mà không có tác dụng phụ nào cả.

I. Idempotent requests

Post không phải là 1 request idempotent. 1 HTTP GET chỉ là để getting things. Và giả định là nó không thay đổi bất kì cái gì trên server. Vì thế 1 GET là, theo định nghĩa của HTTP spec là 1 idempotent. Nó có thể được thực hiện nhiều lần mà không có bất kì tác dụng xấu nào.

POST thì không phải là 1 idempotent. Dữ liệu được submit từ trong thân của POST có thể đã được định sẵn cho 1 transaction không thể reversed. Chính vì thế, hãy cẩn thận với chức năng của doPost().

GET là idempotent, Post thì không. Bạn phải đảm bảo rằng web app của bạn có thể xử lý các kịch bản giống như của Diane, nơi POST có thể đến nhiều hơn 1 lần.

Lưu ý: Có nhiều cách sử dụng khác nhau của từ idempotent. Chúng ta đang sử dụng nó trong HTTP/servlet với ý nghĩa là cùng 1 request (same request ) có thể được tạo 2 lần mà không có hậu quả tiêu cực trên server. Chúng ta không sử dụng idempotent với ý nghĩa là cùng 1 yêu cầu(same request ) luôn luôn trả về cùng 1 response.

I. Phát hiện browser gửi 1 request POST hoặc GET kiểu gì?

GET

1 simple hyperlink luôn có nghĩa là GET request.

POST

Khi bạn chỉ rõ ra phương thức của là POST, thì nó mới là POST.

Khi người dùng click vào "SUMMIT tham số được gửi đi sẽ nằm trong thân của POST request,. Trong ví dụ này, chỉ có 1 tham số tên là "color", và giá trị là cái mà người dùng chọn.

Điều gì sẽ xảy ra khi bạn không nói rõ method = "POST" trong form của mình

POST không phải là phương thức mặc định.

Nếu bạn không đặt phương thức method = "POST" ở trong form của mình, thì mặc định sẽ là HTTP GET request. Điều đó có nghĩa là browser sẽ gửi các tham số trong request header. Nghĩa là append thêm nó vào trên thanh input browser bar. Nhưng điều này sẽ gây ra nhiều vấn đề cho bạn. Bới vì request đến như là GET, có nghĩa là bạn sẽ gặp 1 vấn đề lớn nếu chỉ có phương thức doPOST() mà không có phương thức doGet() trong servlet của mình.

Q: Nếu t muốn support cả GET và POST trong cùng 1 servlet thì phải làm ntn?

A: Dev muốn hỗ trợ cả GET và POST thì chỉ cần gọi doGET(request,response ) trong doPost() là được.

Ví dụ: Gửi và sử dụng 1 param duy nhất

Nhớ rằng, browser sẽ khởi tạo request , vì thế đừng bận tâm về việc tạo nó ntn. Đây có thể là nhưng gì sẽ đến với servlet

Ví dụ 2: Gửi và sử dụng 2 param.

Note: Bạn có thể có nhiều values cho 1 tham số duy nhất. Tức 1 mảng giá trị. Điều đó có nghĩa là bạn sẽ phải sử dụng getParameterValues() để trả về 1 array thay cho việc getParameter() chỉ trả về 1 String.

1 vài kiểu input trong form, như 1 tập các checkboxes có thể có nhiều hơn 1 giá trị. Điều đó có nghĩa là 1 single param có thể có nhiều hơn 1 giá trị, tùy thuộc vào việc có bao nhiều checkx item được check. Ví dụ form sau đây cho phép 1 user có thể chọn nhiều kích thước của chai bia.

I. Đối tượng HTTPServletRequest

Bên cạnh cách tham số, chúng ta có thể lấy cái gì ra từ Request object.

2 inteface ServletRequest và HttpServletRequest có 1 tập các phương thức mà bạn có thể gọi. Nhưng bạn không cần phải nhớ tất cả.

Sau đây là 1 vài phương thức:

- Lấy thông tin về trình duyệt và nền tảng của client

String client = request.getHeader("User-agent");

- Lấy thông tin cookies liên kết với request này:

Cookie[] cookies = request.getCookies();

- Lấy thông tin liên kết với client

HttpSession session = request.getSession();

- Lấy ra phương thức HTTP của request.

String method = request.getMethod();

- Lấy ra 1 inputStream từ request.

InputStream input = request.getInputStream();

Q: Tại sao tôi lại cần InputStream từ request?

A: Với 1 request loại GET, sẽ không có gì ngoài request header info. Nói theo cách khác, sẽ không có ai quan tâm về nó. Nhưng...với 1 HTTP POST, sẽ có body info. Phần lớn thời gian, tất cả những gì bạn quan tâm đến body là lấy ra các giá trị của tham số(ví dụ,) bằng phương thức request.getParameter();... Nhưng những giá trị này có thể rất lớn.

I. Life cycle review

Vòng đời của Servlet và API

- Container khởi tạo 1 Servlet bằng việc loading class, gọi constructor không tham số của Servlet . Và sau đó gọi phương thức init của Servlet .

- Phương thức init() (developer có thể override) được gọi chỉ 1 lần trong vòng đời của Servlet và luôn luôn trước khi Servlet có thể phục vụ bất kì Request nào của Client .

- Phương thức init cung cấp cho sv quyền truy nhập vào 2 đối tượng Sv Config và Sv Context, những thứ mà sv cần để lấy thông tin về cấu hình của sv và web app.

- Cs kết thúc vòng đời của sv bằng cách gọi phương thức destroy()

- Phần lớn thời gian trong vòng đời của sv là dành để chạy 1 phương thức service() nhằm đáp ứng rq của cli .

Chương 5: Attributes và listener

Không có servlet nào đứng 1 mình. Với những web app hiện đại ngày này, những thành phần của nó làm việc cùng nhau để giải quyết chung 1 vấn đề. Bạn có models, controller, view. Bạn có parameter . Bạn có helper classes. Vậy bạn ràng buộc chúng với nhau như thế nào. Các thành phần đó chia sẻ thông tin với nhau như thế nào? Làm thế nào để bạn có thể che giấu thông tin? Làm thế nào để bạn đảm bảo thông tin của mình là thread-safe. Cuộc sống của bạn rất có thể phụ thuộc vào điều đó. Vì thế hãy pha 1 tách trà và học kĩ chapter này. Vì thế hãy pha 1 tách trà và học kĩ chapter này.

Mục đích chương

1. Viết servlet code để access tham số initialization và tạo thành phần trong deployment descriptor dùng cho việc khai báo initialization parameter .

2. Phạm vi của các thuộc tính(attribute) cơ bản của servlet (request, session , context). Viết servlet code để thêm, lấy, remove attribute ; đưa ra 1 kịch bản sử dụng. xác định phạm vi chính xác cho mỗi thuộc tính và chỉ ra những vấn đề về multi-thread có lieenquan đến các phạm vi đó.

3. (Cover in Filters chapter) Mô tả các thành phần của đối tượng xử lý yêu cầu trong web container: Filter, Filter chain, Request and response wrappers, web resource (servlet hoặc jsp page).

4. Mô tả vòng đời đối với request, session, và web app. Tạo và cấu hình các lớp listener đối với vòng đời của mối thằng. Đưa ra 1 kịch bản, chỉ ra đúng listerner attribute để sử dụng.

5. Mô tả cơ chế của requestDispatcher; viết servlet code để tạo ra 1 request dispatcher. Viết servlet code để forward hoặc include 1 target resource.

Nội dung của chương rất quan trọng , vì thế đừng bỏ lỡ nó.

I. Tình huống

II. Kim muốn cấu hình địa chỉ email của anh ý trong deployment descriptor, không phải là mã cứng bên trong servlet class.

Đây là những gì Kim không muốn trong servlet của anh ấy:

Hard-coding thì rất là bad,. Điều gì sẽ xẩy ra nếu anh ấy muốn thay đổi địa chỉ email

è Anh ấy sẽ phải recompile.

Cách tốt hơn đó là anh ấy đưa địa chỉ email của mình vào trong DEPLOYMENT DESCRIPTOR (web.xml file) để khi deploy web app của mình, servlet của anh ấy có thể bằng cách nào đó "read" địa chỉ email của anh ấy từ DEPLOYMENT DESCRIPTOR file. Với cách này anh ấy không cần phải có hard-code trong servlet của mình mà chỉ cần thay đổi địa chỉ email trong web.xml file (không cần phải động vào servlet source code).

1. Init parameter

Init parameter to the resuce.

Bạn đã nhìn thấy request parameter có thể đi vào trong 2 pt doGet() và doPost() rồi nhỉ. Thật ra servlet cũng có thể có tham số khởi tạo đấy.

Trong file deployment descriptor (web.xml)

Rất đơn giản phải không. Chỉ cần đưa ra tên tham số và giá trị của nó. Điều duy nhất bạn cần nhớ là đảm bảo tag config cho init-param nằm trong thành phần <servlet> bên trong DD.

Code để lấy init param cấu hinh trong deployment descriptor file.

out.println(getServletConfig().getInitParameter("adminEmail"));

Mỗi servlet đều inherit 1 phương thức có tên là getServletConfig()

Phương thức getServletConfig trả về ServletConfig và 1 trong những phương thức của object ServletConfig là getInitParameter().

Bạn không thể sử dụng servlet init parameter cho đến khi servlet is initialized

Bạn biết là 1 servlet luôn kế thừa phương thức getServletConfig(), vì thế bạn có thể gọi nó từ bất kì phương thức nào trong servlet của mình để lấy 1 tham chiếu tới object ServletConfig . Khi đã có 1 tham chiếu của servletconfig thì bạn có thể gọi method getInitparameter(). Nhưng hãy nhớ điều này "Bạn không thể gọi nó từ constructor của mình" Quá sớm trong vòng đời của servlet . Servlet sẽ không có đủ hoàn toàn các tính năng của nó cho đến khi container gọi phương thức init()

Khi the Container khởi tạo 1 servlet thì trước tiên nó tạo ra 1 ServletConfig riêng biệt cho servlet đó. Sau đó Container sẽ đọc tham số khởi tạo của servlet đó từ trong dd và đưa nó cho ServletConfig . tiếp đó nó sẽ truyền servletconfig tới phương thức init() của servlet .

Q: Chúng ta đã không để ý là phương thức init() nhận 1 ServletConfig bới vì cái đó không phải là cái bạn sẽ override. Super class của bạn bao gồm 2 phương thức init(). 1 cái không có tham số và 1 cái có là ServletConfig . Phương thưc bạn kế thừa init(ServletConfig ) gọi phương thức init() không có tham số, vì thế cái mà bạn có thể override chỉ có thể là init() không có tham số.

Không có luật lệ nào quy định bạn không được override cái có tham số là ServletConfig . nhưng nếu bạn làm thì để làm clgt.

2. Servlet init parameters.

Tham số khởi tạo của servlet chỉ được đọc 1 lần duy nhất_ khi Container khởi tạo (initializes) servlet .

File web.xml(DD) sẽ được đọc bn lần.?-> Nhiều lần, mỗi khi có request đến.

Khi container tạo ra 1 servlet, nó đọc file DD và tạo ra cặp name/value đối với mối tham số khởi tạo. Container không bao giờ đọc tham số lại tham số init. Và khi đã có tham số là ServletConfig thì chúng sẽ không đọc lại trừ khi bạn redeploy servlet. Nghĩ về điều đó.

1. Container đọc dd đối với servlet này, bao gồm cả servlet init parameter (<init-param>)

2. Container tạo ra 1 thể hiện của ServletConfig đối với servlet này.

3. Container tạo ra 1 cặp name/value của String với mỗi tham số khởi tạo. Giả sử chúng ta chỉ có 1.

4. Container cung cấp cho tham chiếu servletconfig tham số khởi tạo.

5. Container tạo ra 1 thể hiện của lớp servlet class.

6. Container gọi phương thức init() của servlet . truyền vào đó tham chiếu của servlet config.

Container chỉ đọc servlet initi parameter chỉ 1 lần. Vì thế bạn không thể thay đổi địa chỉ email của mình trong vòng đời của servlet . Đó là 1 giải pháp ngớ ngẩn.

è Đó vẫn là cách tốt hơn là việc đặt nó vào trong code servlet của tôi . Tất cả việc t cần phải làm là thay đổi file xml và ấn vào button "redeploy", và địa chỉ mới sẽ nằm trong ServletConfig .

Q: Ừm, vậy button "redeploy" nằm ở đâu trong tomcat vậy?

A: Với tomcat, không có button nào cả. Chỉ có toool cho việc deploy và redeploy. Nhưng, nghĩ về điều này, điều tồi tệ nhất mà bạn cần phải làm để thay đổi tham số khởi tạo của servlet là gì? Bạn tạo ra 1 sự thay đổi nhanh chóng trong file web.xml, shutdown tomcat và restart. Khi restart, Tomcat nhìn vào trong thư mục webapps, và deploy mọi thứ nó tìm thấy trong đó.

Ví dụ:

Q: Tôi vừa nhận ra app của tôi thực tế đang sử dụng jsp để render page. Vậy 1 JSP có thể "see" tham số init của servlet.

1 JSP có thể get servlet initi parameter?

è 1 ServletConfig được sử dụng cho servlet configuration(Nó không hề nói là JSPConfig). Vì thế nếu bạn muốn 1 phần khác của app của bạn sử dụng cùng thông tin(same info) giống như tham số init của servlet , bạn cần thứ gì đó hơn thế.

Q: Chúng ta đã sử dụng cách gì trong beer-app? Chúng ta đã truyền thông tin model info tới jsp bằng cách sử dụng request attribute...

Chúng ta có thể làm theo cách này. Đối tượng request cho phép bạn set attribute, do đó bất kì servlet hoặc jsp nào gets request đều có thể sử dụng. Điều đó nghĩa là bất kì servlet hoặc jsp nào được sử dụng bằng cách forwarded sử dụng requestDitpatcher đều có thể sử dụng.

Chúng ta sẽ sẽ học về RequestDitpacther ở phần cuối của chương này, nhưng bây giờ tất cả điều mà chúng ta quan tâm là lấy data(trong trường hợp này là email address) tới các phần của web app mà cần nó, chứ không phải chỉ là 1 servlet.

3. Sự giới hạn của tham số khởi tạo.

Việc setting request attribute làm việc tốt... Nhưng chỉ với jsp, cái mà bạn dùng request để forwarded.

Với beer app, nó lưu trữ thông tin của model info trong request object . Bới vì bước tiếp theo của nó là forward request tới JSP, cái chịu trách nhiệm cho việc tạo view..

Nhưng điều đó không giúp chúng ta với email address. Bới vì chúng ta có thể cần sử dụng nó ở tất cả các phần trong ứng dụng. Có 1 cách để làm đó là 1 servlet đọc init parameter và sau đó lưu trữ chúng trong 1 nới khác để sử dụng. Nhưng chúng ta cần phải biết là servlet luôn chạy đầu tiên khi app được deployed. Và bất kì sự thay đổi nào tới web appp cũng có thể phá vỡ điều đó.

"Nhưng tôi thật sự muốn tất cả các phần của web app của mình có quyền access tới email address. Với init parameter , I có thể cấu hình chúng trong dd với mỗi servlet, và sau đó các servlet làm nó có thể khả dụng với jsp. Điều này thật buồn chán làm sao. Không có tính duy trì. I cần thứ gì đó mang tính global"

Q: Tối đang phân vân là liệu có cái gì đó giống init paramerter cho ứng dụng không?

A:

4. Context init parameter sẽ giải cứu bạn.

Context init parameter làm việc chỉ giống như servlet init parameter , ngoại trừ việc đó là context parameter là khả dụng với toàn bộ web app, không riêng 1 servlet nào cả. Vậy có nghĩa là bất kì servlet và jsp nào trong app cũng có thể automatically có quyền truy nhập tới context init parameter, vì thế chúng ta không cần phải lo lắng về việc cấu hình trong DD với mỗi servlet, và khi giá trị thay đổi, bạn chỉ cần thay đổi ở 1 nơi.

Ví dụ:

I. So sánh sự khác nhau của context và servlet init parameters.

Hãy nhớ về sự khác nhau giữa servlet init parameter và context init parameter.


Context init parameter,

- Nằm trong <web-app> nhưng không bên trong thành phần <element>

- getServletContext().getInitparameter("str")

- Khả dụng với mọi servlet và jsp và các phần của web app

Servlet init parameter:

- Nằm trong <servlet> với mỗi servlet xác định:

- getServletConfig().getInitparameter()

- Chỉ khả dụng với servlet nào có cấu hình <init-param>(Mặc dù servlet có thể mwor dụng tính khả dụng bằng cách lưu trữ nó trong 1 attribute)

ServletConfig là 1 đối với mỗi servlet

ServletContext là 1 với mỗi web app

Chỉ có 1 ServletContext for toàn bộ web, và tất cả các phần của web chỉa sẻ điều đó. Nhưng mỗi servlet trong app có 1 servletconfig riêng của nó. Container tạo ra 1 servletcontext khi web app được deployed, và tạo ra tính khả dụng với context cho mỗi servlet và jsp(Sẽ trở thành servlet )

- Container tạo ra 1 thể hiện của servletcontext

- Container cung cấp cho servletcontext 1 tham chiếu tới mỗi cặp name/value của tham số init context.

- Mỗi servlet và JSP được deployed giống như những phần của 1 web app đều có khả năng access tới ServletContext như nhau.

Note: Đừng nhầm lẫn giữa ServletConfig parameter với ServletContext parameter ,. Bạn phải biết răng cả ServletContext và ServletConfig đều có cùng init parameter, và cả hai đều có cùng phường thức getter (getInitparameter()). Nhưng...bạn cũng phải biết rằng context init parameter được set trong cặp thẻ <context-parameter>, ngang hàng với cặp thẻ <servlet> chứ không nằm trong thẻ <servlet>. Còn servlet init parameter sử dụng <init-param> đăt trong thành phần <servlet>

Note: Nếu app được distributed, chỉ có 1 ServletContext mỗi JVM.

Q: Nếu bạn modify file xml để chay đổi value của init parameter (cả servlet và context), khi nào servlet hoặc phần còn lại của web app nhìn thấy sự thay đổi này.

A: Chỉ khi nào web app được redeployed. Nhớ rằng, chúng ta đã nói trước đó. Servlet được khởi tạo chỉ 1 lần. và vào lúc bắt đầu vòng đời của nó, nó sẽ được cung cấp ServletConfig và ServletContext . Container đọc giá trị từ DD khi nó tạo 2 đối tượng này, và set giá trị.

Đấy là các tham số init. Hãy nghĩ nó thuần tùy là hằng số trong thời điểm web is deployed. Bạn có thể lấy nó ra để chạy, nhưng không thẻ set chúng. Không có phương thức setInitparameter.

I. ServletContext

Bạn có thể làm gì với ServletContext?

1 ServletContext là 1 connection của JSP hoặc Servlet với cả Container và phần còn lại của web app. Sau đây hãy xem các phương thức quan trọng của servletcontext .

Bạn có thể get 1 ServletContext theo 2 cách khác nhau.Với 1 servlet thì 1 đối tượng ServletConfig luôn luôn lưu giữ 1 tham chiếu đến ServletContext. Vì thế đừng bị lừa nếu bạn nhìn thấy servlet code theo cách sau đây.

getServletConfi (). g tServletContext(). g tInitParameter()No

Không hợp lệ cho lắm, nhưng câu lệnh sau đây cũng làm điều tương tự.

this.getServletContext().getInitParameter()In

Trong servlet bạn chỉ cần dùng ServletConfig để gọi servletcontext khi và chỉ khi servlet class của bạn không extend từ HttpServlet hoặc GenericServlet(Vì phương thức getServletContext() được kế thừa từ GenericServlet)

Vậy những lớp không phải servlet (helper, utility class)? Truyền 1 ServletConfig tới lớp đó, và lớp đó sẽ có thể sự dụng để getServletContext() để lấy ra tham chiếu của context

Q: Tất cả các phần của web app nhận quyền truy nhập tới ServletContext của chúng như thế nào?

A: Đối với servlet: Gọi phương thức getServletContext() mà nó kế thừa từ GenericServlet.

Đối với jsp thì có 1 khác biêt nhỏ: Jsp có nhưng cái gọi là đối tượng ngầm, và ServletContext là 1trong số đó. Hãy để lại phần JSP sử dụng ServletContext trong chương JSP.

II. Sự giới hạn của context parameter

.,.............................................................................

Chương 6: session management.

Web server không có bộ nhớ ngắn hạn. Ngay khi bạn gửi 1 response, chung sẽ quên ngay bạn là ai. Lần tới khi bạn gửi 1 request, chúng sẽ không nhận ra bạn. Nói theo cách khác, chúng không hề nhớ là bạn đã gửi 1 request trước đó, và dĩ nhiên chúng cũng không nhớ là đã gửi cái gì cho bạn trong lần response trước. Không gì cả. Thỉnh thoảng thì điều này là tốt. Nhưng đôi lúc bạn cần phải giữ được "conversation state" với khách hàng qua nhiều yêu cầu. (Kiểu trạng thái của biến gì đấy từ request sang request 2 vẫn còn sử dụng được). 1 giỏ hàng sẽ không làm việc nếu client phải lựa chọn và check out trong 1 single request.

Nội dung của chương

- Viết mã servlet để lưu giữ thông tin của 1 đối tượng A trong 1 session object và lấy thông tin object từ session object.

- Đưa ra kịch bản sử dụng API để truy cập session object. Giải thích khi nào 1 session object được tạo và mô tả cơ chế để destroy session object, và khi nào nó bị destoryed.

- Sử dụng session listener , viết code để phản ứng với 1 sự kiện( khi 1 đối tượng được add vào 1 session ) hoặc khi 1 session object được di chuyển từ 1 cái sang cái khác.

- Mô tả cơ chế khi nào session management có thể được dùng, cookies có thể được sử dụng để quản lí session như thế nào. Và như thế nào để URL rewriting quản lí session,. Viết code servlet để xử lý URL rewriting.

Kim muốn giữ trạng thái của client qua nhiều request .

Đúng thế, business logic trong model đơn giản chỉ check tham sô từ request và trả về câu trả lời(response). Không có ai trong app nhớ bất kì cái gì về client đã tạo ra 1 request như thế trước đó.

Cái a ấy co đơn giản chỉ là 1 xử lý business logic trong model check lại tham số từ request và khởi tạo ra 1 câu trả lời mới. Cái a ấy muốn là:

Tìm kiếm nững câu trả lời trước đo, Và căn cứ vào đó để trả lời câu hỏi hiện tại

Anh ấy có thể theo dõi câu trả lời của khách hàng như thế nào?

Bản thiết kế của Kim sẽ không thể làm việc trừ khi anh ấy có thể giữ được sự giám sát với mọi thứ mà client đã nói trong qua trình hội thoại, không phải chỉ câu trả lời in the current request. Mà cả câu trả lời trước đó nữa. Anh ấy cần 1 servlet để lấy ra các tham số từ request đại diện cho sự lựa chọn của a ấy, và lưu trữ nó ở đâu đó. Mỗi khi khách hàng trả lời 1 câu hỏi, advice engine sẽ sử dụng tất cả các câu trả lời trước đó của khách hàng để đưa làm căn cứ đưa ra lời khuyên cuối cùng.

Những ý tưởng có thể là gì

- Sử dụng session enterprise javabean

- Sử dụng database

- Sử dụng HttpSession

HttpSession: Như bạn đã biết trước đó. Chúng ta có thể sử dụng HttpSession để lưu giữ trạng thái của cuộc hội thoại. Nói cách khác nó tồn tại cho mọi lúc làm việc với 1 khách hàng cụ thể. Chúng ta có thể lưu trữ mọi thứ mà chúng ta nhận từ khác hàng vào trong session.

Session làm việc như thế nào?

1. Diane chọn "Dark" và nhấn submit button

è Container gửi 1 request tới 1 thread mới của BeerApp servlet .

è Thread BeerApp tìm ra session liên kết với Diane, và lưu trữ sự lựa chọn của cô ấy("Dark") trong session

2. Servlet chạy business logic của nó và return a response. Trong trường hợp này câu hỏi là "Tầm giá là bao nhiêu"

3. Diane cân nhắc câu hỏi mới trên page, và chọn "Đắt" rồi ấn submit button.

è BeerApp thread tìm thấy session liên kết với Diane, và lưu trữ câu trả lời "Đăt vào trong session " (Cùng khách hàng, cùng servlet, Khác request , khác thread, cùng session )

4. Servlet chạy business logic và trả về 1 response

Tưởng tượng trong lúc này, 1 client khác cũng vào website beer

5. Session của Diane vẫn đang active, nhưng cùng lúc Terri chọn "Pale" và đánh submit

è Container tạo ra 1 thread mới

è Thread mới bắt đầu với 1 session mới cho Terri, và gọi setAttribute() để lưu trữ sự lựa chọn của Pale

Khác client, Cùng servlet, Khác request, Khác thread, Khác session

I. 1 vấn đề nữa,.. Làm như thế nào mà Container biết được client là ai?

Giao thức http sử dụng kết nối không có định danh. Client browser tạo ra 1 connection tới server, gửi request, get response, và close connection. Nói theo cách khác, connection tồn tại chỉ trong 1 single request/response.

Bới vì connection không tồn tại, nên Container không nhận ra vị khách hàng này đã tạo ra request lần 2 giống hệt với khách hàng đã gửi request trước đo. Như vậy tức là mỗi request thì đến từ 1 client mới.

Q: Tại sao container không sử dụng địa chỉ IP của khách hàng. Nó là 1 phần của request mà?

A: Đúng, container có thể lấy địa chỉ IP của khách hàng từ request. Nhưng đó không phải là định danh của khách hàng được? Nếu bạn trên 1 mạng local, bạn có 1 địa chỉ IP unique, nhưng nếu thay đổi, địa chỉ IP sẽ không còn unique nữa. Với servlet, địa chỉ Ip của bạn là địa chỉ của router, vì thế địa chỉ của bạn có thể giống với bất kì ai cũng đang dùng mạng nó. Vì thế nó không giúp gì được. Bạn vẫn gặp phải vấn đề như cũ. IP address không thể là giải pháp để định danh các khách hàng trên internet được.

Q: Vậy thì làm thế nào về thông tin bảo mật? Nếu người dùng đăng nhập và kết nối được an toàn (HTTPS), Container biết chính xác khách hàng là ai, đúng không?

A: Có, nếu người dùng đăng nhập và kết nối được an toàn, Container có thể xác định khách hàng và liên kết anh ta với một phiên. Nhưng đó là một lớn nếu. Hầu hết các thiết kế trang web tốt cho biết: "Không bắt buộc người dùng phải đăng nhập vào cho đến khi thực sự có vấn đề, và đừng bật tính năng bảo mật (HTTPS) cho đến khi nó thực sự quan trọng." Nếu người dùng của bạn đang duyệt, Thêm mục vào một giỏ hàng, bạn có thể không muốn phí (để bạn hoặc người dùng) cho họ xác thực cho hệ thống cho đến khi họ quyết định để kiểm tra! Vì vậy, chúng ta cần một cơ chế để kết nối một máy khách với một phiên không yêu cầu client được chứng thực an toàn

Solution: client cần 1 session ID unique. Ý tưởng rất đơn giản, vào lần đầu tiên request của client đến, Container tạo ra 1 session ID duy nhât và đưa ngược nó lại cho client bằng cách gửi nó đi kèm response. Và client sẽ gửi lại session ID với mỗi lần request sau đó. Container nhìn thấy ID, và tìm ra session tương ứng, liên kết nó với request.

Vậy làm như thế nào để client và Container trao đổi thông tin session ID?

Bằng cách nào đó, Container phải lấy được session ID của client như 1 phần của response. Và client phải gửi lại session ID như là 1 phần của Request . Cách đơn giản nhất và phổ biến nhất là trao đổi thông tin sử dụng cookies.

Phần tuyệt vời nhất: Container làm phần lớn công việc liên quan tới cookie.

Bạn nói với Container rằng bạn muộn tạo hoặc sử dụng session. Nhưng Container cũng sẽ đảm nhiệm luôn việc khởi tạo sessionID, tạo ra 1 đối tượng Cookie, chuyển session ID vào trong cookie, và thiệt lập cookies như là 1 phần của response. Và vào mỗi lần request sau đó, Container lấy thông tin session ID từ cookie trong request, match session ID với session tồn tại, và liên kết session với request hiện tại.

Gửi 1 session cookie trong response.

Httpsession session = request.getsession();

Request.getSession(): Bạn yêu cầu 1 session, và Container sẽ tự kích hoạt các hành động khác. Bạn ko cần phải làm gì nữa.(Phương thức này làm được nhiều việc hơn là việc chỉ tạo ra session, nhưng lần đầu tiên bạn gọi nó, nó sẽ tạo ra 1 cookies để gửi cùng với câu trả lời). Không có sự đảm bảo nào là khách hàng sẽ chấp nhận cookies này...

Đâu đó trong phương thức service của bạn bạn sẽ đề nghị 1 session, và mọi thứ sẽ diễn ra tự động.

Bạn ko tạo ra 1 đổi tượng Httpsession mới

Bạn ko khởi tạo 1 session ID duy nhất

Bạn không tạo ra 1 đối tượng cookie mới.

Bạn ko liên kết session ID với cookie.

Bạn ko set cookie vào trong response.(Under the set-cookie header)

Tất cả các công việc của cookie xẩy ra bên dưới J

Lấy session ID từ request.

HttpSession session = request.getsession();

Whoa! Phương thức này lấy ra session ID cookie(và matching nó với session hiện tại). Bạn ko thực sự nhìn thấy session ID của mình. Nhìn phương thức này quả thật rất quen? Vâng, nó chính xách là cùng phương thức sử dụng để tạo ra session ID và cookie cho response .

Phương thức này hoạt động như sau:

IF(request bao gồm 1 session ID cookie)

Tìm ra session matching với ID này

ELSE IF(Không có session ID cookie hoặc không có session hiện tại nào matching với session ID)

Tạo ra 1 session mới.

Tất cả các công việc này đều xẩy ra bên dưới.

I. Checking for a new session.

Nếu tôi muốn biết session là đã tồn tại hay vừa tạo thì phải làm như thế nào ?

Câu hỏi hay. Phương thức getsession(), trả về 1 session bất kể trước đó có tồn tại 1 session hay không. Bạn luôn nhận được 1 thể hiện của Httpsession trả về từ phương thức này. Vì thế bạn chỉ có thể biết nó là mới hãy đã tồn tại nếu bạn hỏi session.

Session.isNew() sẽ trả về true nếu client không có response với session ID này.

Điều gì nếu tôi chỉ muôn session có sẵn. Bạn có thể có 1 kịch bản là servlet chỉ muốn sử dụng session tạo trước đó.

Có 1 phương thức overload getsession(boolean) dùng cho mục đích này. Nếu bạn không muốn tạo ra 1 session mới, gọi getsession(false) bạn phận sẽ có thể nhận giá trị null hoặc HttpSession tạo trước đó.

Code dưới đây gọi getsession(false) và test giá trị trả về là null,. Nếu nó là null, đoạn code sẽ output ra 1 msg và tiếp đó tạo ra 1 session mới.

Phương thức getsession() không có tham số sẽ không bao giờ trả về null. (Luôn trả về 1 thể hiện của httpsession)

Q: Trông có vẻ như getSESSION(true) là giống với getsession()..

A: Chính xác. Không có tham số chỉ là cách tiện hơn của có tham số là true.

I. Khi nào thì cookie fail?

Nếu ai đó disable cookies thì sao nhỉ. Session của bạn sẽ như thế nào nếu bạn không thể sử dụng cookies?

è Bạn có thể làm việc với session kể cả khi client không chấp nhận cookies, nhưng bạn sẽ phải làm thêm 1 chút việc.

Phần lớn các browser thì cho phép cookies hoạt động, nhưng ko có sự đảm bảo nào cả.

Nếu app của bạn phụ thuộc vào session, bạn cần 1 cách khác để client và container có thể trao đổi với nhau về thông tin session ID. Rất may là container có thể xử lý trong nhưng hợp này, nhưng nó sẽ cần 1 chút nỗ lực của bạn.

Nếu bạn sử dụng code session ở trang trước. gọi getsession() on the request- Thì container sẽ cố gắng để sử dụng cookies. Nếu cookie không được eneable , nó có nghĩa là client sẽ không join vào session. Nói cách khác phương thức isNew() của session luôn luôn trả về true().

Nếu khách hàng disable cookie thì nó sẽ ignore "Set-cookie" response header.

Nếu 1 client không chấp nhận cookies, bạn sẽ không nhận được bất kì exception nào. Không có hồi chuông nào cảnh bảo session làm việc với khách hàng sẽ sai.

URL rewriting: something to fall back on

Nếu client không nhận cookies, bạn có thể sử dụng URL rewriting như 1 bản sao lưu(back-up). Cho rằng bạn đã làm các phần khác đúng, url rewriting sẽ luôn luôn làm việc. client sẽ không quan tâm rằng nó diễn ra như thế nào và sẽ không làm bất kì cái gì để phòng chống nó. Hãy nhớ mục đích của chúng ta là làm thế nào để client và container trao đổi session Id với nhau. Truyền cookie trở lại là cách đơn giản nhất để trao đổi session ID, nhưng nếu bạn không thể thiết lập Id vào trong cookie, Bạn có thể put nó ở đâu? url rewriting nhận session ID và stick nó vào bên phải của mỗi url sẽ come into app này.

Tưởng tượng rằng mỗi web page mà ở đó mọi link sẽ có thêm 1 phàn mở rộng(session ID) gắn vào cuối của URL. Khi user click vào link mở rộng này, request sẽ đi đến Container cùng với phần mở rộng đó. Và Container đơn giản chỉ lấy ra phần mở rộng của request url và sử dụng nó để matching session.

URL rewriting chỉ được kích hoạt chỉ khi cookies fail, và chỉ khi bạn nói với response to encode the URL

Nếu cookie không làm việc, Container sẽ sử dụng đến url rewriting , nhưng chỉ nếu khi bạn đã encoding url. Container luôn mặc định sử dụng cookies trước, với url rewriting cuối cùng. Nhưng nếu bạn không encode rõ rằng url của mình, và client không chấp nhận cookies, bạn sẽ không thể sử dụng session. Nếu bạn encode url của mình, container sẽ sử dụng cookies trước rồi mới sử dụng url rewriting nếu cookies fail.

Q: Làm thế nào mà container biết cookies không làm việc? Dựa vào cái gì mà container quyết định sẽ làm việc với URL rewriting?

A: Thử cả 2 1 cách lần lượt. Khi container nhìn thấy bạn gọi request.getsession(), và nhận thấy nó cần phải bắt đầu 1 session mới với khách hàng này. Và container gửi 1 response với cả "set-cookies" header cho session ID và session ID apppended tới urls(Cho rằng bạn sử dụng response.encodeUrl())

Bây giờ hãy tưởng tượng lần request tiếp theo từ k.h này. Nó sẽ có session ID append vào URL, nhưng nếu client chấp nhận cookies, request cũng sẽ có 1 session ID cookies. Khi servlet gọi request.getsession(), container đọc session ID từ request, tìm session và nghĩ "Thằng k.h này chấp nhận cookies , vì thế tôi có thể ignore việc gọi response.encodeUrl()". Trong response, Tôi sẽ gửi 1 cookies vì tối biết nó làm việc, và không cần phải url rewriting nữa. Vì thế tối không quan tâm...

Url rewriting làm việc với sendRedirect()

è Bạn có thể có 1 kich bản: Bạn muốn điều hướng request tới 1 Url khác, nhưng bạn vẫn muốn sử dụng session. Có 1 phương thức đặc biết để làm điều này:

response. encodeRedirectURL("/BeerTest.do")

URL rewriting là tự động, nhưng chỉ với điều kiện bạn encode URLs của mình. Bạn cần chạy tất cả url của mình thông qua 1 phương phức response object-encodeURL() hoặc encodeRedirectURL()- Và container sẽ làm các thứ khác.

Q: Bạn nói rằng để sử dụng url rewriting , pages phải được tạo động. Vậy điều này nghĩa là. Tôi có thể làm điều đó với JSPs?

A: Đúng, bạn có thể làm url-rewriting trong jsp. Và có 1 cách rất đơn giản đó là sử dụng tag JSTL <c:URL>

URL encoding được xử lý bởi response.

Đừng quên rằng encodeURL() là cái được gọi từ đối tượng HttpServletResponse. Bạn không gọi nó với request, context, session. Chỉ cần nhơ rằng URL encoding là sử dụng với response.

- URL rewriting thêm session ID vào cuối của tất cả các URL trong html mà bạn viết để response.

- Session id sau đó sẽ trở lại cùng request với thông tin "mở rộng" ở cuối của request url.

- URL rewriting sẽ tự động xảy ra nếu cookies không làm việc với client, nhưng với điều kiện là bạn phải encode rõ rằng tất cả các url mà bạn viết

- Để encode 1 url, gọi response.encodeUrl(aString)

out.println("<a href='" + response.encodeURL("/BeerTest.do") + "'>click me");

- Không có cách nào để tự động nhận url rewriting với static page. Vì thế nếu bạn phụ thuộc vào session, bạn cần phải sử dụng trang khởi tạo động.

Tôi thật sự không muộn quá nhiều session cũ chiếm không gian server của mình.

Loại bỏ các session.

Client đến, bắt đầu 1 session, sau đó thay đổi quyết định của mình, và rời khỏi trang(hoặc browser crashes, hoàn thành mua sắm, hoặc computer crash,...).

Điểm quan trọng là, session object vẫn chiếm resource. Bạn không muốn session được tích lại lâu hơn cần thiết. Hãy nhớ, giao thức http không có đủ cơ chế cho server để biết khi nào 1 client đã gone. Vậy làm như thế nào để container hoặc bạn biết khi nào client bỏ đi? Làm thế nào để container biết trình duyệt của client bị crashed? Làm thế nào để Container biết khi nào việc destroy 1 session là an toàn?

Chúng ta muốn nó làm việc như thế nào.....

1. Diane chọn "Dark" và click summit button

2. Container gửi request tớ servlel

3. Container nạo ra 1 session . ID#343. Cookies "JsessionId" được gửi lại Diane trong response(không hiển thị)

4. Diane biến mất

5. Container không làm bất kì điều gì trong thời gian này(Mặc dù nó có thể phục vụ các client khác)

6. Session bắt đầu cho Diane vẫn ngồi đấy ... chờ....abandoned.

7. Diane không trở lại.....

8. Container kiểm tra trạng thái của session #343 và tìm thấy ko có request nào đến với session ID #343 suốt 20mins

9. Container nói "20mins là quá lâu. Cô ấy sẽ không trở lại" rồi destroy session.

Interface Httpsession

Tất cả những gì bạn quan tâm khi gọi getsession() là bạn sẽ nhận được 1 thể hiện của 1 lớp mà implement cái interface Httpsession. Và công việc tạo ra implement này là công việc của Container.

Khi đã có session, bạn có thể làm gì với nó?

Phần lớn thời gian, bạn sẽ sử dụng session để get và set session-scoperd attribute.

Nhưng vẫn còn nhiều điuề hơn nữa có thể làm. Sau đây là các phương thức chính của HttpSesion interface.

.

Bạn đã biết về các phương thức dùng cho attribute(getAttribute(), setAtribute(). Removeattribute()),), nhưng đây sẽ là vài cái mà bạn có thể cần trong app của mình

Nó làm gì?

Sử dụng nó làm gì?

getCreationTime()

Trả về thời gian session được tạo lần đầu?

Để tìm ra session lâu như thế nào.Bạn có thể muốn ràng buộc các session nào đó trong một khoảng thời gian cố định. Ví dụ, bạn có thể nói "Từ lúc bạn log in, bạn có chính xác 10min để hoàn thiện form này"

getLastAccessTime()

Trả về lần cuối Container nhận 1 rq với session ID này(Theo ms)

Để tìm ra khi nào client truy nhập lần cuối với session này. Bạn có thể sử dụng nó để quyết định rằng nếu sau 1 thời gian client không quay trở lại, bạn có thể gửi 1 email để đề nghị client trở lại. Hoặc có thể, bạn sẽ invalidate() session này.

setMaxInactiveInterval()

Specifies the maximum time, in seconds, that you want to allow between client requests for this session

To cause a session to be destroyed after a certain amount of time has passed without the client making any requests for this session. This is one way to reduce the amount of stale sessions sitting in your server.

getMaxInactiveInterval()

Returns the maximum time, in seconds, that is allowed between client requests for this session.

To find out how long this session can be inactive and still be alive. You could use this to judge how much more time an inactive client has before the session will be invalidated.

invalidate()

Kết thúc session. Điều này bao gồm cả việc remove tất cả các thuộc tính đang được lưu trữ trong session này.

Để kill 1 session nếu client không hoạt động hoặc nếu bạn biết session kết thúc(Ví dụ sau khi client check-out hoặc log out). Thể hiện của session bản thân nó bị thu dọn bởi Container, chúng ta ko quan tâm về điều đó. Invalidate nghĩa là session ID không tồn tại nữa, và attribute sẽ bị remove từ session object.

Tưởng tượng rằng Kim muốn hiển thị nên của user mỗi khi a ấy quay lại beer site. Vì vậy anh ấy set cookies trong lần đầu tiên anh ấy nhận được tên của client. Và nếu anh ấy nhận được cookies back với 1 request. Anh ấy biết là không cần phải ask for name again.

- Bạn có thể sử dụng cookies để trao đổi cặp name/value giữa server và client.

- Servlet gửi cookies tới client , và client gửi nó lại mỗi lần request sau.

- Session cookies biến mất khi mà trình duyệt của client quit. Nhưng bạn có thể nói với cookies để nó vẫn tồn tại sau khi browser shut down.

Sử dụng cookies với servlet API

Mọi thứ bạn cần làm với cookies sẽ được đóng gói trong Servlet API trong 3 lớp: HttpServletRequest , HttpServletResponse, Cookie.

- Tạo ra 1 cookies mới

Cookie cookie = new Cookie("username",name); --Constructor này nhận vào 1 cặp String name/value

- Thiết lập cookies sẽ tồn tại bao lâu ở client

Cookies.setMaxAge(30*60); --Mặc định thời gian là seconds. Code này nghĩa là: Tồn tại ở client 30min.

- Gửi cookies tới client

Response.addcookies(cookies);

- Get cookies từ client request.

Không có phương thức getcookies(String).. Bạn chỉ có thể get cookies trong 1 mảng cookies, và tiếp theo bạn cần lặp qua mảng đó để tìm ra cái phần tử cookies mà bạn muốn.

Ex:

Tưởng tượng Kim muốn user summit form cùng với tên của a ấy. Form gọi 1 servlet để get username request parameter, sau đó name này được set tới 1 cookies trong response.

Lần tới user tạo ra 1 request với bất kì servlet nào trên web, ck sẽ comeback cùng với request (Giả sử là cookies vẫn tồn tại). Khi 1 servlet trong web app nhìn thấy cookies, nó có thể gửi thông tin đó into dynamically-generated response, Và business logic biết là không cần phải hỏi user để lấy name của a ấy nữa.

Đoạn code sau đây sẽ mô tả điều đó.

Chú ý: Có cả phương thức addHeader và setHeader(). Tuy nhiên đối với cookies thì chỉ có phương thức addCookie() chứ không có phương thức setCookie().

Các cột mốc quan trọng của 1 httpsession.

- Session được tạo hoặc phá hủy

- Session attribute được added, removed hoặc replaced bởi 1 phần khác của app.

Cột mốc

- Session được tạo: Khi container tạo ra session lần đầu, vào thời điểm này, client chưa gửi session ID tới cùng với request.

- Session mirgration(Chưa tìm hiểu)

- HttpsessionBindingListener(Chưa tìm hiểu)

- Session listener(Chưa tìm hiểu)

Chương 7: Using JSP

1 jsp trở thành 1 servlet. 1 servlet mà bạn không hề tạo. Container look at jsp, dịch nó sang mã java và compile nó sang java servlet class. Nhưng bạn vẫn cần phải biết điều gì xảy ra khi code bạn viết trong jsp được biến thành mã java. Bạn có thể viết mã java trong jsp của mình, nhưng có nên như thế ko? Và nếu bạn ko viết java code, vậy bạn sẽ viết cái gì? Nó sẽ được dịch sang java code như thế nào? Trong chương này, chúng ta sẽ xem xét 6 loại khác nhau của thành phần JSP, mỗi cái có 1 mục đích và cú pháp riêng. Bạn sẽ học như thế nào, tại sao và viết gì trong jsp của mình. Mà bạn sẽ học để không viết gì trong jsp của mình.

Đến cuối cùng, thì JSP vẫn chỉ là servlet.

Jsp của bạn cuối cùng cũng sẽ trở thành 1 servlet hoàn chỉnh giống như bao servlet đang chay trong ứng dụng web của bạn. Nó giống hết các servlet khác, ngoại trừ việc lớp servlet này được viết cho bạn – Bởi container

Container nhận được nhưng gì bạn viết trong jsp, translate nó sang servlet class source file(.java), sau đó compile nó thành java servlet class. Sau đó, nó chạy giống với các servlet khác mà bạn viết. Nói cách khác, Container load servlet class, tạo ra thể hiện và khởi tạo nó, tạo ra 1 thread mỗi request, và gọi phương thức service().

Điểm quan trọng nhất của chapter này đó là: Vai trò của code jsp đối với final servlet class?

Nói theo cách khác, trong mã nguồn khởi tạo servlet , thành phần trong jsp sẽ kết thúc ở đâu?

Một vài câu hỏi sẽ được trả lời trong chương này:

- Where does each part of your JSP fi le end up in the servlet source code?

- Bạn có quyền truy nhập đến "servletness" của trang jsp của mình? Ví dụ, 1 jsp có các khái niệm về servletconfig hay servletcontext hay không?

- Các loại element bạn có thể đặt trong jsp?

- Cú pháp của các element này?

- Vòng đời của JSP, bạn có thể nhẩy vào giữa nó?

- Các thành phần trong JSP tưởng tác với nhau như thế nào trong final servlet?

I. Making a jsp

Tạo ra 1 trang jsp hiển thị nó được truy nhập bao nhiều lần

Pauline muốn sử dung JSP trong web app của cô ấy – Cô ấy thực sự mệt mỏi với việc viết HTML bằng PrintWriter của servlet

Cô ấy quyết định học JSP bằng cách tạo ra 1 page động để in ra số lần page được request. Cô ấy hiểu rằng bạn có thể đặt Java code trong JSP sử dụng 1 scriplet – Nó có nghĩa là sử dụng java code bên trong 1 tag <%...%>

Cô ấy deploy và test nó

JSP không nhận ra Counter class

Counter claas nằm trong foo package, nhưng ko cái gì trong JSP biết điều đó. Điều tương tự cũng xảy ra với bạn với các đoạn java code khác. Và bạn cần biết luật sau đây: Import package hoặc sử dụng tên đường dẫn đầy đủ trong code của mình

Nhưng bạn có thể đặt câu lệnh import vào trong 1 JSP ... Bạn chỉ cần 1 directive

Sử dụng page directive để import package

1 directive là 1 cách để bạn cung cấp những chỉ dẫn đặc biệt tới Container trong thời gian page traslation. Directive đến với 3 dạng như sau: page, include, và taglib. Chúng ta sẽ để ý include và taglib trong các chương sau, nhưng bây giờ tất cả những điều chúng ta quan tâm là page directive , bởi vì nó là cái cho phép bạn import.

Java code thì nằm giữa cặp <%...%>

Directive thì nằm giữa cặp <%@ ...%>

Nếu bạn nhìn thấy JSP code bắt đầu với <%@, thì đó là 1 directive

Sử dụng expression

Nhưng Kim chú ý đến "expression"

Bạn không cần phải nói out.println() trong JSP!, chỉ cần sử dụng expression . expression tự đọng in ra bất kì cái gì mà bạn đặt vào giữa tags.

Expression thì ngắn hơn, chúng ta không cần phải in ra 1 cách rõ ràng bằng out.println(...

Chú ý 3 điểm sau:

Dấu ; đâu mất rồi?

Expression trở thành tham số của out.print()

Nói theo cách khác, container lấy mọi thứ bạn đánh vào giữa cặp tag <%..%> và đặt nó làm tham sô của câu lệnh in ra response PrintWriter out

Q: Nếu giả định bạn sử dụng expression thay thế cho việc out.println() trong 1 scriptlet, vậy tại sao lại cần 1 "out" ngầm định ở đây?

A: Bạn có thể không muốn sử dụng biến out ngầm định bên trong JSP page của mình, nhưng bạn có thể truyền nó tới những đối tượng khác của app không có quyền truy nhập trực tiếp vào output stream của response.

Q: Trong 1 expression, điều gì xẩy ra nếu phương thức không return bất kì cái gì?

A: Bạn sẽ nhận 1 error! Bạn không thể, không được sử dụng 1 phương thức với kiểu trả về là void. Container đủ thông mình để tìm ra điều này.

Q: Tại sao import directive bắt đầu với từ "page". Tại sao nó không phải là <%@ import..%> mà lại là <%@ page import...%>

A: Vì import là attribute của page directive nên cần phải chỉ ra

Q: Những attribute khác của page directive?

A: Nhớ page directive dùng để cung cấp cho container thông tin mà nó cần khi nó dịch JSP sang servlet. Thuộc tính mà chúng ta quan tâm, bên cạnh import là session, content-type và isELlgnored

Bạn không cần phải dùng Counter class... Bạn có thể làm mọi thứ trong jsp.

Tôi biết JSP sẽ được biến đổi thành servlet, vậy tôi có thể khai báo 1 biến count trong 1 scriptlet? Nó sẽ làm việc chứ

Khai báo 1 biến trong 1 scriptlet

Việc khai báo biến là hoàn toàn hợp lệ, nhưng nó sẽ không hoàn thành được công việc giống như Pauline kì vọng

Điều gì xẩy java với JSP code của bạn vậy?

Bạn viết JSP , nhưng nó trở thành servlet. Chỉ có 1 cách để biết điều gì đã xẩy ra là nhìn vào việc mà Container đã làm với JSP của mình. Nói theo cách khác, Container traslate JSP thành Servlet như thế nào?

Mã trên trang này không thực sự là code được genenrated bởi container – Code servlet source code được generated thì xấu hơn, khó đọc hơn. Bjo , tất cả điều mà chúng ta quan tâm là ở đâu trong servlet class JSP code của bạn kết thúc.

Tất cả các đoạn code expression và tất cả các scriplet đều trong servlet method.

Điều đó có nghĩa là biến được khai báo trong scriplet luôn luôn là biến local

Chú ý nếu bạn muốn nhnf thấy mã servlet code được khởi tạo từ tomcat. Nhìn vào

yourTomcatHomeDir/work/Catalina/yourServerName/yourWebAppName/org/apache/jsp.

Chúng ta cần thành phần JSP khác

Phải có loại JSP element khác để khai báo biến instance thay cho biến local...

Khai báo biến count trong scriplet nghĩa là biến được khởi tạo lại mỗi lần servlet method chạy.

Nghĩa là nó luôn được reset bằng 0 mỗi lần request. Chúng ta cần 1 cách nào đó để tạo ra biến count như 1 biến thể hiện.

Đến nay chúng ta đã xem xét về directive , scriplet và expression.

Directive : Cung cấp các câu lệnh đặc biệt, thông tin cho container traslate JSP

Scriplet: chỉ là plain old Java-> được khởi tạo và nằm trong servlet method.

EXPRESSION: trở thành tham số của phương thức print()

Nhưng vẫn có 1 thành phần JSP khác gọi là declaration

- Thành phần này có dấu ! sau kí hiệu %

- Vì đây không phải là 1 expression – bạn cần dấu ; ở đây

JSP declaration dùng để khai báo thành phần của generated servlet class. Nói theo cách khác, bất kì cái gì đặt trong <%!..%> đều được add vào lớp và bên ngoài phương thức service(). Điều này có nghĩa là bạn có thể khai báo cả phương thức và biến tĩnh. 

Theo lý thuyết thì bạn có thể định nghĩa cả những thành phần khác như inner class, nhưng 99.69% là bạn sẽ sử dụng để khai báo biến và phương thức. Đoạn code sau sẽ giải quyết vấn đề của Pauline. Bây giờ bộ đếm counter sẽ tăng lên mỗi lần client request page.

Container sẽ làm gì với JSP của bạn?

- Nhìn vào directive , lấy thông tin nó cần trong quá trình translation.

- Tạo 1 HttpServlet subclass

Với tomcat 5, generated servlet extends:

org.apache.jasper.runtime.HttpJspBase

- Nếu có 1 page directive với thuộc tính import, nó sẽ viết câu lệnh import ở trên cùng của file, chỉ dưới câu lệnh package.

Với tomcat 5, package statement là:

package org.apache.jsp;

- Nếu có declarations , nó viết chúng vào trong file class. Thường là chỉ dưới cậu lệnh khai báo class và trước phương thức service().

Tomcat 5 declares one static variable and one instance method of its own.

- Build phương thức service. Tên của phương thức là _jspService(). Nó được gọi bởi phương thức service(), và nhận 2 đối tượng HttpServletRequest và HttpServletResponse. Giống như 1 phần của phương thức này. Container khai báo và khởi tạo tất cả các đối tượng ngầm định(Bạn sẽ nhìn thấy nhiều đới tượng ngầm định hơn khi bạn chuyển trang)

- Kết hợp plain old HTML(được gọi là template text), scriplet và expression vào trong phương thức service(), format mọi thứ và viết nó vào response PrintWriter.

Tất cả những điều mà bạn cần và biết là behavior của mỗi thành phần (scriplet,directive,declaration)

Chỉ còn 1 thứ mà bạn cần phải biết đó là vòng đời của jsp: jspInit(), jspDestroy(), _jspService(). Chúng sẽ được giởi thiệu ở phần sau.

Trang 332 đéo hiểu gì

Bài tập và đáp án trang 337.

API cho generated servlet

Container khởi tạo 1 class từ JSP của bạn bằng cách implements interface HttpJspPage. Tất cả những điều bạn cần biết là 3 phương thức chính sau

- jspInit()

Phương thức này được gọi từ phương thức init(). Bạn có thể override nó(Bạn có tìm ra nó như thế nào ?)

- jspService()

Phương thức này được gọi từ phương thức serivce() của servlet. Điều đó có nghĩa là nó chạy trong 1 thread riêng biệt mỗi lần request. Container truyền request và response tới phương thức này.

Bạn không thể override phương thức này. Bạn không thể làm ANYTHING với pt này.

- jspDestroy()

phương thức này được gọi từ destroy() của servlet . Bạn có thể ghi đè pt này.

Không có gì đằng trước 2 phương thức jspInit() và phương thức jspDestroy(). Nghĩ như thế này có dấu gạch dưới trước phương thức nghĩa là đừng động vào nó.

Vì vậy, dấu gạch dưới mà không có nghĩa là bạn có thể override. Nhưng nếu có 1 dấu gạch dưới ở trước tên phương thức. bạn không thể ghi đè nó.

Vòng đời của JSP

Bạn viết .jsp file. Container viết ra .java file từ JSP.

1. Kim viết ra file .jsp và deploy nó gióng như 1 phần của 1 web app->Container đọc file web.xml(DD) của app, không không làm bất kì cái với .jsp file(Cho đến khi nó được request lần đầu)-> Jsp chỉ ngồi đâu đó trên server, và chờ xem có client nào request nó không.

2. Client đánh vào 1 link để yêu cầu .jsp-> Container cố gắng để dịch file .jsp sang .java source code(JSP syntax error được bắt trong giai đoạn này)

3. Container compile servlet .java souce thành .class file(Java languge/syntax error được bắt ở đây)

4. Container LOADS the newly-generated servlet class.

5. Container instantiates servlet và jspInit() được chạy-> Object được init xong thì trở thành 1 full-fledged servlet, sẵn sàng để accept request của client.

6. Container tạo ra 1 thread để xử lý request của client, và _jspService() được chạy.

Mọi thứ sau đó là xử lý của plain old servlet . Sau đó servlet gửi 1 response cho client hoặc forwad request tới thành phần khác của web app.

Translation và compilation chỉ xẩy ra 1 lần.

Khi bạn deploy 1 web app với 1 JSP, toàn bộ bước translation và complitation xẩy ra chỉ 1 lần trong vòng đời của jsp. 1 khi nó được translate và compiled, nó giống như bất kì servlet khác. Và cũng giống như bất kì servlet nào khác, 1 khi servlet đã được loaded và initialized, chỉ có 1 điều xẩy ra vào thời điểm request là tạo hoặc cấp phát 1 thread cho service() method. Vì thế 2 bức tranh trên chỉ xẩy ra trong the first request.

Q: Có lẽ là có cách để pre-translate và complile... đúng không?

A: Có. Nhưng ko nên(Trang 308/913)

Nếu JSP chuyển thành servlet, tôi tự hỏi là liệu tôi có thể cấu hình servlet init parameter... và liệu tôi cũng có thể override phương thức init() của servlet ?

I. Overriding jspInit()

Initializing your JSP

Bạn có thể khởi tạo như servlet trong jsp của mình, tuy nhiên nó có 1 chút khác với những gì bạn làm trong regular servlet.

Configuring servlet init parameter.

Bạn cấu hình servlet init parameter cho jsp của mình gần giống như cách bạn cấu hình cho servlet bình thường. Chỉ có 1 điểm khác là bạn phải add 1 thành phần <jsp-file> bên trong thẻ <servlet>

Overriding jspInit()

Vâng, thật đơn giản. Nếu bạn implement 1 phương thức jspInit(), Container gọi phương thức này ở đầu của life giống như 1 servlet. Nó được gọi từ phương thức init() của servlet, vì thế tại thời điểm phương thức này chạy có 1 ServletConfig và ServletContext available cho servlet. Điều này có nghĩa là bạn có thể getServletConfig() và getServletContext() từ bên trong jspInit() method.

Ví dụ này sử dụng jspInit() method để lấy 1 servlet init parameter (Cấu hình trong DD), và sử dụng value để set vào 1 attribute của application-scoped.

Attribute trong jsp.

Ví dụ trên chỉ ra việc setting 1 application-scope attribute trong jspInit() được mình override. Nhưng phần lớn thời giạn bạn sẽ sử dụng 1 trong 4 đối tượng implicit để get và set attribute tượng tự như 4 attribute scope sẵn có trong jsp.

Vâng,4 . Hãy nhớ, ngoài việc có session, request và application , jsp add thêm scope thứ 4 – page scope – cho phép bạn get từ đối tượng pageContext.

Bạn thường không care về page sopce trừ khi bạn đang phát triển custom tag, vì thế chúng ta sẽ không nói gì nhiều về nó. Mà sẽ nói ở chap Custom tags.

Nhưng đây không phải là toàn bộ câu truyện! Trong jsp có 1 cách để get và set attribute của bất kì scope nào, sử dụng đôi tượng ngầm định pageContext. Giở trang tiếp theo và tìm hiểu xem.

II. PageContext và attribute.

Sử dụng PageContext cho attribute.

Bạn có thể sử dụng tham chiếu PageContext để get attribute từ bất kì scope nào, bao gồm cả page scope dành cho các thuộc tính được ràng buộc với PageContext.

............Tìm hiểu thêm.(Trang 346/914)

III. 3 Directives.

Chúng ta đã thấy dùng directive để getting import statement into generated servlet class trong JSP. Đó là page directive với import attribute(1 trong 13 attribute của page directive ). 12 cái còn lại ít dùng và cũng ko quan trọng lắm.

1. Page directive

<%@ page session = "false" %>

Định nghĩa các thuộc tính của trang chẳng hạn như mã hóa ký tự. Kiểu content-type của response. Có 13 thuộc tính khác nhau, những sẽ chỉ có 4 cái nên được chú ý.

2. Taglive directive

<%@ taglib %>

Định nghĩa thử viện tag cho trang jsp. 2 chương sau sẽ giải quyết

3. Include directive

<%@ include file="wickedHeader.html" %>

Định ngĩa text và code để get thêm vào trang hiện tại trong thời điểm translation. Điều này cho phép bạn build các khối có thể tái sử dụng(KIều như 1 trang chung về heading hoặc navigation bar) để có thể tránh trùng lặp các đoạn code trong JSP.

1. Các thuộc tính ucar page directive

Trong 13 page directive của JSP 2.0 spec, chỉ có 4 cái là rất quan trọng.

Quan trọng:

Import: Định ngĩa câu lệnh import sẽ được thêm vào dùng để khởi tạo servlet class từ JSP, bạn sẽ mặc định có 1 vài câu lệnh import khi container thực hiện conver từ JSP sang servlet. Ví dụ: java.lang, java.servlet.....

isThreadSafe :

contentType : Định nghĩa MIME type(và tùy chọn character encoding) cho JSP response. Đây là mặc định

isELIgnored: Định nghĩa EL expression được ignored khi page được tranlated. Chúng ta chưa nói về EL, nó sẽ nằm ở chương sau. Đây là 1 trong 2 cách để bạn có thể nói với Container để ignored El expression

isErrorPage : Nếu nó là true, page có quyền truy nhập đến các đối tượng exception ngầm định(1 tham chiếu tới Throwable). Nếu là false thì các đối tượng Exception ngầm định sẽ là not available trong JSP.

errorPage:

Không quan trọng

Language: Định nghĩa ngôn ngữ sử dụng trong scriplet, expression , và declaration. Bên phải , chỉ có 1 giá trị hợp lệ là "java", những giá trị khác chưa vào, có thể trong tương lai ngôn ngữ là c#....

Extends: Định nghĩa superclass

Session :

Buffer:

autoFlush:

info:

pageEncoding:

Quả là 1 chương hay với việc làm thế nào để put java code vào trong JSP , nhưng chờ xem nào?

Bức thư này dịch như sau:

"Khẩn cấp

Hiệu quả ngay lập tức, bất kì ai đang sử dụng scriplet , expression hoặc declartion trong jsp sẽ ngay lập tức không được trả lương và bị sa cmn thải ngay lập tức

"

Scriplet được coi là độc hại?

Đó có phải là sự thật? Có nhược điểm gì khi đặt mã java vào trong JSP vậy?

è Rất nhiều người tin rằng thật sự là một thói quen rất xấu khi đặt mã java vào trong JSP của mình

Tại sao? Tưởng tượng ban được thuê để build 1 web site lớn. Team của bạn bao gồm 1 số nhỏ các lập trình viên back-end Java, và 1 nhóm lớn các web designer(Những người sử dụng Dreamwever và Photoshop)_Và họ không phải là lập trình viên(Ngoại trừ 1 vài người vẫn nghĩ thiết kế HTML là coding)

Có 2 sự phiền toái nếu bạn đặt mã java vào trong JSP

1. Web pages designer không biết Java

2. Java code trong JSP rất khó để thay đổi và maintain

è EL : Câu trả lời cho vấn đề này,

EL viết tăt của "expression Language" và nó trở tahnhf 1 phần chính thức bắt đầu từ JSP 2.0. El luôn luôn là cách đơn giản hơn để bạn làm thứ gì đó tương tự như sử dụng scriplet hoặc expression .

Dĩ nhiên bạn có thể nghĩ "Tôi muốn JSP của mình sử dụng custom method?, vậy tôi có thể khai báo phương thức nếu không thể sử dụng Java"

Ahihi... Viết các method... không phải là mục đích của EL. Mục đích của IL là cung cấp 1 cách đơn giản hơn để invoke Java code – và code này lại nằm ở 1 nơi nào đó.

Điều đó có nghĩa là bạn không viết code vào trong JSP của mình. Bạn viết java method ở nơi nào đó, và gọi nó sử dụng EL.

Chương tiếp theo sẽ là về EL, vì thế chúng ta không đi vào chi tiết ở đây,

El expression

Please contact: ${applicationScope.mail}

Tương tự với cách dùng Java expression

Please contact: <%= application.getAttribute("mail") %>

Q: Tôi thật sự vẫn chưa nhìn thấy sự khác biệt và benefit của El và Java expression

A: Bạn vẫn chưa biết hết được full benefit của EL đâu. El là dễ học hơn đối với non-Java programer. Và nó dễ maintain hơn là sử dụng scriplet.

Vậy làm thế nào để bạn ngăn programer sử dụng các thành phần scriplet trong JSP?

è Easy, bạn có thể đặt 1 thành phần trong DD để disable tất cả các thành phần scripting.

Sử dụng <scripting-invalid>

Thật đơn giản – bạn có thể làm cho tât cả các thành phần scripting trong JSP như scriplet , Java expression, declaration là invalid bằng việc viết thêm 1 thẻ <scripting-invalid> trong DD:

Hãy coi chừng – Bạn có thể nhìn thấy ở 1 vài quyển sách hoặc bài báo chỉ ra 1 page directive disable scripting. Trong draft version của 2.0 spec, có 1 thuộc tính của page directive như sau:

<%@ page %>

Điều này sẽ không làm việc, attribute isScirptingEnable đã được remove trong final spec!!

Chỉ có 1 cách để invalidate scripting đó là thông qua tag <scripting-invalid> trong DD.

You can choose to ignore EL

(Trang 356)

Chúng ta đã thấy 5 thành phần khác nhau có thể xuất hiện trong JSP : scriplet , directive, declaration, Java expression, và El expression.

Nhưng chúng ta vẫn chưa nhìn thấy actions. Chúng đến theo 2 kiểu: standard và ... not

Standard Action:

<jsp:include />

Other Action:

<c:set />

Làm bài tập về chương này.

Chương 8: Sciptless Jsp

Tất cả các đầu mục liệt kê sau đây sẽ được trình bày hết trong chowng này,. Đây là 1 chương hay. Hãy giành thời gian cho nó, có rất nhiều chi tiết mà cần phải làm rõ.

Viết JSP page sử dụng EL và Standard Action

1. Viết các đoạn mã sử dụng các đối tượng ngầm định trong EL: pageScope; requestScope; sessionScope; applicationScope; param và paramValue; header và headerValuesm cookies và initParam

2. Viết các đoạn code sử dụng toán tử EL: truy nhập thuộc tính( sử dụng toán tử .), truy nhập collection(Sử dụng toán tử [])

3. Viết 1 đoạn code sử dụng toán tử EL: toán tử toán học, toán tử quan hệ, toán tử logic.

4. Với các chức năng của EL: Viết 1 đoạn code sử dụng EL function: định danh hoặc tạo TLD file sử dụng để khai báo 1 EL function; xác định và tạo ra 1 đoạn code mẫu để define 1 EL function.

5. Viết 1 đoạn code sử dụng standard action: jsp:useBean(với các thuộc tính: 'id','scope', 'type', 'class'). Jsp:getProperty và jsp:setProperty.

6. Viết code sử dung: jsp:include, jsp:forward, jsp:param

MVC app của chúng ta phụ thuộc vào attribute.

Nhớ lại trong MVC beer app chính thức, Servlet controller nói với model(Java class with business logic), tiếp đó set 1 thuộc tính trong request scope trước khi forward nó tới JSP view.

JSP phải get attribute từ request scope, và sử dụng nó để render response để gửi lại client. Đoạn code sẽ giống như sau

Sử dụng scriplet để get attribute trong Jsp. Nhớ là scripting expression luôn luôn là argument của phương thức out.print()

Nhưng chờ đã, nếu attribute là 1 instance của Persion, không phải là String thì sao nhỉ?

Persion có 1 property và "name" và 1 cặp get,set.Code sẽ như sau:

Với 1 vài cặp standard action, chúng ta có thể loại bỏ tất cả scripting code(declartions, scriplet, expression ) trong JSP của mình mà vẫn có thể in ra giá trị của property name. 'name' không phải là 1 attribute.

Code sẽ như sau:

Lý giải cấu trúc <jsp:useBean> và <jsp:getProperty>

Làm thế nào mà Container biết "person" nghĩa là gì? Nếu chúng ta chỉ có tag <jsp:getProperty> trong JSP, nó gần giống như việc bản sử dụng biến mà không khai báo_tên là 'persion'. Container thường sẽ ko có ý kiến gì về việc bạn nói, trừ khi bạn FIRST đặt 1 <jsp:useBean> trong page. <jsp:useBean> là 1 cách để bạn khai báo và khởi tạo các đội tượng sẽ được sử dụng trong <jsp:getproperty>

Khai báo và initalize 1 bean attribute với <jsp:usebean>

Lấy 1 propety của bean attribute sử dụng <jsp:getproperty>

I. <jsp:usebean>

<jsp:usebean> có thể tạo ra 1 bean!

Nếu <jsp:usebean> không thể tìm 1 attribute object với được đặt tên là "persion", nó có thể tạo ra 1 cái! Nó giống với cách mà request.getsession()||request.getsession(true) làm việc – nếu nó tìm kiếm lần đầu mà không thấy, nó sẽ tạo ra 1 cái.

Nhìn vào đoạn code trong generated code bên dưới, bạn sẽ nhìn thấy cái gì đang xẩy ra- có 1 if test ở đó. Nó check bean dựa trên giá trị của id và scope trong tag, và nếu nó không lấy được cái nào,. Nó sẽ tạo ra 1 thể hiện của class specified trong class, assign đối tượng đã tạo tới biến id, tiếp theo set no như là 1 attribute của scope mà bạn đã định nghĩa trong task

Điều này có thể không đúng lắm – Tôi không muốn có 1 bean mà property của nó không được set – Nếu container tạo ra 1 bean sử dụng tag này, thì property của bean sẽ ko có giá trị mất...

Bạn có thể sử dụng <jsp:setproperty>

Bạn đã biết ở đâu có get ở đó có set. Thẻ <jsp:setproperty> là cái thứ 3 và là cái cuối cùng của bean standard action. Nó rất dễ sử dụng.

->Như thế còn tệ hợn, Nếu bean của tôi đã tồn tại, JSP của tôi sẽ reset lại giá trị property của bean đã tồn tại.

1. <jsp:usebean> có 1 body

<jsp:usebean> có thể có 1 body!

Với 1 <jsp:usebean> body, bạn có 1 đoạn code chạy có điều kiện... ONLY nếu bean attribute không thể tìm và new bean được tạo ra.

Nếu bạn đặt setter code(<jsp:setproperty> ) của mình vào bên trong body của <jsp:usebean> , thì thuộc tính setting này sẽ có điều kiện. Nói theo cách khác, giá trị của thuộc tính sẽ được set chỉ nuế 1 bean mới được tạo ra. Nếu bean đã tồn tại với điều kiện scope và id thì thân của tag sẽ không được chạy, vì thế property sẽ không bị reset lại trong JSP code.

Servlet được khởi tạo như nào khi <jsp:usebean> có body

Rất đơn giản. Container sẽ đặt tất cả extra property-setting code vào bên trong if test

I. Ploymorphic reference

Liệu bạn có thể tạo ra tham chiếu kiểu polymorphic?

Khi bạn viết <jsp:usebean> , thuộc tính attribute sẽ xác định class của đối tượng được tạo mới(Nếu nó được tạo). Nó cũng sẽ xác định kiểu của biến tham chiếu sử dụng trong generated servlet.

Nhưng... Nếu chúng ta muốn kiểu tham chiếu khác với kiểu đối tượng thực tế? Chúng ta sẽ thay đổi lớp Person thành abstact mà tạo ra 1 lớp con concrete Employee của nó. Tưởng tượng chúng ta muốn kiểu tham chiếu là Person và kiểu đối tượng tạo ra là Employee.

Thêm vào thuộc tính type của thẻ <jsp:usebean>

Với những thay đổi mà chúng ta tạo ra với Person class, chúng ta sẽ gặp rắc rồi

Persion bây giờ là lớp abstact, Dĩ nhiên, bạn ko thể tạo ra nó, nhưng Container vẫn sẽ phải cố, dựa vào thuộc tính class trong trag

Chúng ta đang cần biến tham chiếu kiểu Person , và đối tượng là 1 thể hiện của class Employee. Thêm vào thuộc tính type sẽ giúp chúng ta làm điều đó.

Type có thể là 1 kiểu class, abstract hoặc interface – Mọi thứ bạn có thể sử dụng để khai báo 1 kiểu tham chiếu. Nếu class không thể được gán tới reference type, sẽ có exception. Vì thế class phải là subclass hoặc concrete implementation của type

Sử dụng type mà không dùng class attribute

Điều gì xảy ra nếu chúng ta khai báo type attribute , nhưng không phải báo class attribute ?

è Kết quả nếu persion attribute đã tồn tại trong page scope

Nó chạy đúng

è Kết quả nếu persion attribute không tồn tại trong page scope

java.lang.InstantiationException: bean person not found within scope

Chú ý:

- Nếu type attribute được sử dụng mà không có class attribute, thì bean phải tồn tại rồi

- Nếu class được sử dụng(có hoặc không có type attribute ) thì class phải không là abstract và phải có 1 phương thức constructer là public và không có tham số.

Q: Trong ví dụ trên, "foo.Person" là kiểu abstract, vì thế dĩ nhiên là nó không thể khởi tạo. Điều gì nếu bạn thay đổi type attribute thành "foo.Employee"? Nó sẽ sử dụng type attribute cho cả reference và object type?

A: Không, nó sẽ không bao giờ làm việc. Nếu container phát hiện ra bean không tồn tại, và nó nhìn thẩy chỉ có type attribute , nó biết rằng bạn mới cung cấp cho nó 1 nửa, vân xconf thiếu class attribute . Nói theo cách khác , bạn ko nói cho nó biết làm thế nào để tạo ra 1 thể hiện.

Nếu bạn sử dụng type mà ko có class, tốt hơn hết là bạn phải chắn chắn rằng bean đã được stored như 1 attribute , ở scope id mà bạn đặt vào trong tag.

1. Giá trị mặc định của scope attribute là "page"

Nếu bạn không chỉ ra scope trong cả <jsp:usebean> và <jsp:getproperty> thì container sẽ mặc định sử dụng "page"

Không được nhầm lẫn giữa type và class. Check out đoạn code sau:

<jsp:useBean type="foo.Employee"/>

Câu lệnh trên sẽ không bao giờ làm việc.

org.apache.jasper.JasperException: Unable to compile class for JSP foo.Person is abstract; cannot be instantiated Person = new foo.Person();

Hãy nhớ là:

Type == reference type

Class == object type

Hoặc hiểu theo nghĩa như thế này:

Type là cái mà bạn sẽ dùng để khai báo(Có thể abstract)

Class là cái mà bạn sẽ instantiate(Phải là concrete)

Type x = new class()

Làm bài tập trang (392/913)

Tôi đang nghĩ đến 1 vài thứ, giả sử chúng ta không sử dụng servlet controller , và HTML form action đi thẳng vào JSP , vậy có cách nào mà tôi có thể sử dụng request parameter để set vào bean property mà không sử dụng scripting?

Tưởng tượng đây là form của chúng ta:

Chúng ta có thể làm bằng cách trọn standard action và scripting:

<jsp:usebean/>

<%person.setName(request.getparameter("userName"));%>

Chúng ta thậm chí còn có thể làm điều đó với việc đặt scripting bên trong 1 standard action:

<jsp:usebean/>

<jsp:setPropertyuserName") %>" />

</jsp:useBean>

è Đúng thế, bạn đang nhìn thấy 1 expression bên trong <jsp:setproperty> tag(Điều này xảy ra bên trong body của tag <jsp:usebean>)

Và vâng, trông nó thật là tệ.

I. Sử dụng param

Param attribute sẽ giải cứu bạn

Rất đơn giản. Bạn có thể gửi 1 request parameter thẳng tới 1 bean, không cần scripting, chỉ cần sử dụng param attribute.

Thuộc tính param cho phép bạn set giá trị của bean property bằng giá trị của request parameter. Chỉ bằng việc đặt tên request parameter.

Bên trong TestBean.jsp

Giá trị của param "userName" đến từ tên thuộc tính của trường trong form input

Nhưng chờ đã, chúng ta vẫn có thể làm tốt hơn...

Tất cả những gì bạn cần phải làm là đảm bảo name của trường input trong form của mình(Cái mà sẽ trở thành request parameter name) có tên giống hệt property name trong bean của mình. Sau đó, trong <jsp:setproperty> bạn không chỉ ra thuộc tính param. Nếu bạn đặt tên của property mà không chỉ ra value hoặc param, bạn đang nói với container rằng hay get value từ request parameter với cùng matching name.

Nếu chúng ta thay đổi HTML để tên của input field match với property name:

Sau đó làm như thế này:

Nếu tên của request parameter match với name của bean property, bạn không cần phải chỉ ra value của attribute value trong thẻ <jsp:setproperty>

Nó thậm chí còn có thể tốt hơn được nữa

Hãy xem thử xem điều gì sẽ xảy ra nếu bạn làm cho tất của tên của request parameter match với tên của bean property. Person bean(1 thể hiện của foo.Employee) thực tế sẽ có 2 properties – name và empID.

Và cái chúng ta nhận được

- JSP: Tôi muốn cậu lặp qua tất cả các request parameter và tìm ra bất kì cái nào match với name property của bean, và set VALUE của giá trị trùng khớp bằng giá trị của tham số truyền vào từ request.

- Container: Oh, chắc chắn rồi...Hãy để công việc đó cho tôi. Tôi sẽ nhìn vào class bean, getter và setter để lấy ra bean properties, sau đó tối match với request parameter qua tên của chúng...

Bean tag convert các thuộc tính primitive 1 cách tự động

JavaBean properties có thể là bất kì thứ gì, nhưng nếu chúng là giá trị String hoặc giá trị kiểu primitive, thì việc ép kiểu sẽ được tự động thực hiện.

<jsp:setProperty> action nhận 1 tham số Request kiểu String, chuyển đổi nó thành 1 giá trị int, và truyền nó vào bên trong phương thức setter để set cho nó giá trị này.

Q: tôi đang nghĩ là trong Container có chỗ nào đó thực hiện Integer.parseInt("343"), vậy sẽ có lỗi NumberFormatException nếu giá trị không phải là "343" chứ?

A: Nắm bắt tốt. Đúng, sẽ có cái gì go wrong nếu Request parameter cho trường empID không phải là int. Bạn cần phải validate nội dung của field để đảm bảo rằng, nó chỉ chứa các ký tự là số. Bạn có thể gửi form data tới Servlet trước, thay cho việc gửi thẳng vào JSP. Nhưng nếu bạn commited trực tiếp dữ liệu từ form lên JSP, và bạn không muốn scipting, chỉ cần sử dụng Javascript để check data của form trước khi gửi Request.

Q: Nếu 1 bean mà không có property là String hoặc primitive, vậy làm cách nào mà bạn set property mà không cần scripting? Giá trị của thuộc tính của tag luôn là String mà, phải không?

A: có thể. Nhưng có lẽ sẽ có nhiều việc phải làm hơn.

Việc tử động convert String, primitive type tới kiểu tương ứng sẽ không được thực hiện nếu bạn sử dụng scripting. Nó fail ngay cả khi có 1 expression bên trong thẻ <jsp:setProperty>

Nếu bạn sử dụng <jsp:setProperty> standard action tag với property wildcard, hoặc chỉ là tên của property mà không có thuộc tính value hoặc param(Điều này có nghĩa property name match với Request parameter name) hoặc bạn sử dụng param Attribute để chỉ ra Request parameter sẽ được gán vào bean property hoặc bạn đánh vào 1 giá trị literal thì việc tự động convert từ String sang int vẫn làm việc. Ví dụ sau cho các trường hợp tự động convert:

Nhưng nếu bạn sử dụng scripting , thì việc tự động conversion sẽ không làm việc.

Những thẻ tag bean standard action là tự nhiên hơn đối với những người không phải là lập trình viên.

Một lần nữa, thuận lợi của việc sử dụng tác lại hơn việc sử dụng scripting đối với web page designer. Với Java programer thì các tag này dễ dàng maintain hơn hard-code java scripting. Còn đối với các designer thì việc sử dụng các các thuộc tính của 1 thẻ mà không cần biết code behind nó sẽ dễ dàng hơn nhiều.

Object properties.

Điều gì sẽ xẩy ra nếu property không phải là giá trị kiểu String hay primitive. Nếu 1 Attribute là kiểu String thì việc print nó ra rất là dễ dàng. Nhưng nếu chúng ta tạo ra 1 Attribute là 1 thể hiện của Person(Trong ví dụ này thì class Person là concrete class). Nhưng chúng ta lại không muốn in ra attribute- Chúng ta muốn in ra 1 property của Attribute (Trong trường hợp này là tên của Persion và empID). Điều này là hoàn toàn có thể, standard action có thể xử lý String. Nhưng nếu bean có 1 property không phải là String hay primitive? Nếu property là Object type. Và Object đó cũng có property.

Điều gì nếu chúng ta muốn in ra property của property đó?

Person có 1 thuộc tính String "name"

Person có 1 thuộc tính Dog "dog"

Dog có 1 thuộc tính String "name"

Làm gì nếu muốn in ra tên của con chó của person.

Servlet : Tạo ra 1 con chó, set name của nó. Set cho thành value của property của Person.

Cố để hiển thị property của property.

Chúng ta đã biết là chúng ta có thể làm điều đó bằng cách sử dụng scripting. Nhưng liệu chúng ta có thể làm nó sử dụng bean standard action?

Điều gì sẽ xẩy ra nếu chúng ta put "dog" như là property của thẻ <jsp:getProperty> ?

- Không sử dụng standard action

- Với standard action(Không sử dụng scripting )

Bạn không thể nói: property = "dog.name"

Bạn đang đọc truyện trên: Truyen2U.Pro

#servlet