Khởi đầu với JavaServer Faces 1.2, Phần 2: Vòng đời, phép chuyển đổi, duyệt
tính hợp lệ, và trình nghe pha của JSF
Richard Hightower, CTO, ArcMind
Tóm tắt: Loạt bài hướng dẫn bao gồm cách làm thế nào để bắt đầu khởi động
công nghệ Java™ Server Faces (JSF), một khung công tác phía máy chủ, cung cấp
một cách tiếp cận dựa vào thành phần để phát triển giao diện người dùng web.
Phần 1 dẫn bạn bắt đầu với một tổng quan JSF 1.2 và một ứng dụng cơ bản. Phần
tiếp theo này sẽ làm cho bạn nắm vững các tính năng cao cấp hơn của JSF: các
trình duyệt tính hợp lệ, các trình chuyển đổi và trình nghe pha tùy biến theo yêu
cầu. Cùng với hướng dẫn này bạn sẽ hiểu rõ về vòng đời của ứng dụng JSF.
Trước khi bạn bắt đầu
Về loạt bài này
Giới thiệu mở đầu về công nghệ Java™ Server Faces (JSF), một khung công tác
thành phần giao diện người dùng phía máy chủ cho các ứng dụng web dựa trên
Java. Loạt bài này dành cho các nhà phát triển, những người mới bắt đầu tìm hiểu
JSF và muốn tiến nhanh — không chỉ với JSF, mà với cả việc sử dụng các thành
phần JSF để giảm công sức. Loạt bài này trình bày chỉ những điều cốt yếu, với rất
nhiều ví dụ.
JSF là một môi trường phát triển GUI khá truyền thống, giống như AWT, SWT,
và Swing. Một trong những lợi ích chính của nó là nó làm cho việc phát triển Web
dễ dàng hơn bằng cách giao những công việc khó khăn cho các nhà phát triển
khung công tác, chứ không phải cho các nhà phát triển ứng dụng. Cứ cho là bản
thân JSF phức tạp hơn nhiều so với các khung công tác Web khác, nhưng sự phức
tạp này được che giấu không để cho các nhà phát triển ứng dụng biết. Phát triển
các ứng dụng Web trong JSF dễ dàng hơn nhiều so với hầu hết các khung công tác
khác: nó đòi hỏi viết mã ít hơn, ít phức tạp hơn, và ít việc cấu hình hơn.
Nếu bạn đang thực hiện phát triển Java phía máy chủ, JSF là khung công tác dễ
nhất để tìm hiểu. Nó được định hướng để tạo các ứng dụng Web (không chỉ là các
trang web). Nó cho phép bạn tập trung vào việc mã hóa Java của bạn mà không
cần đối phó với các đối tượng yêu cầu, các đối tượng phiên, các thông số yêu cầu,
hoặc đối phó với các tệp tin XML phức tạp. Với JSF, nhiều thứ thực hiện nhanh
Các yêu cầu tiên quyết
Hướng dẫn này được viết cho các nhà phát triển Java có kinh nghiệm ở mức bắt
đầu tới mức trung cấp. Bạn cần phải có hiểu biết chung về cách sử dụng ngôn ngữ
Java, cùng với một số kinh nghiệm phát triển giao diện đồ họa người dùng (GUI). Các yêu cầu hệ thống
Để chạy các ví dụ trong hướng dẫn này, bạn cần có một môi trường phát triển Java
(JDK) và Maven Apache. Nó giúp để có một IDE Java. Các tệp tin dự án Maven
và các tệp tin dự án trong Eclipse Java EE và Web Tools Project (WTP) được
cung cấp. Xem Tải về để nhận được mã ví dụ. Hãy truy cập vào trang web sách
hướng dẫn của tác giả (xem Tài nguyên) để biết thêm thông tin về cách làm thế
nào để chạy các ví dụ này.
Ứng dụng CRUD JSF mẫu
Phần này giới thiệu một ứng dụng CRUD đơn giản mà bạn sẽ xây dựng trong các
phần tiếp sau để tìm hiểu về:
Từng thành phần HTML tiêu chuẩn của JSF
Tạo các trình biến đổi tùy biến
Làm việc với các trình duyệt tính hợp lệ
Làm việc với các trình nghe pha
Một ứng dụng quản lý các mối giao tiếp
Ứng dụng mà bạn sẽ xây dựng trong phần này là một ứng dụng quản lý giao tiếp
có cấu trúc giống như ứng dụng máy tính bỏ túi trong Phần 1. Như bạn có thể thấy
trong Hình 1, ứng dụng này là một danh sách CRUD tiêu chuẩn. Nó không yêu
cầu các quy tắc dẫn hướng do toàn bộ ứng dụng sử dụng chỉ một khung nhìn đơn
(trang contacts.jsp).
import javax.faces.application.FacesMessage;
import javax.faces.component.UICommand;
import javax.faces.component.UIForm;
import javax.faces.context.FacesContext;
import com.arcmind.contact.model.Contact;
import com.arcmind.contact.model.ContactRepository;
public class ContactController {
/** Contact Controller collaborates with contactRepository. */
private ContactRepository contactRepository;
/** The current contact that is being edited. */
private Contact contact = new Contact();
/** Contact to remove. */
private Contact selectedContact;
/** The current form. */
private UIForm form;
/** Add new link. */
private UICommand addNewCommand;
/** Persist command. */
private UICommand persistCommand;
/** For injection of collaborator. */
public void setContactRepository(ContactRepository contactRepository) {
this.contactRepository = contactRepository;
private void addStatusMessage(String message) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO, message, null));
}
//most getter/setter omitted
}
Listing 1 tạo ra một GUI CRUD với chưa đến 74 dòng mã — không quá tệ có phải
không.ContactController được quản lý trong phạm vi, yêu cầu , do đó một Contact
mới được tạo ra khi một ContactController được khởi tạo. Ba thành phần — biểu
mẫu (form) (có kiểu là UIForm), addNewCommand (có kiểu là UICommand), và
persistCommand (có kiểu là UICommand) — được liên kết với ContactController.
Phương thức addNew() đảm bảo rằng:
biểu mẫu (form) được bật lên để người sử dụng có thể nhập vào một giao
tiếp mới — form.setRendered(true)
addNewCommand được tắt đi — addNewCommand.setRendered(false)
nhãn của persistCommand được đặt là Add —
persistCommand.setValue("Add")
Phương thức persist() vừa xử lý cập nhật mối giao tiếp hiện có và vừa xử lý bổ
sung thêm mối giao tiếp mới bằng cách sử dụng contactRepository. Phương thức
persist() tắt form bật addNewCommand. Phương thức remove() loại bỏ mối giao
tiếp ra khỏi hệ thống bằng cách sử dụng contactRepository.
Phương thức read() sao chép selectedContact (có kiểu là Contact) vào một mối
giao tiếp. (Mối giao tiếp — cũng có kiểu là Contact — có giá trị liên kết với biểu
mẫu). Bạn có thể tự hỏi selectedContact bắt nguồn từ đâu. Nó được chọn khi
người dùng nhấn vào một mối giao tiếp trong danh sách các mối giao tiếp. (Hướng
dẫn này sẽ đề cập khi thảo luận về JSP.) Như trong Phần 1, addStatusMessage bổ
<h:form>
<h:commandLink binding="#{contactController.addNewCommand}"
action="#{contactController.addNew}" value="Add New " />
</h:form>
<h:form binding="#{contactController.form}" rendered="false"
styleClass="form">
<h:inputHidden value="#{contactController.contact.id}" />
<h:panelGrid columns="6">
<% First Name %>
<h:outputLabel value="First Name" for="firstName" accesskey="f" />
<h:inputText id="firstName" label="First Name" required="true"
value="#{contactController.contact.firstName}" size="10" />
<h:message for="firstName" errorClass="errorClass" />
<% Last Name %>
<h:outputLabel value="Last Name" for="lastName" accesskey="l" />
<h:inputText id="lastName" required="true"
value="#{contactController.contact.lastName}" size="15" />
<h:message for="lastName" errorClass="errorClass" />
</h:panelGrid>
<h:panelGroup>
<h:commandButton binding="#{contactController.persistCommand}"
action="#{contactController.persist}" />
</h:panelGroup>
</h:form>
<h:form>
<h:dataTable value="#{contactController.contacts}" var="contact"
</h:panelGrid>
</h:column>
</h:dataTable>
</h:form>
</f:view>
</body>
</html> <h:dataTable> là giá trị được liên kết để hiển thị các mối giao tiếp từ
contactController, khi sử dụng "#{contactController.contacts}". Mỗi mối giao tiếp
được ánh xạ vào bảng bằng cách sử dụng thuộc tính: var="contact". Trong
contacts.jsp, các kiểu dáng (style) oddRow và evenRow (được định nghĩa trong
một tệp tin CSS) được đặt vào trong thuộc tính rowClasses với
rowClasses="oddRow, evenRow". Điều này cho phép <h:dataTable> sử dụng các
kiểu dáng luân phiên cho các hàng trong bảng. Hãy kiểm tra tài liệu hướng dẫn
trực tuyến <h:dataTable> để tìm một danh sách đầy đủ về những cái có thể làm
với <h:dataTable> và các kiểu dáng, bởi vì <h:dataTable> có khá nhiều khả năng.
(Xem Tài nguyên để tìm liên kết đến JSF API Javadocs).
Bạn cũng có thể thiết lập một kiểu dáng cho toàn bộ <h:dataTable>
vớistyleClass="contactTable" hoặc cho vùng tiêu đề của nó với
headerClass="headerTable". Hoặc bạn có thể thực hiện các kiểu dáng luân phiên
tương tự như thế cho các cột bằng cách sử dụng thuộc tính columnClasses:
columnClasses="normal,centered". <h:dataTable> được thiết lập để nó không
hoàn trả nếu không có mối giao tiếp: rendered="#{not empty
contactController.contacts}"nào. Thành phần <h:dataTable> rất linh hoạt và dễ sử
dụng.
Bên trong <h:dataTable>, bạn sử dụng các <h:column> để hiển thị các giá trị theo
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>contactRepository</property-name>
<property-class>
com.arcmind.contact.model.ContactRepository
</property-class>
<value>#{contactRepository}</value>
</managed-property>
</managed-bean>
Lưu ý rằng contactRepository nằm trong phạm vi ứng dụng và nó được nội xạ vào
trong contactRepository của contactController bằng cách sử dụng phần tử
<managed-property>. Kỹ thuật này cho phép bạn nội xạ các phụ thuộc/các trình
cộng tác vào trong trình điều khiển để hỗ trợ việc duy trì mô hình và khung nhìn
riêng biệt, và nó cho phép bạn nội xạ các đối tượng giả để sau này có thể được
thay thế bằng các đối tượng thực. Nhiều hơn một lần, tôi đã làm giả một đối tượng
mô hình như ContactRepository và sau đó thay thế nó bằng phiên bản thực sau khi
hoàn thành GUI.
Mô hình của ứng dụng là khá đơn giản, như hiển thị trong các Listings 4 và 5.
Listing 4 hiển thị lớp Contact:
Listing 4. Contact
package com.arcmind.contact.model;
public class Contact { private String firstName;
private String lastName; protected long id;
public Contact(String firstName, String lastName) {
public void setId(long id) { this.id = id; }
}
Listing 5 hiển thị lớp ContactRepository, để mô phỏng việc viết các mối giao tiếp
vào một cơ sở dữ liệu:
Listing 5. ContactRepository
package com.arcmind.contact.model;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
public class ContactRepository {
private Map<Long, Contact> contacts = new LinkedHashMap<Long,
Contact>();
private static long counter = 1l;
public List<Contact> getContacts() {
return new ArrayList<Contact>(contacts.values());
}
public synchronized Contact persist(Contact contact) {
if (contact.id == 0) {
contact.id = counter++;
}
Như bạn có thể hình dung, thật khó để bao gồm tất cả các thành phần JSF của bạn
trên một trang web. May mắn thay, bạn có thể phân chia các thành phần JSF theo
các khung nhìn riêng biệt bằng cách sử dụng một <f:subview>, như hiển thị trong
Listing 6:
Listing 6. Các khung nhìn phụ contacts.jsp
<body>
<f:view>
<h3>Contacts (2nd version)</h3>
<h:messages infoClass="infoClass" errorClass="errorClass"
layout="table" globalOnly="true" />
<h:form>
<h:commandLink binding="#{contactController.addNewCommand}"
action="#{contactController.addNew}" value="Add New " />
</h:form>
<f:subview id="form">
<jsp:include page="form.jsp" />
</f:subview>
<f:subview id="listing">
<jsp:include page="listing.jsp" />
</f:subview>
</f:view>
</body>
Bạn có thể sử dụng các <f:subview> trong trang cha mẹ hay trong trang bao gồm
Lưu ý rằng selectOneMenu sử dụng thuộc tính giá trị để liên kết selectOneMenu
với selectedGroupId. Phần thân của selectOneMenu chứa một <f:selectItems>, đó
là giá trị liên kết với thuộc tính các nhóm: value=#{contactController.groups}.
Bạn tạo ra một danh sách các nhóm trong bean phía sau. Mã cho thuộc tính
selectedGroupId và thuộc tính các nhóm có trong Listing 8:
Listing 8. Xây dựng một danh sách của các nhóm
public class ContactController {
private GroupRepository groupRepository;
private Long selectedGroupId;
public List<SelectItem> getGroups() {
List<Group> groups = groupRepository.list();
List<SelectItem> list = new ArrayList<SelectItem>(groups.size()+1);
list.add(new SelectItem(Long.valueOf(-1L), "select one"));
for (Group group : groups) {
SelectItem selectItem = new SelectItem(group.getId(), group.getName());
list.add(selectItem);
}
return list;
}
//Other getter/setters removed Thuộc tính các nhóm (groups) trả về một danh sách các SelectItem. Lớp
SelectItem được sử dụng để biểu diễn một mục trong một danh sách. Nó được cả