使用JSP 2避免JSP文件中的Java代码
技术背景
在早期的JSP开发中,使用脚本片段(<% %>、<%! %>、<%= %>)嵌入Java代码是常见做法,但这种方式存在诸多弊端。随着JSP 2的出现,标签库(如JSTL)和表达式语言(EL)等技术逐渐成熟,使得避免在JSP文件中直接使用Java代码成为可能。
脚本片段的缺点
- 可复用性差:脚本片段无法复用。
- 可替换性差:无法将脚本片段抽象化。
- 面向对象能力弱:难以利用继承和组合。
- 调试困难:若脚本片段中途抛出异常,页面将显示空白。
- 可测试性差:脚本片段无法进行单元测试。
- 维护成本高:混合、杂乱和重复的代码逻辑需要更多时间维护。
实现步骤
1. 定义过滤器处理通用逻辑
若要在每个请求中调用相同的Java代码(如检查用户是否登录),可实现一个过滤器,并在doFilter()方法中编写相应代码。
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginFilter implements Filter {
@Override
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);
}
}
}
在web.xml中配置过滤器:
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.example.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
2. 使用Servlet处理GET请求
若要调用Java代码处理GET请求(如从数据库预加载列表以在表格中显示),可实现一个Servlet,并在doGet()方法中编写相应代码。
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
@WebServlet("/products")
public class ProductServlet extends HttpServlet {
private ProductService productService = new ProductService();
@Override
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请求(如收集表单数据并进行业务处理),可实现一个Servlet,并在doPost()方法中编写相应代码。
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private UserService userService = new UserService();
@Override
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. 使用MVC的前端控制器模式
若要调用Java代码控制请求和响应的执行计划和目标,可根据MVC的前端控制器模式实现一个Servlet。
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class FrontControllerServlet extends HttpServlet {
@Override
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. 使用JSTL控制JSP页面流程
若要调用Java代码控制JSP页面内的流程,可使用JSTL核心标签库。
<%@ 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表达式访问和显示数据
若要调用Java代码访问和显示JSP页面内的“后端”数据,可使用EL表达式。
<input type="text" name="foo" value="${param.foo}" />
7. 使用EL函数调用实用Java代码
若要在JSP页面中直接调用实用Java代码(通常是公共静态方法),可将其定义为EL函数。
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<input type="text" name="foo" value="${fn:escapeXml(param.foo)}" />
最佳实践
- 遵循MVC模式:将业务逻辑与表示逻辑分离,JSP作为视图层,专注于页面展示。
- 使用标签库和EL表达式:尽量使用JSTL标签库和EL表达式替代脚本片段。
- 禁用脚本片段:在web.xml中添加以下配置,防止开发者使用脚本片段。
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<scripting-invalid>true</scripting-invalid>
</jsp-property-group>
</jsp-config>
常见问题
1. 如何处理不遵循getter/setter模式的Java类?
可将这些类包装在自己的JavaBean类中并使用。
2. 使用JSP替代技术有哪些缺点?
- 学习成本:如JSF、Wicket等框架需要学习新的API和概念。
- 性能问题:某些框架可能会带来额外的性能开销。
- 复杂度:框架可能会增加项目的复杂度。
3. 能否完全避免在JSP中使用Java代码?
理论上可以,但实际开发中可能需要根据具体情况进行权衡。一些特殊情况(如临时计算、特定页面的修复)可能仍需要少量Java代码。