Struts 2 là một trong những framework phổ biến để phát triển ứng dụng web bằng Java. Bài viết này sẽ tổng hợp một số câu hỏi phỏng vấn quan trọng về Struts 2 kèm theo câu trả lời để giúp bạn ôn tập và chuẩn bị kĩ lưỡng các kiến thức cơ bản về công cụ này.

Câu hỏi phỏng vấn Struts 2
Struts 2 là gì?
Apache Struts 2 là một framework mã nguồn mở chuyên dùng để xây dựng ứng dụng web bằng Java. Nó được phát triển dựa trên một framework khác là OpenSymphony WebWork.
Struts 2 có nhiều cải tiến so với Struts 1, giúp framework này trở nên linh hoạt, dễ sử dụng và mở rộng hơn. Các thành phần cốt lõi của Struts 2 là Action (xử lý logic cho một request của người dùng), Interceptor (thêm logic xử lý trước và sau một Action) và Result page (trang kết quả).
Framework này cung cấp nhiều cách để tạo các class Action và cấu hình chúng thông qua file struts.xml hoặc annotation. Ta có thể tạo các interceptor của riêng mình cho các tác vụ chung. Struts 2 đi kèm với nhiều tag và sử dụng ngôn ngữ biểu thức OGNL. Ngoài ra, ta cũng có thể tạo các bộ chuyển đổi kiểu riêng để hiển thị các result page. Result page có thể là JSP và các mẫu FreeMarker.
Khác biệt giữa Struts 1 và Struts 2 là gì? Struts 2 tốt hơn Struts 1 ở điểm nào?
Struts 2 được tạo ra để khắc phục những nhược điểm của Struts 1 và làm cho nó linh hoạt và dễ mở rộng hơn. Dưới đây là bảng so sánh điểm khác biệt cơ bản giữa Struts 1 và Struts 2:
| Thành phần | Struts 1 | Struts 2 |
|---|---|---|
| Lớp Action | Các lớp action của Struts 1 buộc phải kế thừa một lớp trừu tượng, nên chúng không thể mở rộng được nữa. | Các lớp action của Struts 2 có tính linh hoạt hơn. Ta có thể tạo chúng bằng cách triển khai interface Action, kế thừa class ActionSupport, hoặc đơn giản là tạo một class có phương thức execute(). |
| An toàn luồng | Lớp Action trong Struts 1 có thiết kế singleton và không có tính an toàn luồng. Lập trình viên phải xử lý cẩn thận việc chạy đa luồng để tránh các tác dụng phụ. | Mỗi lớp action của Struts 2 được khởi tạo cho từng request, do đó không có vấn đề về đa luồng và mặc định được coi là an toàn luồng. |
| Ràng buộc với Servlet API | API của Struts 1 bị ràng buộc chặt chẽ với Servlet API. Các đối tượng Request và Response được truyền trực tiếp vào phương thức execute() của action class. | API của Struts 2 chỉ có sự ràng buộc lỏng lẻo với Servlet API và tự động map (ánh xạ) dữ liệu từ form vào các property (thuộc tính) trong action class. Nếu cần truy cập đến các đối tượng của Servlet API, ta có thể dùng các interface *Aware. |
| Kiểm thử | Các action class của Struts 1 rất khó để test vì sự phụ thuộc chặt chẽ vào Servlet API. | Các action class của Struts 2 hoạt động như class Java thông thường. Chúng có thể được test một cách dễ dàng bằng cách khởi tạo và gán giá trị cho các property của chúng. |
| Map tham số request | Struts 1 yêu cầu chúng ta phải tạo các class ActionForm để chứa tham số từ request và phải đặt chúng trong file cấu hình của Struts. | Struts 2 thực hiện việc map tham số request một cách tự động. Tất cả những gì ta cần làm là khai báo các property JavaBean trong action class hoặc triển khai interfce ModelDriven để cung cấp tên class JavaBean cần map. |
| Tag | Struts 1 sử dụng các tag của JSTL, do đó bị hạn chế về chức năng. | Struts 2 sử dụng OGNL và cung cấp nhiều loại tag khác nhau như UI, Control, và Data. Điều này giúp các tag trở nên đa dụng và dễ sử dụng hơn. |
| Xác thực dữ liệu | Struts 1 hỗ trợ xác thực thông qua việc triển khai thủ công phương thức validate(). | Struts 2 hỗ trợ cả xác thực thủ công lẫn tích hợp với các framework xác thực. |
| Hiển thị View | Struts 1 sử dụng công nghệ JSP tiêu chuẩn để hiển thị giá trị từ các bean lên view. | Struts 2 sử dụng ValueStack để lưu các tham số và thuộc tính của request. Ta có thể dùng OGNL và các tag của Struts 2 để truy cập các giá trị này. |
| Mô hình hỗ trợ | Việc thiết kế module trong Struts 1 rất phức tạp, tương đương như những dự án riêng biệt. | Struts 2 cung cấp cấu hình namespace (không gian tên) cho các gói, giúp việc tạo các module trở nên dễ dàng hơn. |
Các thành phần cốt lõi của Struts 2 là gì?
Các thành phần chính của Struts 2 là:
- Các class Action
- Các Interceptor
- Result Page, mẫu JSP hoặc FreeMarker
- ValueStack, OGNL và các thư viện Tag

Interceptor trong Struts 2 là gì?
Interceptor là xương sống của Struts 2. Các interceptor chịu trách nhiệm cho hầu hết các công việc xử lý của framework này, chẳng hạn như việc truyền tham số request đến các class Action, cung cấp Servlet API request, response, session cho các class Action, xác thực, hỗ trợ i18n, v.v. ActionInvocation chịu trách nhiệm đóng gói các class Action và interceptor, cũng như kích hoạt chúng theo thứ tự.
Phương thức quan trọng nhất được sử dụng trong ActionInvocation là invoke() Nó có nhiệm vụ theo dõi chuỗi interceptor và gọi interceptor hoặc action tiếp theo. Đây là một trong những ví dụ điển hình nhất về mẫu thiết kế Chain of Responsibility (Chuỗi trách nhiệm) trong các framework Java EE.
Interceptor của Struts 2 sử dụng mẫu thiết kế nào?
Interceptor trong Struts 2 được xây dựng dựa trên thiết kế kiểu Intercepting Filter (Bộ lọc chặn). Việc gọi các interceptor trong một stack (tập hợp các interceptor xử lý theo thứ tự) rất giống với mẫu thiết kế Chain of Responsibility.
Có những cách nào để tạo các class Action trong Struts 2?
Struts 2 cung cấp nhiều cách khác nhau để tạo các class Action:
- Bằng cách triển khai interface Action.
- Sử dụng annotation @Action của Struts 2.
- Bằng cách kế thừa class ActionSupport.
- Bất kỳ class Java thông thường nào có phương thức execute() trả về String đều có thể được cấu hình thành một class Action.
Action và Interceptor của Struts 2 có tính an toàn luồng không?
Các class Action của Struts 2 có tính an toàn luồng vì với mỗi request sẽ có một đối tượng mới được khởi tạo để xử lý nó. Các interceptor của Struts 2 là các class singleton và một luồng mới sẽ được tạo ra để xử lý request. Do đó chúng không có tính an toàn luồng và chúng ta cần viết code cẩn thận để tránh các vấn đề với dữ liệu chia sẻ.
Class nào là Front Controller trong Struts 2?
org.apache.Struts 2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter là class Front Controller trong Struts 2 và mọi quá trình xử lý request đều bắt đầu từ class này. Các phiên bản trước của Struts 2 sử dụng org.apache.Struts 2.dispatcher.FilterDispatcher làm class Front Controller.
Lợi ích của Interceptor trong Struts 2 là gì?
Một số lợi ích của interceptor là:
- Interceptor đóng vai trò quan trọng trong việc đạt được mức độ tách biệt trách nhiệm (separation of concerns) cao.
- Các interceptor của Struts 2 có thể cấu hình được, và ta có thể cấu hình nó cho bất kỳ action nào ta muốn.
- Ta có thể tạo các interceptor tùy chỉnh của riêng mình để thực hiện các tác vụ chung như ghi log tham số request, xác thực, v.v. Điều này giúp chúng ta xử lý các tác vụ hay gặp tại một nơi duy nhất, giúp giảm công sức bảo trì code.
- Ta có thể tạo các interceptor stack để sử dụng với các action khác nhau.
ValueStack và OGNL là gì?
ValueStack là khu vực lưu trữ nơi dữ liệu ứng dụng được Struts 2 dùng để xử lý các request từ client. Dữ liệu được lưu trong các đối tượng ActionContext sử dụng ThreadLocal để chứa các giá trị cụ thể cho từng luồng request.
Object-Graph Navigation Language (OGNL) là một ngôn ngữ biểu thức (Expression language) mạnh mẽ được sử dụng để thao tác dữ liệu được lưu trên ValueStack. Như bạn có thể thấy trong sơ đồ kiến trúc, cả interceptor và result page đều có thể truy cập dữ liệu được lưu trên ValueStack bằng OGNL.
Kể tên một số annotation hữu ích được giới thiệu trong Struts 2?
Một số annotation quan trọng được thêm vào trong Struts 2 là:
- @Action để tạo class action
- @Actions để cấu hình một class cho nhiều action
- @Namespace và @Namespaces để tạo các module khác nhau
- @Result cho các result page
- @ResultPath để cấu hình vị trí của result page
Nói về một số hằng số quan trọng của Struts 2 mà bạn đã sử dụng?
Một số hằng số quan trọng của Struts 2 bao gồm:
struts.devModeđể chạy ứng dụng ở chế độ development. Nó sẽ tải lại các thuộc tính file và cung cấp thêm tính năng ghi log và debug. Chế độ rất hữu ích khi phát triển ứng dụng nhưng chúng ta nên tắt nó khi đưa code lên môi trường production.struts.convention.result.pathđể cấu hình vị trí của các result page. Mặc định Struts 2 tìm kiếm result page tại{WEBAPP-ROOT}/{Namespace}/và ta có thể thay đổi vị trí đó bằng hằng số này.struts.custom.i18n.resourcesđể định nghĩa gói resource toàn cầu cho việc hỗ trợ i18n.struts.action.extensionđể cấu hình hậu tố URL cho ứng dụng Struts 2. Hậu tố mặc định là.actionnhưng đôi khi chúng ta có thể muốn đổi nó thành.dohoặc một giá trị khác.
Ta có thể thiết lập các hằng trên trong file struts.xml như bên dưới:
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,do"></constant>
<constant name="struts.custom.i18n.resources" value="global"></constant>
<constant name="struts.convention.result.path" value="/"></constant>
Công dụng của namespace trong việc ánh xạ action của Struts 2 là gì?
Cấu hình của namespace (không gian tên) trong Struts 2 cho phép chúng ta tạo các module một cách dễ dàng. Ta có thể sử dụng chúng để phân tách các class action dựa trên chức năng, ví dụ như admin, user, customer, v.v.
Interceptor nào chịu trách nhiệm map các tham số request vào các thuộc tính Java Bean của class Action?
Interceptor com.opensymphony.xwork2.interceptor.ParametersInterceptor có trách nhiệm map các tham số request vào các thuộc tính Java Bean của class Action. Nó được cấu hình trong package struts-default với tên là “params”. Đây là một phần của các interceptor stack basicStack và defaultStack.
Interceptor nào chịu trách nhiệm hỗ trợ i18n?
Interceptor com.opensymphony.xwork2.interceptor.I18nInterceptor chịu trách nhiệm hỗ trợ i18n trong các ứng dụng Struts 2. Nó này được cấu hình trong package struts-default với tên là “i18n” và là một phần của i18nStack và defaultStack.
Sự khác biệt giữa việc sử dụng interface Action và class ActionSupport cho các class Action là gì? Bạn sẽ ưu tiên dùng cái nào?
Chúng ta có thể dùng interface Action để tạo các class Action. Interface này chỉ có một phương thức execute() mà ta cần phải triển khai. Lợi ích duy nhất của việc chọn interface này là nó chứa một số hằng số mà ta có thể dùng cho các result page, đó là SUCCESS, ERROR, NONE, INPUT và LOGIN.
Class ActionSupport là một bản triển khai mặc định của interface Action và nó cũng triển khai các interface liên quan đến xác thực dữ liệu và hỗ trợ i18n. Class ActionSupport triển khai các interface Action, Validateable, ValidationAware, TextProvider và LocaleProvider. Ta có thể ghi đè phương thức validate() của class ActionSupport để thêm logic xác thực ở cấp độ field vào các class Action của mình.
Tùy thuộc vào yêu cầu, ta có thể sử dụng bất kỳ cách tiếp cận nào để tạo class Action trong Struts 2. Tuy vậy, class ActionSupport thường được ưu tiên hơn vì nó giúp viết logic xác thực và i18n trong các class Action một cách dễ dàng hơn.
Làm thế nào để lấy các đối tượng Servlet API như Request, Response, HttpSession trong các class Action?
Các class Action của Struts 2 không cung cấp quyền truy cập trực tiếp vào các thành phần Servlet API như Request, Response và Session. Tuy nhiên, đôi khi chúng ta cần truy cập chúng trong các class Action, ví dụ như để kiểm tra phương thức HTTP hoặc đặt cookie trong response.
Đó là lý do tại sao Struts 2 API cung cấp một loạt các interface *Aware mà chúng ta có thể triển khai để truy cập các đối tượng này. Struts 2 API sử dụng dependency injection (kỹ thuật cung cấp đối tượng phụ thuộc từ bên ngoài) để thêm các thành phần Servlet API vào các class Action. Một số interface Aware quan trọng là SessionAware, ApplicationAware, ServletRequestAware, và ServletResponseAware.
Công dụng của interceptor execAndWait là gì?
Struts 2 cung cấp interceptor execAndWait cho các class Action chạy trong thời gian dài. Ta có thể sử dụng nó để trả về một trang phản hồi tạm thời cho client. Khi quá trình xử lý hoàn tất, phản hồi cuối cùng sẽ được trả về cho client. Interceptor này được định nghĩa trong package struts-default và được triển khai trong class ExecuteAndWaitInterceptor.
Công dụng của interceptor token trong Struts 2 là gì?
Một trong những vấn đề lớn với các ứng dụng web là việc gửi cùng một form hai lần. Nếu không được xử lý, việc này có thể dẫn đến các sai sót như tính phí khách hàng hoặc cập nhật giá trị cơ sở dữ liệu hai lần.
Ta có thể sử dụng interceptor token để giải quyết vấn đề này. Tuy được định nghĩa trong package struts-default, nó không phải là một phần của bất kỳ interceptor stack nào. Vì vậy chúng ta cần phải thêm nó vào các class Action của mình một cách thủ công.
Làm thế nào để tích hợp log4j vào ứng dụng Struts 2?
Struts 2 cung cấp khả năng tích hợp dễ dàng với log4j API cho mục đích ghi log. Tất cả những gì chúng ta cần làm là đặt file cấu hình log4j trong thư mục WEB-INF/classes.
Các loại tag khác nhau của Struts 2 là gì? Làm thế nào để sử dụng chúng?
Struts 2 cung cấp rất nhiều tag tùy chỉnh mà ta có thể sử dụng trong các result page để tạo view cho client request. Các tag này được chia thành ba loại chính: Data tag, Control tag và UI tag. Ta có thể sử dụng các tag này bằng cách thêm chúng vào các trang JSP bằng chỉ thị taglib.
<%@ taglib uri="/struts-tags" prefix="s" %>
Một số Data tag quan trọng là property, set, push, bean, action, include, i18n và text. Control tag được sử dụng để thao tác và điều hướng dữ liệu từ một collection. Một số Control tag quan trọng là if-elseif-else, iterator, append, merge, sort, subset và generator. UI tag của Struts 2 được sử dụng để tạo mã HTML, binding dữ liệu form HTML với các thuộc tính của class action, chuyển đổi kiểu, xác thực và hỗ trợ i18n. Một số UI tag quan trọng là form, textfield, password, textarea, checkbox, select, radio và submit.
Custom Type Converter trong Struts 2 là gì?
Struts 2 hỗ trợ ngôn ngữ biểu thức OGNL. Nó thực hiện hai công việc quan trọng là truyền dữ liệu và chuyển đổi kiểu. OGNL rất linh hoạt và ta có thể dễ dàng mở rộng nó để tạo ra class converter tùy chỉnh của riêng mình.
Việc tạo và cấu hình một class custom type converter (bộ chuyển đổi kiểu tùy chỉnh) rất dễ dàng. Bước đầu tiên là xác định định dạng đầu vào cho class tùy chỉnh. Bước thứ hai là triển khai class chuyển đổi. Các class này phải triển khai interface com.opensymphony.xwork2.conversion.TypeConverter.
Vì trong ứng dụng web, chúng ta luôn nhận request và gửi response dưới dạng String, Struts 2 API cung cấp một triển khai mặc định của interface TypeConverter là StrutsTypeConverter. Nó chứa hai phương thức trừu tượng: convertFromString để chuyển đổi từ String sang Object và convertToString để chuyển đổi từ Object sang String.
Làm thế nào để tự viết một interceptor và map nó cho action?
Ta có thể triển khai interface com.opensymphony.xwork2.interceptor.Interceptor để tạo interceptor của riêng mình. Khi đã tạo xong class interceptor, ta cần định nghĩa nó trong file struts.xml trong package mà ta muốn sử dụng.
Ta cũng có thể tạo một interceptor stack với interceptor tự tạo này với các interceptor của defaultStack. Sau đó, ta có thể cấu hình nó cho các class Action nơi ta muốn sử dụng. Một trong những ví dụ điển hình nhất về việc sử dụng interceptor tùy chỉnh là để xác thực session.
Vòng đời của một interceptor là gì?
Interface Interceptor định nghĩa ba phương thức: init(), destroy() và intercept(). init() và destroy() là các phương thức vòng đời của một interceptor.
Interceptor là các class Singleton và Struts 2 khởi tạo một luồng mới để xử lý mỗi request. Phương thức init() được gọi khi một cá thể của interceptor được tạo ra và ta có thể khởi tạo bất kỳ tài nguyên nào trong phương thức này. Phương thức destroy() được gọi khi ứng dụng dừng chạy và ta có thể giải phóng bất kỳ tài nguyên nào trong phương thức này. intercept() là phương thức được gọi mỗi khi request của client đi qua interceptor.
Interceptor stack là gì?
Một interceptor stack giúp chúng ta nhóm nhiều interceptor lại với nhau trong một package để sử dụng sau này. Package struts-default tạo ra một số interceptor stack được sử dụng thường xuyên nhất: basicStack và defaultStack. Ta có thể tạo interceptor stack của riêng mình ở đầu package và sau đó cấu hình các class Action để sử dụng nó.
Package struts-default là gì và lợi ích của nó là gì?
struts-default là một package trừu tượng định nghĩa tất cả các interceptor của Struts 2 và các interceptor stack thường được sử dụng. Bạn nên kế thừa package này khi cấu hình package ứng dụng của mình để tránh phải cấu hình lại các interceptor. Nó được cung cấp để giúp các lập trình viên loại bỏ một số công việc không cần thiết là cấu hình interceptor và result page trong ứng dụng.
Hậu tố mặc định cho action URI của Struts 2 là gì và làm thế nào để thay đổi nó?
Hậu tố URI mặc định cho action của Struts 2 là .action, trong khi hậu tố mặc định của Struts 1 là .do. Ta có thể thay đổi hậu tố này bằng cách định nghĩa giá trị hằng số struts.action.extension trong file cấu hình Struts 2.
<constant name="struts.action.extension" value="action,do"></constant>
Vị trí mặc định của result page là ở đâu và làm thế nào để thay đổi nó?
Mặc định, Struts 2 tìm kiếm các result page trong thư mục {WEBAPP-ROOT}/{Namespace}/. Khi muốn giữ các result page ở một vị trí khác, ta có thể cung cấp giá trị hằng số struts.convention.result.path trong file cấu hình Struts 2 để thay đổi vị trí của chúng. Một cách khác là sử dụng annotation @ResultPath trong các class Action để cung cấp thẳng vị trí của result page.
Làm thế nào để tải file lên trong ứng dụng Struts 2?
Tải file lên (upload) là một trong những tác vụ phổ biến trong ứng dụng web. Struts 2 cung cấp hỗ trợ tích hợp sẵn cho việc này thông qua FileUploadInterceptor. Interceptor này được cấu hình trong package struts-default và cung cấp các tùy chọn để đặt kích thước tối đa của file và các loại file có thể được tải lên server.
Các nguyên tắc nên làm theo phát triển ứng dụng Struts 2 là gì?
Một số nguyên tắc nên cân nhắc khi phát triển ứng dụng Struts 2 là:
- Luôn kế thừa package struts-default khi tạo package của bạn để tránh lặp code khi cấu hình interceptor.
- Đối với các tác vụ chung cho toàn ứng dụng (chẳng hạn như ghi log tham số request), hãy cố gắng sử dụng interceptor.
- Luôn giữ các thuộc tính Java bean của class Action trong một bean riêng biệt để tái sử dụng code và triển khai interface ModelDriven.
- Nếu có một custom interceptor sẽ được sử dụng trong nhiều action, hãy tạo một interceptor stack cho nó và sau đó sử dụng stack đó.
- Cố gắng chia ứng dụng của bạn thành các module khác nhau với cấu hình namespace dựa trên các khu vực chức năng.
- Cố gắng sử dụng các tag của Struts 2 trong các result page để làm code rõ ràng hơn. Nếu cần, hãy tạo các type converter của riêng bạn.
- Sử dụng chế độ development (phát triển) để phát triển nhanh hơn, tuy nhiên hãy đảm bảo code production không chạy ở chế độ đó.
- Sử dụng hỗ trợ i18n của Struts 2 cho các resource bundle (tập hợp các tài nguyên hỗ trợ bản địa hóa ứng dụng).
- Struts 2 cung cấp nhiều nơi để bạn có thể đặt các resource bundle, nhưng hãy cố gắng giữ một resource bundle toàn cục và một cho class action để tránh nhầm lẫn.
- Package struts-default cấu hình tất cả các interceptor và tạo ra các interceptor stack khác nhau. Cố gắng chỉ sử dụng những gì cần thiết. Ví dụ, nếu bạn không có yêu cầu về bản địa hóa, bạn có thể bỏ qua interceptor i18n.
Làm thế nào để xử lý exception được tạo ra bởi ứng dụng trong Struts 2?
Struts 2 cung cấp một framework rất mạnh để xử lý exception. Ta có thể chỉ định các kết quả toàn cục trong các package và sau đó map các exception cụ thể tới các result page này. Việc map exception có thể được thực hiện ở cấp độ package toàn cục cũng như ở cấp độ action.
Bạn nên có các result page cho exception để cung cấp một số thông tin cho người dùng khi có một lỗi bất ngờ xảy ra mà không được ứng dụng xử lý. Đây là một cấu hình mẫu trong fiel struts.xml:
<package name="user" namespace="/" extends="struts-default">
<global-results>
<result name="exception">/exception.jsp</result>
<result name="runtime_exception">/runtime_exception.jsp</result>
<result name="error">/error.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="exception"></exception-mapping>
<exception-mapping exception="java.lang.Error" result="error"></exception-mapping>
<exception-mapping exception="java.lang.RuntimeException" result="runtime_exception"></exception-mapping>
</global-exception-mappings>
<action name="myaction" class="com.journaldev.struts2.exception.MyAction">
</action>
<action name="myspecialaction" class="com.journaldev.struts2.exception.MySpecialAction">
<exception-mapping exception="java.io.IOException" result="login"></exception-mapping>
<result name="login">/error.jsp</result>
</action>
</package>
Tổng kết
Chúng tôi tin rằng với những giải thích cho các câu hỏi phỏng vấn Struts 2 trong bài này, bạn sẽ có đủ kiến thức và kỹ năng để tự tin cho những thử thách sắp tới. Nếu có bất kỳ câu hỏi nào – hoặc muốn chúng tôi trả lời thêm, đừng ngần ngại – để lại phản hồi cho chúng tôi ở phần bình luận bên dưới.