一、Servlet 核心原理与高级特性
1. 什么是 Servlet?
- 🌐 Servlet 是一种运行在服务器端的 Java 技术,用于处理 HTTP 请求并动态生成 Web 内容。它是 Java EE 的一部分,运行在 Servlet 容器(如 Apache Tomcat、Jetty 等)中。
- Servlet 提供了一种标准化的方式来开发基于 Web 的应用程序,支持多种 HTTP 方法(如 GET、POST、PUT、DELETE 等)。
2. Servlet 的核心原理
- 📥 当客户端通过浏览器发送请求时,Servlet 容器会将该请求封装为
HttpServletRequest
对象,并将响应封装为HttpServletResponse
对象。 - 🔄 Servlet 的生命周期由容器管理,主要包括以下几个阶段:
- 加载和实例化: 容器加载 Servlet 类并创建其实例。
- 初始化 (
init
): 容器调用 Servlet 的init()
方法,完成初始化工作。 - 服务 (
service
): 容器调用service()
方法,根据请求类型调用相应的处理方法(如doGet()
或doPost()
)。 - 销毁 (
destroy
): 当 Servlet 不再需要时,容器调用destroy()
方法释放资源。
3. Servlet 的配置方式
-
传统方式:
web.xml
配置
在早期的 Java Web 开发中,Servlet 的映射和初始化参数通常通过web.xml
文件进行配置。例如:<servlet> <servlet-name>HelloWorldServlet</servlet-name> <servlet-class>com.example.HelloWorldServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloWorldServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
注解方式(推荐)
自从 Java EE 6 引入了@WebServlet
注解后,开发者可以直接在 Servlet 类上使用注解来定义 URL 映射,简化了配置过程。例如:@WebServlet("/hello") public class HelloWorldServlet extends HttpServlet { // Servlet 逻辑代码 }
4. 过滤器(Filter)与监听器(Listener)
-
过滤器(Filter)
过滤器用于拦截和处理请求或响应,常用于日志记录、权限检查、数据压缩等场景。以下是一个简单的过滤器示例:import jakarta.servlet.*; import java.io.IOException; public class LoggingFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("LoggingFilter 初始化完成!"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("请求被 LoggingFilter 拦截!"); chain.doFilter(request, response); // 继续传递请求 System.out.println("响应被 LoggingFilter 拦截!"); } @Override public void destroy() { System.out.println("LoggingFilter 被销毁!"); } }
-
监听器(Listener)
监听器用于监听 Web 应用程序中的事件,例如应用启动、会话创建或销毁等。以下是一个监听会话创建的示例:import jakarta.servlet.http.HttpSessionEvent; import jakarta.servlet.http.HttpSessionListener; public class SessionCounterListener implements HttpSessionListener { private static int activeSessions = 0; @Override public void sessionCreated(HttpSessionEvent se) { activeSessions++; System.out.println("当前活动会话数:" + activeSessions); } @Override public void sessionDestroyed(HttpSessionEvent se) { activeSessions--; System.out.println("当前活动会话数:" + activeSessions); } }
5. 异步处理(Asynchronous Processing)
-
在现代 Web 应用中,高并发场景下同步处理请求可能导致性能瓶颈。Servlet 3.0 引入了异步处理功能,允许 Servlet 在非阻塞模式下处理请求。
-
启用异步支持:
- 在
web.xml
中配置:<servlet> <async-supported>true</async-supported> </servlet>
- 或在注解中声明:
@WebServlet(urlPatterns = "/async", asyncSupported = true) public class AsyncServlet extends HttpServlet { // 异步逻辑代码 }
- 在
-
异步示例:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); // 开启异步上下文 AsyncContext asyncContext = request.startAsync(); // 模拟耗时任务 new Thread(() -> { try { Thread.sleep(5000); // 模拟长时间操作 PrintWriter out = asyncContext.getResponse().getWriter(); out.println("<h1>异步处理完成!</h1>"); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { asyncContext.complete(); // 完成异步操作 } }).start(); }
6. WebSocket 支持
- WebSocket 是一种全双工通信协议,适用于实时应用场景(如聊天室、在线游戏等)。Servlet 3.1 引入了对 WebSocket 的支持。
- WebSocket 示例:
import jakarta.websocket.OnMessage; import jakarta.websocket.Session; import jakarta.websocket.server.ServerEndpoint; @ServerEndpoint("/chat") public class ChatWebSocket { @OnMessage public String onMessage(String message, Session session) { return "服务器收到消息:" + message; } }
7. 文件上传/下载
-
文件上传: 使用 Apache Commons FileUpload 或 Servlet 提供的
Part
接口处理文件上传。protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Part filePart = request.getPart("file"); // 获取上传文件 String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // 将文件保存到指定路径 String uploadPath = getServletContext().getRealPath("/") + "uploads/" + fileName; Files.copy(filePart.getInputStream(), Paths.get(uploadPath), StandardCopyOption.REPLACE_EXISTING); response.getWriter().println("文件上传成功!"); }
-
文件下载: 设置响应头并输出文件内容。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String filePath = getServletContext().getRealPath("/") + "files/example.pdf"; File file = new File(filePath); if (!file.exists()) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在!"); return; } response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "attachment; filename=" + file.getName()); Files.copy(file.toPath(), response.getOutputStream()); }
二、JSP 核心原理与高级特性
1. 什么是 JSP?
- 🌱 JSP(JavaServer Pages)是一种基于 Servlet 的技术,允许开发者将 HTML、CSS、JavaScript 和 Java 代码混合编写,从而简化动态网页的开发过程。
2. JSP 的核心原理
- 🔄 JSP 文件在第一次被请求时会被 JSP 引擎编译为一个 Servlet 源文件(即
.java
文件),然后由 Servlet 容器将其编译为字节码(.class
文件)并执行。 - 📝 编译后的 Servlet 动态生成 HTML 内容,并将其作为响应返回给客户端。
3. JSP 表达式语言(EL)
- JSP 表达式语言(Expression Language,简称 EL)是一种简洁的方式来访问后台数据。例如:
${user.name} <!-- 输出用户名称 --> ${param.username} <!-- 获取请求参数 username -->
4. JSTL(JSP Standard Tag Library)
- JSTL 是一组标准标签库,提供了常用的页面功能,如条件判断、循环、日期格式化等。例如:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <c:if test="${user.admin}"> <p>欢迎管理员!</p> </c:if> <c:forEach var="item" items="${items}"> <p>${item}</p> </c:forEach>
5. JSP 页面间的跳转与包含
-
页面跳转: 使用
response.sendRedirect()
或<jsp:forward>
标签实现页面跳转。例如:<% response.sendRedirect("login.jsp"); %>
或者:
<jsp:forward page="login.jsp" />
-
页面包含: 使用
<%@ include file="..." %>
或<jsp:include page="..." />
标签将其他页面的内容嵌入当前页面。例如:<%@ include file="header.jsp" %> <jsp:include page="footer.jsp" />
6. JSP 自定义标签库(Custom Tag Libraries)
-
自定义标签库允许开发者创建可重用的 JSP 标签,简化页面开发。例如:
import jakarta.servlet.jsp.JspException; import jakarta.servlet.jsp.tagext.SimpleTagSupport; import java.io.IOException; public class HelloTag extends SimpleTagSupport { private String name; public void setName(String name) { this.name = name; } @Override public void doTag() throws JspException, IOException { getJspContext().getOut().write("Hello, " + name + "!"); } }
配置
taglib.tld
文件后,可以在 JSP 页面中使用自定义标签:<%@ taglib prefix="my" uri="/WEB-INF/tlds/custom.tld" %> <my:hello name="World" />
7. JSP 缓存机制
- JSP 页面可以通过
<%@ page isCacheable="true" %>
启用缓存,减少重复计算。 - 使用
<%@ include file="..." flush="true" %>
可以控制包含页面的缓冲行为。
8. JSP 错误页面处理
- 使用
<%@ page errorPage="error.jsp" %>
指定错误页面。 - 在全局
web.xml
中配置错误页面映射:<error-page> <error-code>404</error-code> <location>/error/404.jsp</location> </error-page>
三、Servlet 和 JSP 的实际应用场景
1. Servlet 的典型应用场景
- 用户登录验证: 使用 Servlet 处理用户提交的登录信息,并验证用户名和密码。
- RESTful API: 使用 Servlet 实现 RESTful 风格的接口,提供 JSON 或 XML 格式的响应。
- 文件上传/下载: 使用 Servlet 处理大文件的上传和下载操作。
- 异步任务处理: 利用 Servlet 的异步功能处理耗时任务,提升系统性能。
2. JSP 的典型应用场景
- 动态页面展示: 使用 JSP 动态生成商品列表、新闻资讯等内容。
- 表单处理: 结合 JSP 和 Servlet,处理复杂的表单提交逻辑。
- 国际化支持: 使用 JSP 的资源绑定功能(ResourceBundle),实现多语言支持。
四、Servlet 和 JSP 的性能优化
1. Servlet 性能优化
- 减少线程竞争: 避免在 Servlet 中使用共享变量。
- 启用缓存: 对于静态资源或频繁访问的数据,使用缓存机制减少数据库查询。
- 异步处理: 利用异步功能提高并发处理能力。
- 合理配置线程池: 根据应用需求调整容器的线程池大小。
2. JSP 性能优化
- 减少内嵌脚本: 使用 JSTL 和 EL 替代 Java 脚本片段。
- 避免复杂逻辑: 将复杂业务逻辑移至 Servlet 或服务层。
- 启用 GZIP 压缩: 在容器中启用 GZIP 压缩,减少传输数据量。
- 使用模板引擎替代 JSP: 在复杂场景下,可以考虑使用 Thymeleaf、Freemarker 等现代模板引擎。
五、Servlet 和 JSP 的安全最佳实践
1. 输入验证
- 防止 SQL 注入和 XSS 攻击,确保所有用户输入经过严格验证。
- 使用框架提供的工具(如 Hibernate Validator)进行输入校验。
2. 会话管理
- 设置会话超时时间,防止会话劫持。
- 使用 HTTPS 加密传输敏感数据。
- 避免在会话中存储敏感信息。
3. 权限控制
- 在
web.xml
中配置安全约束:<security-constraint> <web-resource-collection> <url-pattern>/admin/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint>
4. 防止 CSRF 攻击
- 使用令牌机制(Token)验证请求来源,防止跨站请求伪造攻击。
5. 日志记录
- 记录关键操作日志,便于问题排查和安全审计。