【Java项目脚手架系列】第二篇:JavaWeb项目脚手架
前言
在Java Web开发中,一个好的项目脚手架可以大大提高开发效率,减少重复工作。本篇文章将介绍一个基于Maven的JavaWeb项目脚手架,它包含了基础的Web开发配置和常用功能。
什么是JavaWeb项目脚手架?
JavaWeb项目脚手架是一个基于Maven的Web项目模板,它提供了:
- 标准的Web项目结构
- 基础的Web开发配置
- 常用功能组件
- 最佳实践示例
JavaWeb项目脚手架
1. 项目结构
javaweb-scaffold/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── filter/
│ │ │ │ └── CharacterEncodingFilter.java
│ │ │ └── servlet/
│ │ │ ├── Error404Servlet.java
│ │ │ ├── Error500Servlet.java
│ │ │ └── HelloServlet.java
│ │ ├── resources/
│ │ │ └── log4j2.xml
│ │ └── webapp/
│ │ ├── WEB-INF/
│ │ │ └── web.xml
│ │ ├── error/
│ │ │ ├── 404.jsp
│ │ │ └── 500.jsp
│ │ └── index.jsp
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ └── servlet/
│ │ └── HelloServletTest.java
│ └── resources/
├── docs/
│ └── 02-javaweb-scaffold.md
├── pom.xml
└── README.md
2. 核心文件内容
2.1 pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>javaweb-scaffold</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<servlet.version>4.0.1</servlet.version>
<jsp.version>2.3.3</jsp.version>
<jstl.version>1.2</jstl.version>
<junit.version>5.8.2</junit.version>
<mockito.version>4.5.1</mockito.version>
<lombok.version>1.18.24</lombok.version>
<log4j2.version>2.17.2</log4j2.version>
</properties>
<dependencies>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<!-- JSP API -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>${jsp.version}</version>
<scope>provided</scope>
</dependency>
<!-- JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
<scope>compile</scope>
</dependency>
<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- Log4j2 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j2.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/javaweb-scaffold</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.2 web.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>JavaWeb Scaffold</display-name>
<!-- 字符编码过滤器 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>com.example.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 错误页面配置 -->
<error-page>
<error-code>404</error-code>
<location>/error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error/500.jsp</location>
</error-page>
</web-app>
2.3 CharacterEncodingFilter.java
java
package com.example.filter;
import javax.servlet.*;
import java.io.IOException;
public class CharacterEncodingFilter implements Filter {
private String encoding;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
encoding = filterConfig.getInitParameter("encoding");
if (encoding == null) {
encoding = "UTF-8";
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
2.4 HelloServlet.java
java
package com.example.servlet;
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("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("Hello from JavaWeb Scaffold!");
}
}
2.5 Error404Servlet.java
java
package com.example.servlet;
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("/error/404")
public class Error404Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
request.getRequestDispatcher("/error/404.jsp").forward(request, response);
}
}
2.6 Error500Servlet.java
java
package com.example.servlet;
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("/error/500")
public class Error500Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
request.getRequestDispatcher("/error/500.jsp").forward(request, response);
}
}
2.7 index.jsp
jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaWeb Scaffold</title>
</head>
<body>
<div class="container">
<h1>Welcome to JavaWeb Scaffold</h1>
<p>Click <a href="hello">here</a> to test the HelloServlet.</p>
</div>
</body>
</html>
2.8 log4j2.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Properties>
<Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Property>
<Property name="APP_LOG_ROOT">logs</Property>
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
<RollingFile name="FileAppender"
fileName="${APP_LOG_ROOT}/app.log"
filePattern="${APP_LOG_ROOT}/app-%d{yyyy-MM-dd}-%i.log">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="FileAppender"/>
</Root>
</Loggers>
</Configuration>
2.9 404.jsp
jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>404 - Page Not Found</title>
<style>
.error-container {
text-align: center;
margin-top: 100px;
}
.error-code {
font-size: 72px;
color: #e74c3c;
}
.error-message {
font-size: 24px;
margin: 20px 0;
}
.home-link {
color: #3498db;
text-decoration: none;
}
</style>
</head>
<body>
<div class="error-container">
<div class="error-code">404</div>
<div class="error-message">Oops! The page you're looking for doesn't exist.</div>
<a href="${pageContext.request.contextPath}/" class="home-link">Return to Home</a>
</div>
</body>
</html>
2.10 500.jsp
jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>500 - Internal Server Error</title>
<style>
.error-container {
text-align: center;
margin-top: 100px;
}
.error-code {
font-size: 72px;
color: #e74c3c;
}
.error-message {
font-size: 24px;
margin: 20px 0;
}
.error-details {
color: #7f8c8d;
margin: 20px 0;
}
.home-link {
color: #3498db;
text-decoration: none;
}
</style>
</head>
<body>
<div class="error-container">
<div class="error-code">500</div>
<div class="error-message">Internal Server Error</div>
<div class="error-details">
An unexpected error occurred. Please try again later.
</div>
<a href="${pageContext.request.contextPath}/" class="home-link">Return to Home</a>
</div>
</body>
</html>
2.11 HelloServletTest.java
java
package com.example.servlet;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.StringWriter;
import static org.mockito.Mockito.*;
class HelloServletTest {
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
private HelloServlet servlet;
private StringWriter stringWriter;
private PrintWriter writer;
@BeforeEach
void setUp() throws Exception {
MockitoAnnotations.openMocks(this);
servlet = new HelloServlet();
stringWriter = new StringWriter();
writer = new PrintWriter(stringWriter);
when(response.getWriter()).thenReturn(writer);
}
@Test
void doGet_ShouldWriteHelloMessage() throws Exception {
// When
servlet.doGet(request, response);
// Then
verify(response).setContentType("text/html;charset=UTF-8");
writer.flush();
assertTrue(stringWriter.toString().contains("Hello from JavaWeb Scaffold!"));
}
}
3. 使用说明
-
环境准备
- JDK 8+
- Maven 3.6+
- Tomcat 8.5+
-
克隆项目
bashgit clone [email protected]:zengqiang_wang/leecode-inteview-questions-journal.git
-
导入IDE
- 推荐使用IntelliJ IDEA
- 选择"Open as Maven Project"
-
运行项目
bashmvn tomcat7:run
4. 最佳实践
-
项目结构
- 遵循标准的Web项目结构
- 合理划分包层次
- 保持代码整洁
-
编码规范
- 统一使用UTF-8编码
- 使用字符编码过滤器
- 规范JSP页面编码
-
错误处理
- 配置统一的错误页面
- 记录详细的错误日志
- 提供友好的错误提示
-
测试编写
- 使用JUnit 5编写单元测试
- 使用Mockito模拟Servlet环境
- 测试覆盖核心功能
5. 常见问题
-
IDEA社区版无Tomcat插件
- 使用Maven插件运行
- 安装Smart Tomcat插件
- 配置Tomcat服务器
-
首页500错误
- 检查JSTL依赖配置
- 简化JSP页面内容
- 配置错误页面
-
编码问题
- 确保所有文件使用UTF-8编码
- 配置字符编码过滤器
- 检查JSP页面编码声明
6. 总结
这个JavaWeb项目脚手架提供了一个基础的Web开发环境,包含了常用的配置和功能。通过解决常见问题,我们可以快速搭建一个可用的Web项目。
7. 下期预告
下一期我们将介绍SpringMVC项目脚手架,它将基于本期项目进行扩展,添加Spring MVC相关功能,包括:
- 标准的MVC架构
- 统一响应处理
- 统一异常处理
- 参数校验
- 文件上传
- RESTful API支持
敬请期待!