1. 项目概述
作为一名有10年Java Web开发经验的工程师,最近我参与开发了潍坊市旅游景点管理系统。这个项目让我深刻体会到Servlet技术在传统Web开发中的稳定性和可靠性。虽然现在Spring Boot大行其道,但在某些特定场景下,基于Servlet的轻量级架构依然有其独特的优势。
这个系统采用经典的B/S架构,前端使用JSP+JSTL+EL表达式,后端基于Servlet 2.5规范,数据存储采用MySQL 8.0。整个项目从需求分析到上线部署历时3个月,最终实现了景点展示、票务管理、用户评价等核心功能模块。
提示:在Servlet项目中,合理使用Filter和Listener能大幅提升代码的可维护性。比如我们通过Filter实现了统一的权限控制和字符编码设置。
2. 核心架构设计
2.1 技术选型分析
选择Servlet作为后端框架主要基于以下几点考虑:
- 性能考量:Servlet作为Java EE标准,直接运行在Web容器中,没有Spring那样的额外抽象层
- 维护成本:客户技术团队对Servlet更熟悉,后期维护更方便
- 资源限制:项目预算有限,需要轻量级解决方案
java复制// 典型Servlet代码结构示例
public class AttractionServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
AttractionService service = new AttractionServiceImpl();
List<Attraction> list = service.getAllAttractions();
request.setAttribute("attractions", list);
request.getRequestDispatcher("/attractions.jsp").forward(request, response);
}
}
2.2 系统分层架构
系统采用经典的三层架构设计:
| 层级 | 技术实现 | 职责 |
|---|---|---|
| 表现层 | JSP+JSTL+Bootstrap | 数据展示和用户交互 |
| 业务层 | JavaBean+Service | 业务逻辑处理 |
| 数据层 | JDBC+MySQL | 数据持久化 |
数据库连接我们采用了连接池技术,配置在Tomcat的context.xml中:
xml复制<Resource name="jdbc/tourismDB"
auth="Container"
type="javax.sql.DataSource"
maxTotal="100"
maxIdle="30"
maxWaitMillis="10000"
username="admin"
password="123456"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/tourism_db?useSSL=false"/>
3. 核心功能实现
3.1 景点信息管理模块
这个模块的技术难点在于:
- 多条件组合查询的性能优化
- 图片上传和缩略图生成
- 富文本编辑器的集成
我们通过以下方案解决这些问题:
java复制// 分页查询实现示例
public Page<Attraction> queryAttractions(Map<String,String> params, int pageNo, int pageSize) {
StringBuilder sql = new StringBuilder("SELECT * FROM attractions WHERE 1=1");
List<Object> paramsList = new ArrayList<>();
// 动态拼接SQL
if(params.containsKey("name")) {
sql.append(" AND name LIKE ?");
paramsList.add("%"+params.get("name")+"%");
}
if(params.containsKey("type")) {
sql.append(" AND type = ?");
paramsList.add(params.get("type"));
}
// 添加分页
sql.append(" LIMIT ?,?");
paramsList.add((pageNo-1)*pageSize);
paramsList.add(pageSize);
// 执行查询...
}
注意:动态SQL拼接要特别注意SQL注入问题,我们使用了PreparedStatement来预防。
3.2 票务管理模块
票务管理涉及到复杂的业务规则:
- 不同票种的价格策略
- 节假日票价浮动
- 库存实时更新
我们采用数据库事务确保数据一致性:
java复制// 购票事务处理
public boolean purchaseTicket(int userId, int attractionId, int ticketType, int quantity) {
Connection conn = null;
try {
conn = dataSource.getConnection();
conn.setAutoCommit(false);
// 1. 检查库存
Ticket ticket = ticketDao.getById(conn, ticketType);
if(ticket.getStock() < quantity) {
throw new RuntimeException("库存不足");
}
// 2. 扣减库存
ticketDao.updateStock(conn, ticketType, -quantity);
// 3. 创建订单
Order order = new Order(userId, attractionId, ticketType, quantity);
orderDao.create(conn, order);
conn.commit();
return true;
} catch (Exception e) {
if(conn != null) conn.rollback();
throw e;
} finally {
if(conn != null) conn.close();
}
}
4. 性能优化实践
4.1 缓存策略设计
我们实现了两级缓存来提高系统响应速度:
- 页面片段缓存:使用OSCache缓存热门景点列表
- 数据对象缓存:使用HashMap实现简单的应用级缓存
xml复制<!-- oscache.properties配置 -->
cache.memory=true
cache.capacity=1000
cache.algorithm=com.opensymphony.oscache.base.algorithm.LRUCache
cache.unlimited.disk=false
4.2 数据库优化
针对景点表的查询优化:
-
添加适当的索引:
sql复制CREATE INDEX idx_attraction_name ON attractions(name); CREATE INDEX idx_attraction_type ON attractions(type); CREATE INDEX idx_attraction_location ON attractions(latitude, longitude); -
对大文本字段(如景点描述)使用垂直分表
-
定期执行ANALYZE TABLE更新统计信息
5. 安全防护措施
5.1 常见Web攻击防护
我们在Filter中实现了以下安全措施:
| 攻击类型 | 防护措施 | 实现方式 |
|---|---|---|
| XSS | 输出编码 | JSTL <c:out>标签 |
| CSRF | Token验证 | 表单隐藏字段 |
| SQL注入 | 预编译语句 | PreparedStatement |
| 暴力破解 | 登录限制 | 记录失败次数 |
java复制// CSRF防护Filter示例
public class CsrfFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if("POST".equalsIgnoreCase(request.getMethod())) {
String sessionToken = (String) request.getSession().getAttribute("CSRF_TOKEN");
String requestToken = request.getParameter("csrfToken");
if(sessionToken == null || !sessionToken.equals(requestToken)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
}
chain.doFilter(req, res);
}
}
6. 部署与监控
6.1 Tomcat优化配置
我们在server.xml中做了以下优化:
xml复制<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
maxThreads="500"
minSpareThreads="30"
acceptCount="100"
enableLookups="false"
compression="on"
compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/css,application/json"/>
6.2 日志监控方案
采用log4j实现分级日志记录:
properties复制# log4j.properties
log4j.rootLogger=INFO, file
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File=${catalina.base}/logs/tourism.log
log4j.appender.file.DatePattern='.'yyyy-MM-dd
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
# SQL日志单独记录
log4j.logger.java.sql=DEBUG, sql
log4j.appender.sql=org.apache.log4j.DailyRollingFileAppender
log4j.appender.sql.File=${catalina.base}/logs/sql.log
7. 开发经验总结
在项目开发过程中,我总结了以下几点经验:
-
Servlet最佳实践:
- 每个Servlet应该只负责单一功能
- 使用init()方法加载静态资源
- 避免在Servlet中直接写HTML
-
JSP使用技巧:
- 尽量使用JSTL代替Scriptlet
- 将公共页面元素提取为include文件
- 使用EL表达式简化代码
-
调试技巧:
java复制// 调试Servlet的小技巧 response.setContentType("text/plain"); PrintWriter out = response.getWriter(); out.println("Debug Information:"); out.println("Parameters: " + request.getParameterMap()); out.println("Session: " + request.getSession().getAttributeNames());
这个项目让我重新认识了Servlet技术的价值。虽然现在各种新框架层出不穷,但扎实掌握Java Web基础技术仍然是开发者的立身之本。在项目后期,我们还尝试将部分模块改造成Spring Boot版本作为技术储备,这让我对两种技术栈的差异有了更深刻的理解。