1. 三层架构设计概述
在Java企业级应用开发中,分层架构是最基础也是最重要的设计模式之一。我从业十年来,见过太多因为架构混乱导致的维护噩梦——一个Controller里既有参数校验又有业务逻辑还有SQL查询,这种"意大利面条式"代码让后续开发举步维艰。而合理的三层架构就像给代码装上GPS,让每个模块都有明确的定位和职责边界。
1.1 为什么需要分层架构
想象一下餐厅的后厨:服务员负责接待顾客(表现层),厨师专注烹饪(业务层),配菜员准备食材(数据层)。如果让服务员既点单又炒菜,结果必然是混乱低效。软件工程也是同理,分层架构的核心价值在于:
- 解耦性:各层通过接口通信,修改数据库访问方式不会影响业务逻辑
- 可维护性:问题定位更快速,比如性能瓶颈可以明确是在DAO层还是Service层
- 可测试性:可以Mock下层服务独立测试当前层,比如用Mock Service测试Controller
- 团队协作:前端和后端开发可以基于接口契约并行开发
1.2 经典三层架构组成
标准的Java Web应用通常分为以下三层:
-
表现层(Presentation Layer)
对应Spring MVC中的Controller,职责包括:- 接收HTTP请求并解析参数
- 调用Service层处理业务
- 组装响应数据(JSON/XML/HTML)
- 统一异常处理
-
业务逻辑层(Business Layer)
对应Service层,是系统的"大脑",负责:- 核心业务规则实现
- 事务控制(@Transactional)
- 多DAO的协调调用
- 业务异常抛出
-
数据访问层(Data Access Layer)
对应DAO/Repository,专注:- 数据库CRUD操作
- SQL/NoSQL交互细节封装
- 数据缓存处理
- 分页查询实现
提示:在实际项目中,还会增加DTO层(数据传输对象)用于解耦Entity与前端交互,以及Common层存放公共组件。但核心仍然是这三层的职责划分。
2. Spring Boot下的三层实现方案
2.1 技术选型对比
Java生态中实现数据持久化的主流方案有:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| JPA | 标准化接口,对象化操作,开发效率高 | 复杂查询性能较差,学习曲线陡峭 | 快速开发,需求变化频繁的项目 |
| MyBatis | SQL灵活可控,性能优化方便 | 需要手写SQL/XML,工作量大 | 需要复杂SQL优化的传统项目 |
| MyBatis-Plus | 保留MyBatis灵活性的同时增强CRUD功能 | 对多表关联查询支持不如JPA | 需要平衡开发效率和SQL控制的场景 |
| JDBC Template | 轻量级,直接控制SQL | 需要手动处理结果集映射,代码冗余 | 简单小工具或学习用途 |
以用户管理模块为例,下面分别展示基于JPA和MyBatis-Plus的实现差异。
2.2 基于Spring Data JPA的实现
2.2.1 项目结构
bash复制src/main/java/com/example/userdemo
├── config
│ └── JpaAuditingConfig.java # JPA审计配置
├── controller
│ └── UserController.java
├── service
│ ├── UserService.java
│ └── impl/UserServiceImpl.java
├── repository # JPA的DAO层
│ └── UserRepository.java
├── entity
│ └── User.java
└── dto
├── UserQueryDTO.java
└── UserVO.java
2.2.2 核心代码实现
实体类配置:
java复制@Entity
@Table(name = "sys_user")
@Data
@EntityListeners(AuditingEntityListener.class)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
private String email;
@CreatedDate
private LocalDateTime createTime;
@LastModifiedDate
private LocalDateTime updateTime;
}
Repository接口:
java复制public interface UserRepository extends JpaRepository<User, Long> {
// 方法名自动推导查询
Optional<User> findByUsername(String username);
// 自定义JPQL
@Query("SELECT u FROM User u WHERE u.email LIKE %:email%")
Page<User> findByEmailContaining(@Param("email") String email, Pageable pageable);
// 原生SQL查询
@Query(value = "SELECT * FROM sys_user WHERE age > :age", nativeQuery = true)
List<User> findAdultUsers(@Param("age") int age);
}
Service层事务控制:
java复制@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
@Transactional
@Override
public UserVO createUser(UserDTO dto) {
if (userRepository.existsByUsername(dto.getUsername())) {
throw new BusinessException("用户名已存在");
}
User user = new User();
BeanUtils.copyProperties(dto, user);
User savedUser = userRepository.save(user);
return convertToVO(savedUser);
}
// 分页查询示例
@Override
public Page<UserVO> queryUsers(UserQueryDTO query, Pageable pageable) {
Specification<User> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (StringUtils.hasText(query.getKeyword())) {
predicates.add(cb.or(
cb.like(root.get("username"), "%" + query.getKeyword() + "%"),
cb.like(root.get("email"), "%" + query.getKeyword() + "%")
));
}
if (query.getMinAge() != null) {
predicates.add(cb.ge(root.get("age"), query.getMinAge()));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
return userRepository.findAll(spec, pageable)
.map(this::convertToVO);
}
}
2.2.3 JPA最佳实践
-
审计字段自动化
通过@EntityListeners+@CreatedDate自动维护createTime/updateTime,避免手动设置 -
动态查询技巧
使用Specification实现复杂条件查询,比方法名推导更灵活 -
N+1问题解决
@EntityGraph注解定义抓取策略,或使用JOIN FETCH优化关联查询 -
乐观锁控制
@Version字段实现乐观锁,处理并发更新
2.3 基于MyBatis-Plus的实现
2.3.1 项目结构优化
bash复制src/main/java/com/example/userdemo
├── common
│ ├── PageParam.java # 分页参数基类
│ └── Result.java # 统一响应封装
├── mapper
│ └── UserMapper.java
└── query # 查询条件封装
└── UserQuery.java
2.3.2 MyBatis-Plus特色功能
Mapper接口增强:
java复制@Mapper
public interface UserMapper extends BaseMapper<User> {
// 自定义SQL示例
@Select("SELECT * FROM sys_user WHERE status = #{status}")
List<User> selectByStatus(@Param("status") int status);
// XML映射查询
List<UserVO> selectByQuery(UserQuery query);
}
Service层高效封装:
java复制@Service
public class UserService extends ServiceImpl<UserMapper, User> {
public Page<UserVO> queryByPage(PageParam pageParam, UserQuery query) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.hasText(query.getKeyword()),
User::getUsername, query.getKeyword())
.ge(query.getMinAge() != null,
User::getAge, query.getMinAge());
return baseMapper.selectPage(pageParam.toPage(), wrapper)
.convert(this::convertToVO);
}
}
XML映射文件示例:
xml复制<!-- UserMapper.xml -->
<select id="selectByQuery" resultType="UserVO">
SELECT
id, username, email,
CASE WHEN age >= 18 THEN '成年' ELSE '未成年' END AS ageGroup
FROM sys_user
<where>
<if test="keyword != null and keyword != ''">
AND (username LIKE CONCAT('%', #{keyword}, '%')
OR email LIKE CONCAT('%', #{keyword}, '%'))
</if>
<if test="minAge != null">
AND age >= #{minAge}
</if>
</where>
ORDER BY create_time DESC
</select>
2.3.3 性能优化技巧
-
批量操作
saveBatch()方法实现批量插入,配合rewriteBatchedStatements=true参数提升性能 -
逻辑删除
配置@TableLogic字段实现软删除,避免物理删除数据 -
租户隔离
通过TenantLineInnerInterceptor实现多租户数据隔离 -
SQL注入防护
始终使用#{}参数绑定,禁止直接拼接${}
3. 企业级应用进阶设计
3.1 分层架构的扩展模式
随着业务复杂度的提升,基础三层架构可能需要扩展:
-
应用层(Application Layer)
新增Command/Query处理,实现CQRS模式:java复制@Service @RequiredArgsConstructor public class UserCommandHandler { private final UserRepository repository; @Transactional public Long handle(CreateUserCommand command) { User user = new User(); // ...校验与业务处理 return repository.save(user).getId(); } } -
领域层(Domain Layer)
引入领域驱动设计(DDD),封装核心业务逻辑:java复制@Entity public class User { // ... public void changePassword(String oldPass, String newPass) { if (!passwordEncoder.matches(oldPass, this.password)) { throw new DomainException("原密码错误"); } this.password = passwordEncoder.encode(newPass); } } -
基础设施层(Infrastructure Layer)
将缓存、消息队列等组件抽象为独立层:java复制@Repository @RequiredArgsConstructor public class UserRepositoryCacheImpl implements UserRepository { private final UserRepositoryJpaImpl delegate; private final RedisTemplate redisTemplate; @Override public Optional<User> findById(Long id) { String cacheKey = "user:" + id; User cached = (User)redisTemplate.opsForValue().get(cacheKey); if (cached != null) return Optional.of(cached); Optional<User> dbUser = delegate.findById(id); dbUser.ifPresent(user -> redisTemplate.opsForValue().set(cacheKey, user, 30, MINUTES)); return dbUser; } }
3.2 跨层通信规范
-
DTO传输规范
- Controller与Service间使用Request/Response DTO
- Service与Repository间使用Entity或参数列表
- 禁止跨层传递HttpServletRequest等容器对象
-
异常处理原则
- Controller层捕获所有异常,转换为用户友好提示
- Service层抛出业务异常(如UserNotFoundException)
- Repository层抛出数据访问异常(如OptimisticLockingFailureException)
-
事务边界控制
- 事务注解
@Transactional应放在Service层 - 只读方法添加
@Transactional(readOnly = true) - 避免在Controller开启事务
- 事务注解
3.3 常见架构误区与修正
误区一:贫血模型
问题:将所有逻辑放在Service,Entity只有getter/setter
修正:采用富领域模型,将业务行为内聚到Entity
误区二:层间耦合
问题:Controller直接调用Repository
修正:通过接口隔离,每层只能调用直接下层
误区三:过度抽象
问题:为仅有单一实现的模块定义接口
修正:YAGNI原则,需要时再提取接口
误区四:循环依赖
问题:UserService调用OrderService,后者又调用UserService
修正:引入第三方服务协调,或重构领域边界
4. 实战经验与性能调优
4.1 分页查询优化方案
JPA分页陷阱:
java复制// 反例:查询全表后内存分页
@Query("SELECT u FROM User u")
List<User> findAll(Pageable pageable);
// 正解:数据库分页
@Query("SELECT u FROM User u")
Page<User> findAllWithPage(Pageable pageable);
MyBatis-Plus分页插件:
java复制@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
深度分页优化:
sql复制-- 传统分页(越往后越慢)
SELECT * FROM user ORDER BY id LIMIT 1000000, 10;
-- 优化方案:基于游标的分页
SELECT * FROM user WHERE id > 1000000 ORDER BY id LIMIT 10;
4.2 事务管理实战技巧
事务传播行为选择:
REQUIRED(默认):当前有事务则加入,没有则新建REQUIRES_NEW:总是新建事务,挂起当前事务NESTED:创建保存点,可部分回滚
声明式事务最佳实践:
java复制@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
rollbackFor = Exception.class,
timeout = 30)
public void placeOrder(OrderDTO dto) {
// 主业务逻辑
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void auditLog(Action action) {
// 审计日志(需要独立事务)
}
}
事务失效场景:
- 方法非public修饰
- 自调用(this.method())
- 异常类型不匹配(默认只回滚RuntimeException)
- 数据库引擎不支持(如MyISAM)
4.3 缓存集成策略
多级缓存架构:
code复制请求 -> 前端缓存 -> 网关缓存 -> 应用缓存 -> 分布式缓存 -> 数据库
Spring Cache注解:
java复制@Service
@CacheConfig(cacheNames = "users")
public class UserService {
@Cacheable(key = "#id", unless = "#result == null")
public User getById(Long id) {
return repository.findById(id).orElse(null);
}
@CachePut(key = "#user.id")
public User update(User user) {
return repository.save(user);
}
@CacheEvict(key = "#id")
public void delete(Long id) {
repository.deleteById(id);
}
}
缓存击穿防护:
java复制@Cacheable(key = "#id", cacheResolver = "redisCacheResolver")
public User getWithLock(Long id) {
// 1. 先查缓存
// 2. 获取分布式锁
// 3. 二次检查缓存
// 4. 查数据库并回填
// 5. 释放锁
}
5. 测试策略与持续集成
5.1 分层测试方案
测试金字塔实践:
code复制 UI测试(10%)
/ \
集成测试(20%) \
/ \
单元测试(70%) E2E测试
JUnit5测试示例:
java复制@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository repository;
@InjectMocks
private UserServiceImpl service;
@Test
@DisplayName("根据ID查询用户-成功案例")
void testGetUserByIdSuccess() {
// 准备Mock数据
User mockUser = new User(1L, "test", "test@example.com");
when(repository.findById(1L)).thenReturn(Optional.of(mockUser));
// 调用测试方法
UserVO result = service.getUserById(1L);
// 验证结果
assertEquals("test", result.getUsername());
verify(repository, times(1)).findById(1L);
}
}
SpringBootTest集成测试:
java复制@SpringBootTest
@AutoConfigureMockMvc
class UserControllerIT {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testCreateUser() throws Exception {
UserVO mockVo = new UserVO(1L, "test", "test@example.com");
when(userService.createUser(any())).thenReturn(mockVo);
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"username\":\"test\"}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.username").value("test"));
}
}
5.2 持续集成流水线
Jenkins Pipeline示例:
groovy复制pipeline {
agent any
stages {
stage('Checkout') {
steps { git url: 'https://github.com/your-repo.git' }
}
stage('Build') {
steps { sh 'mvn clean package -DskipTests' }
}
stage('Test') {
parallel {
stage('Unit Test') {
steps { sh 'mvn test' }
}
stage('Integration Test') {
steps { sh 'mvn verify -Pintegration-test' }
}
}
}
stage('Deploy') {
when { branch 'main' }
steps { sh 'kubectl apply -f k8s/deployment.yaml' }
}
}
}
质量门禁配置:
- 单元测试覆盖率≥80%
- 集成测试通过率100%
- SonarQube无阻断级别问题
- 构建时间<10分钟
6. 项目脚手架与代码生成
6.1 Spring Initializr定制
企业级项目通常需要统一技术栈,可以通过定制start.spring.io模板:
-
固定依赖版本
在application.yml中预置企业标准配置:yaml复制spring: datasource: hikari: maximum-pool-size: 20 connection-timeout: 30000 -
添加企业父POM
xml复制<parent> <groupId>com.yourcompany</groupId> <artifactId>enterprise-bom</artifactId> <version>1.0.0</version> </parent> -
预置基础包结构
自动生成controller/service/repository等标准目录
6.2 MyBatis-Plus代码生成
代码生成器配置:
java复制FastAutoGenerator.create(dataSourceConfig)
.globalConfig(builder -> builder
.author("yourname")
.outputDir("src/main/java")
.enableSwagger())
.packageConfig(builder -> builder
.parent("com.yourpackage")
.moduleName("system"))
.strategyConfig(builder -> builder
.addInclude("sys_user", "sys_role")
.entityBuilder()
.enableLombok()
.enableChainModel()
.controllerBuilder()
.enableRestStyle())
.execute();
生成内容包含:
- Entity类(带Swagger注解)
- Mapper接口及XML文件
- Service接口及实现类
- Controller(RESTful风格)
- 单元测试骨架
6.3 定制企业Starter
开发公司内部Starter统一技术实现:
自动配置类:
java复制@AutoConfiguration
@ConditionalOnClass(UserService.class)
@EnableConfigurationProperties(UserProperties.class)
public class UserAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public UserService userService(UserRepository repository) {
return new UserServiceImpl(repository);
}
}
META-INF/spring.factories:
code复制org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.yourcompany.starter.user.UserAutoConfiguration
7. 微服务架构演进
当单体应用向微服务拆分时,三层架构需要调整:
7.1 服务拆分策略
-
垂直拆分
按业务领域划分:用户服务、订单服务、商品服务 -
水平拆分
基础服务:文件服务、消息服务、权限服务 -
DDD界限上下文
识别核心域、支撑域、通用域
7.2 服务间通信
RESTful API设计:
java复制@RestController
@RequestMapping("/api/internal/users")
public class UserInternalController {
@GetMapping("/{id}")
public UserDTO getById(@PathVariable Long id) {
// 内部接口可返回详细DTO
}
@PostMapping("/batch")
public List<UserBriefDTO> getBatch(@RequestBody List<Long> ids) {
// 批量查询接口优化性能
}
}
FeignClient声明:
java复制@FeignClient(name = "user-service",
url = "${feign.user-service.url}",
configuration = FeignConfig.class)
public interface UserServiceClient {
@GetMapping("/api/internal/users/{id}")
Result<UserDTO> getById(@PathVariable("id") Long id);
@PostMapping("/api/internal/users/batch")
Result<List<UserBriefDTO>> getBatch(@RequestBody List<Long> ids);
}
7.3 分布式事务处理
Saga模式实现:
java复制@Service
public class OrderSagaService {
@SagaStart
@Transactional
public void createOrder(OrderDTO dto) {
// 1. 创建订单(本地事务)
Order order = orderRepository.save(convertToEntity(dto));
// 2. 调用库存服务
inventoryClient.reduce(dto.getProductId(), dto.getQuantity());
// 3. 如果失败触发补偿
if (paymentClient.charge(dto.getUserId(), order.getTotalAmount())) {
orderService.confirm(order.getId());
} else {
orderService.cancel(order.getId());
throw new SagaException("支付失败");
}
}
@SagaEnd
public void confirmOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();
order.confirm();
}
@Compensate
public void cancelOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();
order.cancel();
inventoryClient.restore(order.getProductId(), order.getQuantity());
}
}
8. 架构演进路线图
8.1 技术演进阶段
| 阶段 | 架构特征 | 适用场景 | 关键技术栈 |
|---|---|---|---|
| 初创期 | 单体三层架构 | 快速验证业务 | Spring Boot + JPA |
| 发展期 | 模块化分层 | 业务复杂度增加 | 领域驱动设计 + 六边形架构 |
| 成熟期 | 微服务架构 | 多团队协作,高并发需求 | Spring Cloud + Kubernetes |
| 平台期 | 服务网格 + 中台化 | 多产品线复用能力 | Istio + 领域能力中心 |
8.2 架构师成长建议
-
基础夯实
- 深入理解设计模式(特别是分层模式)
- 掌握Spring框架核心原理(IoC/AOP/事务)
- 熟练使用主流ORM工具(JPA/MyBatis)
-
视野拓展
- 学习领域驱动设计(DDD)
- 了解CQRS/Event Sourcing模式
- 研究云原生架构(Kubernetes/Service Mesh)
-
实战积累
- 参与至少一个完整的架构演进项目
- 主导技术难题攻关(如分库分表、分布式事务)
- 建立自己的技术决策框架
9. 常见问题排查指南
9.1 JPA相关问题
问题一:N+1查询
症状:控制台打印大量相似SQL
解决:
- 使用
@EntityGraph定义抓取策略 - 手动编写
JOIN FETCH查询 - 开启
spring.jpa.properties.hibernate.default_batch_fetch_size
问题二:乐观锁冲突
症状:抛出OptimisticLockingFailureException
解决:
- 实体类添加
@Version字段 - 前端传递版本号
- 实现重试机制
9.2 MyBatis问题
问题一:SQL注入风险
症状:SQL中使用${param}拼接
解决:
- 全部改为
#{param}参数绑定 - 使用
<bind>标签预处理参数 - 集成SQL防注入插件
问题二:一级缓存污染
症状:查询结果与数据库不一致
解决:
- 在方法上添加
@Options(flushCache=true) - 必要时调用
sqlSession.clearCache() - 考虑关闭一级缓存
9.3 事务问题
问题一:事务不生效
排查步骤:
- 检查方法是否为public
- 确认是否自调用(this.method())
- 检查异常类型是否匹配rollbackFor
- 查看数据库引擎(需InnoDB)
问题二:事务超时
优化方案:
- 拆分长事务为多个小事务
- 添加
@Transactional(timeout=30) - 监控慢SQL优化查询
10. 性能调优实战
10.1 数据库层面优化
索引优化:
sql复制-- 复合索引设计
ALTER TABLE sys_user ADD INDEX idx_username_email (username, email);
-- 执行计划分析
EXPLAIN SELECT * FROM sys_user WHERE username = 'test';
连接池配置:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20 # CPU核心数 * 2 + 有效磁盘数
minimum-idle: 5
idle-timeout: 600000
max-lifetime: 1800000
connection-timeout: 30000
10.2 JVM调优参数
Spring Boot应用启动参数:
bash复制java -jar your-app.jar \
-Xms1024m -Xmx2048m \ # 堆内存
-XX:MaxMetaspaceSize=512m \ # 元空间
-XX:+UseG1GC \ # GC算法
-XX:MaxGCPauseMillis=200 \ # 目标暂停时间
-XX:ParallelGCThreads=4 \ # 并行GC线程数
-XX:ConcGCThreads=2 \ # 并发GC线程数
-XX:+HeapDumpOnOutOfMemoryError # OOM时dump堆
10.3 应用层优化
缓存策略:
java复制@Cacheable(value = "users", key = "#id",
cacheManager = "redisCacheManager",
unless = "#result == null || #result.age < 18")
public User getById(Long id) {
return repository.findById(id).orElse(null);
}
异步处理:
java复制@Async("taskExecutor")
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void asyncUpdateUserStat(Long userId) {
// 耗时统计操作
userStatRepository.updateLoginCount(userId);
}
线程池配置:
java复制@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("Async-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
11. 安全防护方案
11.1 基础安全措施
SQL注入防护:
- 使用预编译语句(JPA参数绑定/MyBatis #{})
- 集成SQL防火墙(如Druid Filter)
XSS防护:
java复制@Bean
public FilterRegistrationBean<XssFilter> xssFilter() {
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new XssFilter());
registration.addUrlPatterns("/*");
return registration;
}
CSRF防护:
java复制@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
11.2 数据安全策略
敏感数据加密:
java复制@Converter
public class CryptoConverter implements AttributeConverter<String, String> {
@Override
public String convertToDatabaseColumn(String attribute) {
return AESUtil.encrypt(attribute);
}
@Override
public String convertToEntityAttribute(String dbData) {
return AESUtil.decrypt(dbData);
}
}
@Entity
public class User {
@Convert(converter = CryptoConverter.class)
private String idCardNo;
}
字段权限控制:
java复制@JsonView(Views.Public.class)
private String username;
@JsonView(Views.Internal.class)
private String mobile;
11.3 接口安全设计
签名验证:
java复制@RestControllerAdvice
public class SignInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String sign = request.getHeader("X-Sign");
String nonce = request.getHeader("X-Nonce");
String timestamp = request.getHeader("X-Timestamp");
// 验证签名有效性
if (!SignUtil.verifySign(sign, nonce, timestamp)) {
throw new ApiException("非法请求");
}
// 防重放攻击
if (nonceCache.exists(nonce)) {
throw new ApiException("请求重复");
}
nonceCache.add(nonce, 300); // 5分钟有效期
return true;
}
}
速率限制:
java复制@RateLimiter(value = 100, key = "#userId") // 每秒100次
@PostMapping("/api/users")
public Result createUser(@RequestBody UserDTO dto,
@RequestHeader Long userId) {
// 业务逻辑
}
12. 监控与可观测性
12.1 基础监控指标
应用健康指标:
java复制@RestController
@RequestMapping("/actuator")
public class HealthController {
@GetMapping("/health")
public HealthInfo health() {
return HealthInfo.builder()
.status(checkDb() && checkCache())
.dbLastCheck(LocalDateTime.now())
.build();
}
}
Prometheus指标:
java复制@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags(
"application", "user-service",
"region", System.getenv("REGION"));
}
@Timed(value = "user.query.time", description = "用户查询耗时")
@GetMapping("/{id}")
public UserVO getById(@PathVariable Long id) {
// 业务逻辑
}
12.2 分布式追踪
Sleuth+Zipkin集成:
yaml复制spring:
sleuth:
sampler:
probability: 1.0
zipkin:
base-url: http://zipkin-server:9411
自定义业务追踪:
java复制@Aspect
@Component
@RequiredArgsConstructor
public class BizTraceAspect {
private final Tracer tracer;
@Around("@annotation(bizTrace)")
public Object trace(ProceedingJoinPoint pjp, BizTrace bizTrace) throws Throwable {
Span span = tracer.nextSpan()
.name(bizTrace.value())
.tag("params", Arrays.toString(pjp.getArgs()));
try (Scope scope = tracer.withSpan(span.start())) {
return pjp.proceed();
} finally {
span.finish();
}
}
}
12.3 日志规范化
日志格式配置:
xml复制<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %X{traceId} %msg%n"/>
关键操作审计:
java复制@Log4j2
@Service
public class UserService {
@AuditLog(action = "CREATE_USER")
public UserVO createUser(UserDTO dto) {
// 业务逻辑
log.info("创建用户成功, username={}, operator={}",
dto.getUsername(), SecurityUtils.getCurrentUser());
}
}
13. 容器化部署方案
13.1 Docker镜像优化
多阶段构建:
dockerfile复制# 构建阶段
FROM maven:3.8.6-jdk-11 AS build
COPY . .
RUN mvn clean package -DskipTests
# 运行阶段
FROM openjdk:11-jre-slim
COPY --from=build /target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
JVM调优参数:
dockerfile复制ENV JAVA_OPTS="-Xms1024m -Xmx1024m -XX:+UseG1GC"
ENTRYPOINT exec java $JAVA_OPTS -jar /app.jar
13.2 Kubernetes部署
Deployment配置:
yaml复制apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: registry.example.com/user-service:v1.0.0
ports:
- containerPort: 8080
resources:
limits:
cpu: "2"
memory: 2Gi
requests:
cpu: "1"
memory: 1Gi
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
Horizontal Pod Autoscaler:
yaml复制apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: