1. JavaWeb开发环境搭建与基础配置
作为一名从零开始学习JavaWeb的开发者,环境搭建往往是第一个拦路虎。我至今记得第一次配置Tomcat时遇到的端口冲突问题,折腾了整整一个下午。这里分享下经过多次实践验证的可靠配置方案。
1.1 JDK安装与验证
推荐使用JDK 11这个LTS版本,它在稳定性和新特性之间取得了很好的平衡。安装完成后需要特别注意环境变量配置:
bash复制# 在~/.bash_profile或~/.zshrc中添加
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-11.0.15.jdk/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH
验证安装时不要仅满足于java -version,建议用以下组合命令全面检查:
bash复制java -version && javac -version && which java
注意:macOS自带的Java版本可能干扰你的开发环境,务必通过上述方式确认使用的是你安装的JDK版本。
1.2 Tomcat服务器配置
Apache Tomcat 9.x是目前最稳定的Servlet容器选择。下载解压后,重点需要配置:
conf/server.xml中的连接器配置:
xml复制<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"/>
- 内存参数调整(在
bin/catalina.sh或catalina.bat中):
bash复制export JAVA_OPTS="-server -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m"
启动后建议立即访问http://localhost:8080的管理界面,验证基础功能是否正常。常见问题包括:
- 端口被占用(netstat -ano | findstr 8080)
- 权限不足(chmod +x *.sh)
- JAVA_HOME未正确设置
1.3 IDE的选择与优化
IntelliJ IDEA Ultimate版对JavaWeb支持最为完善。创建项目时建议选择"Maven"作为构建工具,模板选用"maven-archetype-webapp"。关键配置项:
-
在Project Structure中确保:
- SDK指向JDK 11
- Language level设为11
- Modules的Dependencies包含Servlet API
-
部署配置(Edit Configurations):
- 添加Local Tomcat Server
- Deployment选项卡添加Artifact选择war exploded
- Application context设为
/(避免多余路径)
经验:开发时使用"Update classes and resources"的热部署模式,可以节省大量重启时间。但要注意静态资源修改有时需要Rebuild才能生效。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Servlet核心机制深度解析
2.1 生命周期与线程模型
Servlet的生命周期是面试常考点,但实际开发中更需要注意其线程安全问题。通过一个简单的访问计数器示例:
java复制public class CounterServlet extends HttpServlet {
private int count = 0; // 实例变量 - 线程不安全!
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
count++;
resp.getWriter().println("Visits: " + count);
}
}
这个经典案例展示了为什么Servlet需要避免使用实例变量。正确的做法应该是:
java复制private AtomicInteger count = new AtomicInteger(0);
// 或者
synchronized(this) {
count++;
}
2.2 请求处理流程
从HTTP请求到Servlet响应的完整流程:
- 容器创建HttpServletRequest/Response对象
- 调用service()方法(通常不需要重写)
- 根据请求方法路由到doGet/doPost等
- 开发者实现业务逻辑
- 通过response输出结果
关键对象方法:
request.getParameter()vsgetParameterValues()request.setAttribute()与getAttribute()的作用域response.sendRedirect()与forward()的区别
2.3 过滤器与监听器实战
过滤器(Filter)的典型应用场景:
java复制public class LoggingFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
long start = System.currentTimeMillis();
chain.doFilter(req, res); // 必须调用!
long duration = System.currentTimeMillis() - start;
System.out.println("Request took: " + duration + "ms");
}
}
在web.xml中的配置:
xml复制<filter>
<filter-name>loggingFilter</filter-name>
<filter-class>com.example.LoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
监听器(Listener)的常见用途:
- ServletContextListener:初始化全局资源
- HttpSessionListener:统计在线用户
- ServletRequestListener:记录请求信息
3. JSP与EL表达式最佳实践
3.1 从Servlet到JSP的演进
早期JavaWeb开发中,Servlet直接输出HTML的弊端:
java复制resp.getWriter().println("<html><body>");
resp.getWriter().println("<h1>Hello " + username + "</h1>");
// 更多HTML拼接...
这种写法难以维护,催生了JSP技术。现代JSP应该遵循的原则:
- 尽量减少Java代码片段(<% %>)
- 使用JSTL和EL代替脚本
- 保持业务逻辑在Servlet中
3.2 EL表达式的妙用
EL(Expression Language)的进阶技巧:
jsp复制${user.name} 相当于 <%= ((User)request.getAttribute("user")).getName() %>
<!-- 安全访问嵌套属性 -->
${user?.address?.city}
<!-- 集合操作 -->
<c:forEach items="${users}" var="u" varStatus="status">
${status.index}: ${u.name}
</c:forEach>
<!-- 条件判断 -->
${empty param.name ? 'Anonymous' : param.name}
3.3 JSTL核心标签库
常用标签分类:
| 标签类型 | 示例 | 用途 |
|---|---|---|
| 核心标签 | <c:if>, <c:forEach> |
流程控制 |
| 格式化标签 | <fmt:formatDate> |
数据格式化 |
| SQL标签 | <sql:query> |
数据库操作(不推荐) |
| 函数标签 | ${fn:length(list)} |
集合操作 |
实际开发建议:避免在JSP中进行复杂业务逻辑,保持其作为视图层的纯粹性。
4. 会话管理与会话安全
4.1 Cookie与Session机制对比
| 特性 | Cookie | HttpSession |
|---|---|---|
| 存储位置 | 客户端 | 服务端 |
| 安全性 | 较低(可被篡改) | 较高 |
| 容量限制 | 4KB左右 | 受服务器内存限制 |
| 生命周期 | 可设置过期时间 | 默认30分钟不活动则失效 |
典型会话跟踪实现:
java复制// 创建会话
HttpSession session = request.getSession(true);
session.setAttribute("user", currentUser);
// 设置会话超时(分钟)
session.setMaxInactiveInterval(30 * 60);
// 使会话失效
session.invalidate();
4.2 会话固定攻击防御
常见安全漏洞及解决方案:
- 会话固定(Session Fixation):
java复制// 登录前先使旧会话失效
request.getSession().invalidate();
HttpSession newSession = request.getSession(true);
- 会话劫持防护:
java复制// 生成CSRF Token
String token = UUID.randomUUID().toString();
session.setAttribute("csrfToken", token);
// 在表单中添加隐藏域
<input type="hidden" name="csrfToken" value="${csrfToken}">
- 敏感操作重新认证:
java复制if (!checkPassword(user, currentPassword)) {
// 强制退出
}
4.3 分布式会话方案
当应用需要水平扩展时,本地会话存储的局限性显现。常见解决方案:
- Session复制(Tomcat集群配置)
xml复制<!-- server.xml -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
- 集中式存储(Redis方案)
java复制// Spring Session配置示例
@EnableRedisHttpSession
public class SessionConfig {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
}
- JWT无状态方案(现代微服务常用)
java复制String token = Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
.compact();
5. 数据库连接与DAO模式实现
5.1 JDBC连接池配置
原生JDBC的典型问题:
java复制// 反模式:每次请求都创建新连接
Connection conn = DriverManager.getConnection(url, user, pass);
// 业务代码...
conn.close(); // 可能忘记关闭!
推荐使用HikariCP连接池配置:
java复制HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("user");
config.setPassword("pass");
config.setMaximumPoolSize(10);
config.setConnectionTimeout(3000);
DataSource dataSource = new HikariDataSource(config);
连接池参数调优建议:
| 参数 | 建议值 | 说明 |
|---|---|---|
| maximumPoolSize | CPU核心数*2 + 有效磁盘数 | 过大反而降低性能 |
| connectionTimeout | 3000ms | 避免长时间等待 |
| idleTimeout | 600000ms(10分钟) | 空闲连接回收 |
| maxLifetime | 1800000ms(30分钟) | 连接最大存活时间 |
5.2 DAO模式实现演进
传统DAO实现:
java复制public class UserDaoImpl implements UserDao {
public User findById(int id) {
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id=?")) {
stmt.setInt(1, id);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return mapRowToUser(rs);
}
} catch (SQLException e) {
throw new DataAccessException(e);
}
return null;
}
private User mapRowToUser(ResultSet rs) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
// 更多字段映射...
return user;
}
}
使用Spring JdbcTemplate简化:
java复制public User findById(int id) {
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id=?",
new Object[]{id},
(rs, rowNum) -> {
User user = new User();
user.setId(rs.getInt("id"));
// 映射其他字段
return user;
});
}
5.3 事务管理要点
编程式事务管理示例:
java复制public void transferMoney(Account from, Account to, BigDecimal amount) {
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
accountDao.debit(from, amount);
accountDao.credit(to, amount);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw new TransferException("Transfer failed", e);
}
}
声明式事务配置(基于注解):
java复制@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
timeout = 30,
rollbackFor = {SQLException.class, DataAccessException.class}
)
public void transferMoney(Account from, Account to, BigDecimal amount) {
// 业务代码
}
经验之谈:事务注解应该放在服务层而非DAO层,因为一个业务操作可能涉及多个DAO调用。
6. 项目结构与设计模式应用
6.1 分层架构规范
推荐的项目包结构:
code复制src/main/java
├── com.example
│ ├── config/ # 配置类
│ ├── controller/ # Servlet/Spring MVC控制器
│ ├── service/ # 业务逻辑层
│ ├── dao/ # 数据访问层
│ ├── model/ # 实体类
│ ├── util/ # 工具类
│ └── filter/ # 过滤器
src/main/webapp
├── WEB-INF
│ ├── views/ # JSP文件
│ └── web.xml
├── static/ # 静态资源
│ ├── css/
│ ├── js/
│ └── images/
6.2 MVC模式实现
传统Servlet实现MVC:
java复制// Controller
public class UserServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
String action = req.getParameter("action");
if ("list".equals(action)) {
List<User> users = userService.findAll();
req.setAttribute("users", users);
req.getRequestDispatcher("/WEB-INF/views/user/list.jsp").forward(req, resp);
}
// 其他action处理...
}
}
前端控制器模式改进:
java复制public class FrontController extends HttpServlet {
private Map<String, Command> commands = new HashMap<>();
public void init() {
commands.put("user/list", new ListUsersCommand());
// 注册更多命令...
}
protected void service(HttpServletRequest req, HttpServletResponse resp) {
String path = req.getServletPath() + req.getPathInfo();
Command command = commands.get(path);
if (command != null) {
String view = command.execute(req, resp);
req.getRequestDispatcher(view).forward(req, resp);
} else {
resp.sendError(404);
}
}
}
6.3 工厂模式与依赖注入
简单工厂模式实现:
java复制public class DaoFactory {
public static UserDao getUserDao() {
return new UserDaoImpl();
}
public static ProductDao getProductDao() {
return new ProductDaoImpl();
}
}
改进为依赖注入:
java复制public class UserServiceImpl implements UserService {
private final UserDao userDao;
// 构造器注入
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
// setter注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
7. 性能优化与调试技巧
7.1 JVM参数调优
Tomcat的JVM参数建议(在catalina.sh中设置):
bash复制export CATALINA_OPTS="-server -Xms1024m -Xmx2048m -XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-XX:ParallelGCThreads=4 -XX:ConcGCThreads=2"
关键参数说明:
-Xms和-Xmx设置为相同值避免动态调整- G1垃圾收集器适合Web应用
MaxGCPauseMillis控制最大停顿时间- 根据CPU核心数设置GC线程数
7.2 数据库访问优化
常见性能问题及解决方案:
- N+1查询问题:
java复制// 反例
List<User> users = userDao.findAll();
for (User user : users) {
List<Order> orders = orderDao.findByUserId(user.getId());
// ...
}
// 正解:使用JOIN查询一次获取
SELECT u.*, o.* FROM users u LEFT JOIN orders o ON u.id = o.user_id
- 批量操作优化:
java复制// 单条插入
INSERT INTO table VALUES (...)
// 批量插入
INSERT INTO table VALUES (...),(...),(...)
- 合理使用索引:
sql复制-- 为常用查询条件创建索引
CREATE INDEX idx_user_email ON users(email);
-- 复合索引注意顺序
CREATE INDEX idx_user_status_created ON users(status, created_at);
7.3 日志与监控配置
Logback日志配置示例(logback.xml):
xml复制<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.example" level="DEBUG"/>
<logger name="org.springframework" level="WARN"/>
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</configuration>
监控指标建议:
- 请求响应时间(Filter中记录)
- 数据库查询耗时(JDBC拦截器)
- JVM内存使用(JMX监控)
- 活跃会话数(SessionListener统计)
8. 安全防护与最佳实践
8.1 常见Web安全漏洞防护
- SQL注入防护:
java复制// 错误做法
String sql = "SELECT * FROM users WHERE username='" + username + "'";
// 正确做法:使用PreparedStatement
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM users WHERE username=?");
stmt.setString(1, username);
- XSS防护:
jsp复制<!-- 输出前过滤 -->
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
${fn:escapeXml(userInput)}
<!-- 或者使用ESAPI -->
<%@ taglib prefix="esapi" uri="http://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API" %>
<esapi:encodeForHTML>${userInput}</esapi:encodeForHTML>
- 文件上传安全:
java复制// 限制文件类型
String contentType = file.getContentType();
if (!ALLOWED_TYPES.contains(contentType)) {
throw new IllegalFileTypeException();
}
// 重命名上传文件
String safeName = UUID.randomUUID() + "." + getFileExtension(originalName);
File dest = new File(UPLOAD_DIR, safeName);
file.transferTo(dest);
8.2 HTTPS强制与安全头配置
在web.xml中配置安全传输:
xml复制<security-constraint>
<web-resource-collection>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
通过Filter添加安全头:
java复制public class SecurityHeadersFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("X-Content-Type-Options", "nosniff");
response.setHeader("X-Frame-Options", "DENY");
response.setHeader("X-XSS-Protection", "1; mode=block");
response.setHeader("Content-Security-Policy", "default-src 'self'");
chain.doFilter(req, res);
}
}
8.3 密码安全存储
密码哈希处理示例:
java复制public class PasswordUtil {
private static final int ITERATIONS = 10000;
private static final int KEY_LENGTH = 256;
private static final SecureRandom RANDOM = new SecureRandom();
public static String hashPassword(String password) {
byte[] salt = new byte[16];
RANDOM.nextBytes(salt);
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
salt,
ITERATIONS,
KEY_LENGTH);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = skf.generateSecret(spec).getEncoded();
return ITERATIONS + ":" +
Base64.getEncoder().encodeToString(salt) + ":" +
Base64.getEncoder().encodeToString(hash);
}
public static boolean verifyPassword(String inputPassword, String storedPassword) {
// 验证逻辑...
}
}
重要提示:永远不要自己实现加密算法,使用经过验证的库如BCrypt或Argon2。Spring Security提供了现成的
BCryptPasswordEncoder。
9. 前后端分离趋势下的JavaWeb
9.1 RESTful API设计
传统Servlet开发REST API:
java复制@WebServlet("/api/users/*")
public class UserApiServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
String pathInfo = req.getPathInfo(); // /{id}
if (pathInfo == null || pathInfo.equals("/")) {
// 获取所有用户
List<User> users = userService.findAll();
resp.setContentType("application/json");
new ObjectMapper().writeValue(resp.getWriter(), users);
} else {
// 获取单个用户
String id = pathInfo.substring(1);
User user = userService.findById(Integer.parseInt(id));
// 处理响应...
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
// 创建用户
User user = new ObjectMapper().readValue(req.getInputStream(), User.class);
userService.create(user);
resp.setStatus(HttpServletResponse.SC_CREATED);
}
}
9.2 JSON处理优化
Jackson高级配置:
java复制ObjectMapper mapper = new ObjectMapper();
// 忽略未知属性
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 日期格式
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// 空值处理
mapper.setSerializationInclusion(Include.NON_NULL);
// 使用TypeReference处理泛型
List<User> users = mapper.readValue(json, new TypeReference<List<User>>(){});
9.3 跨域解决方案
Filter实现CORS支持:
java复制public class CorsFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
}
10. 现代化技术栈演进
10.1 从Servlet到Spring Boot
传统Servlet应用迁移到Spring Boot的步骤:
- 添加依赖:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 主类配置:
java复制@SpringBootApplication
public class MyApp extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
- 控制器改造:
java复制@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping
public List<User> listUsers() {
return userService.findAll();
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User createUser(@RequestBody User user) {
return userService.create(user);
}
}
10.2 微服务架构准备
Spring Cloud基础组件:
- 服务发现:Eureka/Nacos
- 配置中心:Spring Cloud Config
- 客户端负载均衡:Ribbon
- 声明式客户端:Feign
- 熔断器:Hystrix/Sentinel
- 网关:Spring Cloud Gateway
10.3 云原生部署实践
Docker化JavaWeb应用:
dockerfile复制FROM openjdk:11-jre-slim
WORKDIR /app
COPY target/myapp.war .
CMD ["java", "-jar", "myapp.war"]
Kubernetes部署示例:
yaml复制apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myrepo/myapp:1.0.0
ports:
- containerPort: 8080
resources:
limits:
memory: "1Gi"
cpu: "500m"
11. 测试策略与质量保障
11.1 单元测试实践
Servlet单元测试方案:
java复制public class UserServletTest {
private UserServlet servlet;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
@Before
public void setUp() {
servlet = new UserServlet();
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}
@Test
public void testDoGet() throws Exception {
request.setParameter("id", "1");
servlet.doGet(request, response);
assertEquals(200, response.getStatus());
assertNotNull(response.getContentAsString());
}
}
11.2 集成测试方案
使用TestContainers进行数据库集成测试:
java复制@Testcontainers
public class UserDaoIntegrationTest {
@Container
public static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:13");
@BeforeAll
public static void setup() {
System.setProperty("DB_URL", postgres.getJdbcUrl());
System.setProperty("DB_USERNAME", postgres.getUsername());
System.setProperty("DB_PASSWORD", postgres.getPassword());
}
@Test
public void testFindById() {
UserDao dao = new UserDaoImpl();
User user = dao.findById(1);
assertNotNull(user);
}
}
11.3 性能测试工具
JMeter测试计划关键配置:
- 线程组:模拟并发用户数
- HTTP请求采样器:配置请求路径和参数
- 监听器:查看结果树、聚合报告
- 断言:验证响应内容
- 定时器:控制请求频率
性能测试关注指标:
- 吞吐量(Throughput):请求/秒
- 响应时间:平均、90%线、最大
- 错误率:失败请求占比
- 资源使用率:CPU、内存、IO
12. 持续集成与部署流水线
12.1 Maven构建配置
多环境profile配置:
xml复制<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<env>dev</env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env>prod</env>
</properties>
</profile>
</profiles>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
12.2 Jenkins流水线示例
Jenkinsfile定义:
groovy复制pipeline {
agent any
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/user/repo.git'
}
}
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit '**/target/surefire-reports/*.xml'
}
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh 'scp target/*.war user@server:/deploy/path'
}
}
}
}
12.3 制品管理与版本控制
版本号规范建议:
- MAJOR.MINOR.PATCH (语义化版本)
- SNAPSHOT用于开发版本
- RELEASE用于生产部署
Nexus仓库配置:
xml复制<distributionManagement>
<snapshotRepository>
<id>nexus-snapshots</id>
<url>http://nexus.example.com/repository/maven-snapshots/</url>
</snapshotRepository>
<repository>
<id>nexus-releases</id>
<url>http://nexus.example.com/repository/maven-releases/</url>
</repository>
</distributionManagement>
部署命令:
bash复制mvn clean deploy -Pprod
