javaWeb核心05-Filter&Listener&Ajax(Axios)&json
'# JavaWeb核心05-Filter&Listener&Ajax(Axios)&json
一、背景与问题
在构建现代Web应用时,Filter、Listener、Ajax和JSON是核心组件。Filter用于控制请求流程,Listener用于监听应用生命周期,Ajax实现异步交互,JSON作为数据交换格式。然而,开发者常遇到以下问题:
- Filter配置错误导致请求拦截失效
- Listener未正确初始化资源导致服务异常
- Ajax跨域问题导致请求失败
- JSON序列化/反序列化错误引发数据丢失
这些问题在实际项目中频繁出现,需要深入理解底层原理和最佳实践。
二、基本原理
1. Filter原理
Filter是Servlet规范定义的过滤器,通过doFilter()方法控制请求流程。其核心是请求过滤链,每个Filter依次执行,最终传递给Servlet。
2. Listener原理
Listener是Servlet规范定义的事件监听器,通过实现ServletContextListener接口,可以在应用启动/关闭时执行初始化和销毁逻辑。
3. Ajax原理
Ajax通过XMLHttpRequest或Fetch API实现异步请求,结合JSON格式数据传输,无需刷新页面即可更新内容。Axios作为封装库,简化了请求流程。
4. JSON原理
JSON(JavaScript Object Notation)是一种轻量级数据交换格式,通过键值对结构表示数据。其核心是序列化/反序列化机制,支持跨平台数据传输。
三、环境准备
// pom.xml依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency># 前端依赖
npm install axios四、核心实现
1. Filter实现示例
// AuthFilter.java
public class AuthFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
// 初始化资源
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// 检查认证信息
if (checkAuth(req)) {
chain.doFilter(request, response);
} else {
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
res.setContentType("application/json");
try (PrintWriter writer = res.getWriter()) {
writer.write("{\"error\": \"Unauthorized\"}");
}
}
}
@Override
public void destroy() {
// 释放资源
}
private boolean checkAuth(HttpServletRequest request) {
String token = request.getHeader("Authorization");
return "valid_token".equals(token);
}
}关键解释:
init()方法用于初始化资源(如数据库连接)doFilter()方法控制请求流程,检查认证信息checkAuth()方法实现具体的认证逻辑- 通过
FilterChain将请求传递给后续处理组件
2. Listener实现示例
// AppContextListener.java
public class AppContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// 初始化应用上下文
ServletContext context = sce.getServletContext();
context.setAttribute("appConfig", loadAppConfig());
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// 释放资源
ServletContext context = sce.getServletContext();
context.removeAttribute("appConfig");
}
private Map<String, String> loadAppConfig() {
// 加载配置文件
return new HashMap<>();
}
}关键解释:
contextInitialized()在应用启动时执行contextDestroyed()在应用关闭时执行- 通过
ServletContext共享应用级资源 - 适用于初始化数据库连接池、缓存等资源
3. Ajax实现示例
// frontend.js
async function login(username, password) {
try {
const response = await axios.post('/api/login', {
username,
password
});
console.log('登录成功:', response.data);
return response.data;
} catch (error) {
console.error('登录失败:', error.response?.data || error.message);
throw error;
}
}关键解释:
- 使用
axios.post()发送POST请求 - 异步处理响应,避免阻塞主线程
- 错误处理包含服务器响应和网络错误
- 通过
response.data获取JSON格式的返回数据
五、完整案例
1. 用户登录系统
后端接口:
// LoginServlet.java
@WebServlet("/api/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("admin".equals(username) && "123456".equals(password)) {
response.setContentType("application/json");
response.getWriter().write("{\"token\": \"valid_token\"}");
} else {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"error\": \"Invalid credentials\"}");
}
}
}前端页面:
<!-- login.html -->
<!DOCTYPE html>
<html>
<head>
<title>登录</title>
</head>
<body>
<h2>用户登录</h2>
<form id="loginForm">
用户名: <input type="text" id="username" name="username"><br>
密码: <input type="password" id="password" name="password"><br>
<button type="submit">登录</button>
</form>
<div id="message"></div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
document.getElementById('loginForm').addEventListener('submit', async function(e) {
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const message = document.getElementById('message');
try {
const result = await login(username, password);
message.textContent = '登录成功';
console.log('登录成功:', result);
} catch (error) {
message.textContent = '登录失败';
console.error('登录失败:', error);
}
});
</script>
</body>
</html>关键流程:
- 用户提交表单
- 前端调用
login()函数 - 通过Axios发送POST请求
- 后端验证凭据
- 返回JSON响应
- 前端处理响应并显示结果
六、源码解析
1. Filter源码分析
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// 检查认证信息
if (checkAuth(request)) {
// 将请求传递给后续过滤器
chain.doFilter(request, response);
} else {
// 拒绝请求
response.getWriter().write("{\"error\": \"Unauthorized\"}");
}
}关键点:
chain.doFilter()是核心方法,控制请求流程- 可以在多个Filter中依次处理请求
- 需要确保
FilterChain的正确传递
2. Listener源码分析
public void contextInitialized(ServletContextEvent sce) {
// 加载配置文件
Properties props = new Properties();
try (InputStream input = getClass().getClassLoader().getResourceAsStream("app.properties")) {
if (input != null) {
props.load(input);
}
} catch (IOException e) {
e.printStackTrace();
}
// 存储到ServletContext
sce.getServletContext().setAttribute("config", props);
}关键点:
- 通过
ServletContext共享配置 - 可以访问
ServletConfig获取初始化参数 - 需要处理可能的异常情况
七、进阶使用
1. Filter的高级用法
// 自定义Filter链
public class ChainFilter implements Filter {
private FilterChain chain;
@Override
public void init(FilterConfig config) throws ServletException {
// 初始化链式处理
}
@Override
public void doFilter(ServletRequest request, ServletResponse response) {
// 第一个Filter处理
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 清理链式处理
}
}应用场景:
- 多级认证体系
- 请求日志记录
- 动态路由处理
2. Ajax的高级用法
// 带超时和重试的Ajax请求
async function retryRequest(url, retries = 3, timeout = 5000) {
for (let i = 0; i < retries; i++) {
try {
const response = await axios.get(url, { timeout });
return response.data;
} catch (error) {
if (error.response && error.response.status === 503) {
// 服务不可用,重试
await new Promise(resolve => setTimeout(resolve, 1000));
} else {
throw error;
}
}
}
throw new Error('请求失败');
}应用场景:
- 网络不稳定时的重试机制
- 超时处理
- 错误重传
八、性能与工程实践
1. 性能优化
Filter优化:
- 减少不必要的认证检查
- 缓存常用认证结果
- 使用异步处理非关键认证
Ajax优化:
- 启用HTTP/2协议
- 使用Gzip压缩
- 合并多个请求为一个
- 使用长连接(WebSocket)
JSON优化:
- 使用Schema校验
- 压缩传输数据
- 使用二进制替代文本(如Protobuf)
2. 安全风险
Filter安全风险:
- 未校验请求来源导致CSRF攻击
- 未加密敏感数据传输
- 未处理异常导致信息泄露
Ajax安全风险:
- 未处理CORS头导致跨域漏洞
- 未验证输入数据导致XSS
- 未使用HTTPS传输敏感数据
JSON安全风险:
- 未进行类型校验导致注入攻击
- 未处理特殊字符导致序列化错误
- 未使用加密算法导致数据篡改
九、常见问题与踩坑
1. 常见错误
错误示例:
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
chain.doFilter(req, res); // 忘记检查认证
}
}问题分析:
- 直接调用
chain.doFilter()导致所有请求通过 - 缺乏认证检查,存在安全风险
解决方法:
- 增加认证检查逻辑
- 使用
if-else控制流程 - 添加异常处理
2. 踩坑案例
跨域问题:
// 前端请求
axios.get('http://localhost:8080/api/data');错误日志:
Access to XMLHttpRequest at 'http://localhost:8080/api/data' from origin 'http://localhost:3000' has been blocked by CORS policy解决方案:
- 后端配置CORS头
- 使用代理服务器
- 使用
axios的withCredentials选项
十、最佳实践
1. Filter使用建议
什么时候使用:
- 需要统一处理请求(如日志、安全、缓存)
- 需要控制请求流程(如认证、限流)
- 需要访问请求上下文信息
什么时候不使用:
- 需要复杂的业务逻辑处理
- 需要高性能计算(建议使用Servlet)
- 需要细粒度的控制(建议使用Servlet)
2. Listener使用建议
什么时候使用:
- 需要初始化应用级资源
- 需要处理应用生命周期事件
- 需要共享配置信息
什么时候不使用:
- 需要频繁更新资源
- 需要实时处理请求
- 需要高并发处理(建议使用Spring的Bean生命周期)
3. Ajax使用建议
什么时候使用:
- 需要动态更新页面内容
- 需要实时数据处理
- 需要提升用户体验
什么时候不使用:
- 需要大量数据传输
- 需要复杂交互(建议使用WebSocket)
- 需要高并发处理(建议使用长连接)
十一、总结
Filter、Listener、Ajax和JSON是JavaWeb开发的核心组件,理解其原理和最佳实践对构建可靠系统至关重要。通过合理使用Filter控制请求流程,利用Listener管理生命周期,结合Ajax实现异步交互,以及采用JSON进行数据交换,可以构建高性能、安全的Web应用。
关键注意事项:
- Filter需要谨慎处理认证和异常
- Listener要合理管理资源生命周期
- Ajax要处理跨域和安全性问题
- JSON要确保数据的准确性和安全性
在实际开发中,应根据具体需求选择合适的技术组合,避免过度设计,同时关注性能和安全性。通过不断学习和实践,可以更好地掌握这些核心技术,提升开发效率和系统质量。
评论已关闭