在JSP 2中避免使用Java代码的方法
技术背景
自2001年标签库(如JSTL)和EL表达式诞生以来,JSP中使用脚本片段(<%%>
)的方式就不被推荐。脚本片段存在诸多缺点,如不可复用、不可抽象、无法利用继承和组合、调试困难、无法进行单元测试以及维护成本高等。Oracle也在JSP编码规范中建议,当标签类能实现相同功能时,应避免使用脚本片段。
实现步骤
1. 使用过滤器处理通用逻辑
若要在每个请求中调用相同的Java代码,如检查用户是否登录,可实现一个过滤器,并在doFilter()
方法中编写相应代码。
1 2 3 4 5 6 7
| public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { if (((HttpServletRequest) request).getSession().getAttribute("user") == null) { ((HttpServletResponse) response).sendRedirect("login"); } else { chain.doFilter(request, response); } }
|
2. 使用Servlet处理GET请求
若要调用Java代码处理GET请求,如从数据库预加载列表以在表格中显示,可实现一个Servlet,并在doGet()
方法中编写代码。
1 2 3 4 5 6 7 8 9
| protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { List<Product> products = productService.list(); request.setAttribute("products", products); request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response); } catch (SQLException e) { throw new ServletException("Retrieving products failed!", e); } }
|
3. 使用Servlet处理POST请求
若要调用Java代码处理POST请求,如收集HTML表单数据并进行业务处理,可实现一个Servlet,并在doPost()
方法中编写代码。
1 2 3 4 5 6 7 8 9 10 11 12 13
| protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); User user = userService.find(username, password);
if (user != null) { request.getSession().setAttribute("user", user); response.sendRedirect("home"); } else { request.setAttribute("message", "Unknown username/password. Please retry."); request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response); } }
|
4. 使用Servlet实现MVC前端控制器模式
若要控制请求和响应的执行计划及目标,可根据MVC的前端控制器模式实现一个Servlet。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { Action action = ActionFactory.getAction(request); String view = action.execute(request, response);
if (view.equals(request.getPathInfo().substring(1)) { request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response); } else { response.sendRedirect(view); } } catch (Exception e) { throw new ServletException("Executing action failed.", e); } }
|
5. 使用标签库控制JSP页面流程
若要在JSP页面中控制流程,可使用现有的流程控制标签库,如JSTL core。
1 2 3 4 5 6 7 8 9 10 11
| <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> ... <table> <c:forEach items="${products}" var="product"> <tr> <td>${product.name}</td> <td>${product.description}</td> <td>${product.price}</td> </tr> </c:forEach> </table>
|
6. 使用EL表达式访问和显示数据
若要在JSP页面中访问和显示“后端”数据,可使用EL表达式。
1
| <input type="text" name="foo" value="${param.foo}" />
|
7. 定义EL函数调用实用Java代码
若要在JSP页面中直接调用实用Java代码(通常是public static
方法),可将其定义为EL函数。
1 2 3
| <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> ... <input type="text" name="foo" value="${fn:escapeXml(param.foo)}" />
|
最佳实践
- 使用MVC架构:将业务逻辑、数据和视图分离,使代码更易于维护和扩展。
- 禁用脚本片段:在
web.xml
中添加以下配置,防止开发人员使用脚本片段。
1 2 3 4 5 6
| <jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <scripting-invalid>true</scripting-invalid> </jsp-property-group> </jsp-config>
|
- 使用模板引擎:如Facelets、Freemarker、Velocity等,可更清晰地分离业务逻辑和展示逻辑。
常见问题
1. 为什么不推荐使用脚本片段?
脚本片段存在不可复用、不可抽象、调试困难、无法单元测试等缺点,会导致代码难以维护。
2. EL表达式在JSP中使用有什么问题?
EL表达式在某些复杂场景下可能无法满足需求,且与JSP结合使用时可能存在兼容性问题。因此,可考虑使用Facelets替代JSP。
3. 如何创建自定义标签?
可参考相关文档,定义标签库描述符(TLD)文件,并实现相应的标签处理类。也可使用JSP 2.0的“标签文件”功能,创建.tag
文件。例如:
1 2 3 4 5
| /WEB-INF/tags/html/label.tag <%@tag description="Rensders a label with required css class" pageEncoding="UTF-8"%> <%@attribute name="name" required="true" description="The label"%>
<label class="control-label control-default" id="${name}Label">${name}</label>
|
使用时:
1 2
| <%@ taglib prefix="h" tagdir="/WEB-INF/tags/html"%> <h:label name="customer name" />
|