学习目标
掌握性能优化方法,学习系统监控与可观测性,理解安全认证机制,掌握测试方法,了解最佳实践,提升系统性能与稳定性。
1. 性能优化
1.1 JVM调优
JVM参数配置:
// JVM调优配置类
@Configuration
@Slf4j
public class JvmTuningConfig {
// 堆内存配置
// -Xms2g -Xmx2g:初始堆和最大堆大小
// -XX:NewRatio=2:新生代与老年代比例
// -XX:SurvivorRatio=8:Eden与Survivor比例
// GC配置
// -XX:+UseG1GC:使用G1垃圾收集器
// -XX:MaxGCPauseMillis=200:最大GC暂停时间
// -XX:G1HeapRegionSize=16m:G1区域大小
@PostConstruct
public void printJvmInfo() {
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
log.info("最大内存: {} MB", maxMemory / 1024 / 1024);
log.info("总内存: {} MB", totalMemory / 1024 / 1024);
log.info("已用内存: {} MB", usedMemory / 1024 / 1024);
log.info("可用内存: {} MB", freeMemory / 1024 / 1024);
// 内存使用率
double memoryUsage = (double) usedMemory / totalMemory * 100;
log.info("内存使用率: {:.2f}%", memoryUsage);
}
}
// 内存监控服务
@Service
@Slf4j
public class MemoryMonitorService {
private final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
// 获取堆内存使用情况
public MemoryUsage getHeapMemoryUsage() {
return memoryBean.getHeapMemoryUsage();
}
// 获取非堆内存使用情况
public MemoryUsage getNonHeapMemoryUsage() {
return memoryBean.getNonHeapMemoryUsage();
}
// 检查内存使用率
public boolean isMemoryUsageHigh(double threshold) {
MemoryUsage heapUsage = getHeapMemoryUsage();
long used = heapUsage.getUsed();
long max = heapUsage.getMax();
double usage = (double) used / max * 100;
log.info("堆内存使用率: {:.2f}%", usage);
return usage > threshold;
}
// 触发GC
public void triggerGC() {
System.gc();
log.info("手动触发GC");
}
// 获取GC信息
public List<GarbageCollectorMXBean> getGCBeans() {
return ManagementFactory.getGarbageCollectorMXBeans();
}
// 打印GC统计信息
public void printGCStats() {
List<GarbageCollectorMXBean> gcBeans = getGCBeans();
for (GarbageCollectorMXBean gcBean : gcBeans) {
log.info("GC名称: {}", gcBean.getName());
log.info("GC次数: {}", gcBean.getCollectionCount());
log.info("GC总时间: {} ms", gcBean.getCollectionTime());
}
}
}
// 线程监控服务
@Service
@Slf4j
public class ThreadMonitorService {
private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
// 获取线程信息
public ThreadInfo[] getAllThreadInfo() {
long[] threadIds = threadBean.getAllThreadIds();
return threadBean.getThreadInfo(threadIds);
}
// 打印线程统计信息
public void printThreadStats() {
int threadCount = threadBean.getThreadCount();
int peakThreadCount = threadBean.getPeakThreadCount();
long totalStartedThreadCount = threadBean.getTotalStartedThreadCount();
log.info("当前线程数: {}", threadCount);
log.info("峰值线程数: {}", peakThreadCount);
log.info("总启动线程数: {}", totalStartedThreadCount);
// 打印所有线程信息
ThreadInfo[] threadInfos = getAllThreadInfo();
for (ThreadInfo threadInfo : threadInfos) {
if (threadInfo != null) {
log.info("线程: {} - 状态: {} - CPU时间: {} ns",
threadInfo.getThreadName(),
threadInfo.getThreadState(),
threadBean.getThreadCpuTime(threadInfo.getThreadId())
);
}
}
}
// 检测死锁
public long[] detectDeadlocks() {
return threadBean.findDeadlockedThreads();
}
}
1.2 代码级优化
字符串优化:
// 字符串优化服务
@Service
@Slf4j
public class StringOptimizationService {
// 使用StringBuilder代替字符串拼接
public String buildString(List<String> items) {
StringBuilder sb = new StringBuilder();
for (String item : items) {
sb.append(item);
}
return sb.toString();
}
// 使用String.join(Java 8+)
public String joinStrings(List<String> items) {
return String.join("", items);
}
// 字符串常量池优化
public boolean compareStrings(String str1, String str2) {
// 使用equals比较内容
return str1.equals(str2);
// 使用intern()利用常量池
// return str1.intern() == str2.intern();
}
// 避免在循环中创建字符串
public String processStrings(List<String> items) {
StringBuilder result = new StringBuilder();
for (String item : items) {
// 避免在循环中创建临时字符串
result.append(item.toUpperCase());
}
return result.toString();
}
}
// 集合优化服务
@Service
@Slf4j
public class CollectionOptimizationService {
// 指定集合初始容量
public List<String> createList(int expectedSize) {
return new ArrayList<>(expectedSize);
}
// 使用合适的集合类型
public void useAppropriateCollection() {
// 需要快速查找使用HashSet
Set<String> uniqueItems = new HashSet<>();
// 需要保持顺序使用LinkedHashSet
Set<String> orderedItems = new LinkedHashSet<>();
// 需要排序使用TreeSet
Set<String> sortedItems = new TreeSet<>();
// 需要键值对使用HashMap
Map<String, String> keyValueMap = new HashMap<>();
// 需要线程安全使用ConcurrentHashMap
Map<String, String> concurrentMap = new ConcurrentHashMap<>();
}
// 使用Stream API优化集合操作
public List<String> filterAndTransform(List<String> items) {
return items.stream()
.filter(item -> item.length() > 5)
.map(String::toUpperCase)
.collect(Collectors.toList());
}
// 避免不必要的迭代
public boolean containsItem(List<String> items, String target) {
// 使用contains方法而不是遍历
return items.contains(target);
}
}
// 对象创建优化
@Service
@Slf4j
public class ObjectCreationOptimizationService {
// 使用对象池
private final ObjectPool<StringBuilder> stringBuilderPool = new GenericObjectPool<>(
new PooledObjectFactory<StringBuilder>() {
@Override
public PooledObject<StringBuilder> makeObject() {
return new DefaultPooledObject<>(new StringBuilder());
}
@Override
public void destroyObject(PooledObject<StringBuilder> p) {
p.getObject().setLength(0);
}
@Override
public boolean validateObject(PooledObject<StringBuilder> p) {
return true;
}
@Override
public void activateObject(PooledObject<StringBuilder> p) {
p.getObject().setLength(0);
}
@Override
public void passivateObject(PooledObject<StringBuilder> p) {
p.getObject().setLength(0);
}
}
);
// 重用对象
public StringBuilder getStringBuilder() {
try {
return stringBuilderPool.borrowObject();
} catch (Exception e) {
log.error("获取StringBuilder失败", e);
return new StringBuilder();
}
}
public void returnStringBuilder(StringBuilder sb) {
try {
stringBuilderPool.returnObject(sb);
} catch (Exception e) {
log.error("归还StringBuilder失败", e);
}
}
// 使用不可变对象
public final class ImmutableData {
private final String name;
private final int value;
public ImmutableData(String name, int value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public int getValue() {
return value;
}
}
}
1.3 数据库优化
数据库连接池优化:
// 数据库连接池配置
@Configuration
@Slf4j
public class DataSourceConfig {
// HikariCP连接池配置(推荐)
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public HikariDataSource dataSource() {
HikariConfig config = new HikariConfig();
// 连接池大小
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
// 连接超时
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
// 连接测试
config.setConnectionTestQuery("SELECT 1");
config.setValidationTimeout(5000);
// 性能优化
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
config.addDataSourceProperty("useServerPrepStmts", "true");
config.addDataSourceProperty("useLocalSessionState", "true");
config.addDataSourceProperty("rewriteBatchedStatements", "true");
config.addDataSourceProperty("cacheResultSetMetadata", "true");
config.addDataSourceProperty("cacheServerConfiguration", "true");
config.addDataSourceProperty("elideSetAutoCommits", "true");
config.addDataSourceProperty("maintainTimeStats", "false");
return new HikariDataSource(config);
}
}
// 数据库查询优化服务
@Service
@Slf4j
public class DatabaseOptimizationService {
@Autowired
private JdbcTemplate jdbcTemplate;
// 批量插入优化
public void batchInsert(List<Map<String, Object>> dataList) {
String sql = "INSERT INTO orders (order_id, user_id, amount) VALUES (?, ?, ?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Map<String, Object> data = dataList.get(i);
ps.setString(1, (String) data.get("orderId"));
ps.setLong(2, (Long) data.get("userId"));
ps.setBigDecimal(3, (BigDecimal) data.get("amount"));
}
@Override
public int getBatchSize() {
return dataList.size();
}
});
}
// 使用索引优化查询
public List<Map<String, Object>> queryWithIndex(Long userId) {
// 确保userId字段有索引
String sql = "SELECT * FROM orders WHERE user_id = ?";
return jdbcTemplate.queryForList(sql, userId);
}
// 分页查询优化
public List<Map<String, Object>> queryWithPagination(int page, int size) {
// 使用LIMIT和OFFSET
int offset = (page - 1) * size;
String sql = "SELECT * FROM orders ORDER BY create_time DESC LIMIT ? OFFSET ?";
return jdbcTemplate.queryForList(sql, size, offset);
}
// 避免N+1查询问题
public List<OrderDTO> getOrdersWithUsers() {
// 使用JOIN一次性查询
String sql = "SELECT o.*, u.name as user_name " +
"FROM orders o " +
"JOIN users u ON o.user_id = u.id";
return jdbcTemplate.query(sql, (rs, rowNum) -> {
OrderDTO order = new OrderDTO();
order.setId(rs.getLong("id"));
order.setUserId(rs.getLong("user_id"));
order.setUserName(rs.getString("user_name"));
order.setAmount(rs.getBigDecimal("amount"));
return order;
});
}
}
// MyBatis Plus查询优化
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
// 使用索引字段查询
@Select("SELECT * FROM orders WHERE user_id = #{userId} AND status = #{status}")
List<Order> findByUserIdAndStatus(@Param("userId") Long userId,
@Param("status") String status);
// 批量插入
@Insert("<script>" +
"INSERT INTO orders (order_id, user_id, amount) VALUES " +
"<foreach collection='orders' item='order' separator=','>" +
"(#{order.orderId}, #{order.userId}, #{order.amount})" +
"</foreach>" +
"</script>")
void batchInsert(@Param("orders") List<Order> orders);
// 使用分页插件
IPage<Order> selectPage(IPage<Order> page,
@Param("userId") Long userId);
}
2. 系统监控
2.1 APM应用性能监控
Spring Boot Actuator监控:
// Actuator配置
@Configuration
@Slf4j
public class ActuatorConfig {
// 自定义健康检查
@Component
@Slf4j
public static class CustomHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Override
public Health health() {
try {
// 检查数据库连接
try (Connection connection = dataSource.getConnection()) {
if (connection.isValid(1)) {
return Health.up()
.withDetail("database", "可用")
.build();
}
}
} catch (Exception e) {
log.error("健康检查失败", e);
return Health.down()
.withDetail("database", "不可用")
.withException(e)
.build();
}
return Health.down().build();
}
}
// 自定义指标
@Component
@Slf4j
public static class CustomMetrics {
private final MeterRegistry meterRegistry;
private final Counter requestCounter;
private final Timer requestTimer;
private final Gauge activeUsers;
public CustomMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.requestCounter = Counter.builder("custom.requests")
.description("自定义请求计数")
.register(meterRegistry);
this.requestTimer = Timer.builder("custom.request.duration")
.description("自定义请求耗时")
.register(meterRegistry);
this.activeUsers = Gauge.builder("custom.active.users",
() -> getActiveUserCount())
.description("活跃用户数")
.register(meterRegistry);
}
public void incrementRequest() {
requestCounter.increment();
}
public void recordRequestTime(Duration duration) {
requestTimer.record(duration);
}
private double getActiveUserCount() {
// 获取活跃用户数
return 100.0;
}
}
}
// 监控拦截器
@Component
@Slf4j
public class MonitoringInterceptor implements HandlerInterceptor {
@Autowired
private CustomMetrics customMetrics;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
request.setAttribute("startTime", System.currentTimeMillis());
customMetrics.incrementRequest();
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
Long startTime = (Long) request.getAttribute("startTime");
if (startTime != null) {
long duration = System.currentTimeMillis() - startTime;
customMetrics.recordRequestTime(Duration.ofMillis(duration));
log.info("请求路径: {}, 耗时: {} ms",
request.getRequestURI(), duration);
}
}
}
2.2 日志管理
日志配置与优化:
// 日志配置类
@Configuration
@Slf4j
public class LoggingConfig {
// Logback配置(logback-spring.xml)
/*
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<springProfile name="dev">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</springProfile>
<springProfile name="prod">
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/application.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="FILE" />
</root>
</springProfile>
</configuration>
*/
}
// 结构化日志服务
@Service
@Slf4j
public class StructuredLoggingService {
// 使用MDC添加上下文信息
public void logWithContext(String userId, String operation) {
MDC.put("userId", userId);
MDC.put("operation", operation);
MDC.put("requestId", UUID.randomUUID().toString());
try {
log.info("执行操作: {}", operation);
// 业务逻辑
} finally {
MDC.clear();
}
}
// 异步日志
public void logAsync(String message) {
CompletableFuture.runAsync(() -> {
log.info("异步日志: {}", message);
});
}
// 日志采样(降低日志量)
private final AtomicLong logCounter = new AtomicLong(0);
private static final int SAMPLE_RATE = 100; // 每100条记录1条
public void logWithSampling(String message) {
if (logCounter.incrementAndGet() % SAMPLE_RATE == 0) {
log.info("采样日志: {}", message);
}
}
}
// ELK日志收集配置
@Configuration
@Slf4j
public class ElkLoggingConfig {
// Logstash配置
/*
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>logstash-server:5000</destination>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"application":"order-service"}</customFields>
</encoder>
</appender>
*/
}
2.3 指标监控
Prometheus指标监控:
// Prometheus配置
@Configuration
@Slf4j
public class PrometheusConfig {
// 自定义指标
@Component
@Slf4j
public static class BusinessMetrics {
private final Counter orderCounter;
private final Timer orderProcessingTime;
private final Gauge activeOrders;
private final Summary orderAmount;
public BusinessMetrics(MeterRegistry meterRegistry) {
this.orderCounter = Counter.builder("orders.total")
.description("订单总数")
.tag("type", "business")
.register(meterRegistry);
this.orderProcessingTime = Timer.builder("orders.processing.time")
.description("订单处理时间")
.register(meterRegistry);
this.activeOrders = Gauge.builder("orders.active",
() -> getActiveOrderCount())
.description("活跃订单数")
.register(meterRegistry);
this.orderAmount = Summary.builder("orders.amount")
.description("订单金额")
.register(meterRegistry);
}
public void recordOrder(String orderType) {
orderCounter.increment(
Tags.of("order_type", orderType)
);
}
public void recordProcessingTime(Duration duration) {
orderProcessingTime.record(duration);
}
public void recordOrderAmount(BigDecimal amount) {
orderAmount.record(amount.doubleValue());
}
private double getActiveOrderCount() {
// 获取活跃订单数
return 50.0;
}
}
}
// Grafana仪表板配置
@RestController
@RequestMapping("/api/metrics")
@Slf4j
public class MetricsController {
@Autowired
private MeterRegistry meterRegistry;
@GetMapping("/custom")
public Map<String, Object> getCustomMetrics() {
Map<String, Object> metrics = new HashMap<>();
// 获取计数器值
Counter counter = meterRegistry.find("orders.total").counter();
if (counter != null) {
metrics.put("ordersTotal", counter.count());
}
// 获取计时器统计
Timer timer = meterRegistry.find("orders.processing.time").timer();
if (timer != null) {
metrics.put("avgProcessingTime", timer.mean(TimeUnit.MILLISECONDS));
metrics.put("maxProcessingTime", timer.max(TimeUnit.MILLISECONDS));
}
return metrics;
}
}
3. 安全与认证
3.1 JWT认证
JWT工具类:
// JWT工具类
@Component
@Slf4j
public class JwtUtil {
@Value("${jwt.secret:mySecretKey}")
private String secret;
@Value("${jwt.expiration:86400000}")
private Long expiration;
// 生成Token
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("sub", userDetails.getUsername());
claims.put("roles", userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()));
claims.put("iat", new Date());
return createToken(claims, userDetails.getUsername());
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
// 验证Token
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
// 从Token获取用户名
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
// 从Token获取过期时间
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
// 从Token获取指定Claim
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
// 从Token获取所有Claims
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}
// 检查Token是否过期
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
// 刷新Token
public String refreshToken(String token) {
final Claims claims = getAllClaimsFromToken(token);
claims.setIssuedAt(new Date());
return createToken(claims, claims.getSubject());
}
}
// JWT认证过滤器
@Component
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String token = getTokenFromRequest(request);
if (token != null && jwtUtil.validateToken(token,
userDetailsService.loadUserByUsername(
jwtUtil.getUsernameFromToken(token)))) {
String username = jwtUtil.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String getTokenFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
3.2 OAuth2认证
OAuth2配置:
// OAuth2资源服务器配置
@Configuration
@EnableResourceServer
@Slf4j
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
}
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri("http://auth-server/.well-known/jwks.json")
.build();
}
}
// OAuth2授权服务器配置
@Configuration
@EnableAuthorizationServer
@Slf4j
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authenticationManager)
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter());
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("mySecretKey");
return converter;
}
}
3.3 Spring Security配置
Spring Security配置:
// Spring Security配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
// 用户详情服务
@Service
@Slf4j
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username));
List<GrantedAuthority> authorities = user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
authorities
);
}
}
4. 测试与质量
4.1 单元测试
JUnit 5单元测试:
// 服务层单元测试
@ExtendWith(MockitoExtension.class)
@Slf4j
class OrderServiceTest {
@Mock
private OrderRepository orderRepository;
@Mock
private UserRepository userRepository;
@InjectMocks
private OrderService orderService;
@Test
@DisplayName("创建订单成功")
void testCreateOrderSuccess() {
// Given
CreateOrderRequest request = new CreateOrderRequest();
request.setUserId(1L);
request.setAmount(new BigDecimal("100.00"));
User user = new User();
user.setId(1L);
user.setName("测试用户");
Order order = new Order();
order.setId(1L);
order.setUserId(1L);
order.setAmount(new BigDecimal("100.00"));
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
when(orderRepository.save(any(Order.class))).thenReturn(order);
// When
OrderDTO result = orderService.createOrder(request);
// Then
assertNotNull(result);
assertEquals(1L, result.getId());
assertEquals(new BigDecimal("100.00"), result.getAmount());
verify(orderRepository, times(1)).save(any(Order.class));
}
@Test
@DisplayName("用户不存在时创建订单失败")
void testCreateOrderUserNotFound() {
// Given
CreateOrderRequest request = new CreateOrderRequest();
request.setUserId(999L);
when(userRepository.findById(999L)).thenReturn(Optional.empty());
// When & Then
assertThrows(UserNotFoundException.class, () -> {
orderService.createOrder(request);
});
}
@ParameterizedTest
@ValueSource(doubles = {100.0, 200.0, 500.0})
@DisplayName("不同金额的订单创建")
void testCreateOrderWithDifferentAmounts(double amount) {
// Given
CreateOrderRequest request = new CreateOrderRequest();
request.setUserId(1L);
request.setAmount(new BigDecimal(String.valueOf(amount)));
User user = new User();
user.setId(1L);
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
when(orderRepository.save(any(Order.class))).thenAnswer(invocation -> {
Order order = invocation.getArgument(0);
order.setId(1L);
return order;
});
// When
OrderDTO result = orderService.createOrder(request);
// Then
assertNotNull(result);
assertEquals(new BigDecimal(String.valueOf(amount)), result.getAmount());
}
}
4.2 集成测试
Spring Boot集成测试:
// 集成测试
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@Transactional
@Slf4j
class OrderControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private OrderRepository orderRepository;
@Autowired
private UserRepository userRepository;
@Test
@DisplayName("创建订单集成测试")
void testCreateOrderIntegration() throws Exception {
// Given
User user = new User();
user.setUsername("testuser");
user.setPassword("password");
user = userRepository.save(user);
CreateOrderRequest request = new CreateOrderRequest();
request.setUserId(user.getId());
request.setAmount(new BigDecimal("100.00"));
// When
MvcResult result = mockMvc.perform(post("/api/orders")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk())
.andReturn();
// Then
String responseContent = result.getResponse().getContentAsString();
OrderDTO orderDTO = objectMapper.readValue(responseContent, OrderDTO.class);
assertNotNull(orderDTO);
assertEquals(user.getId(), orderDTO.getUserId());
assertEquals(new BigDecimal("100.00"), orderDTO.getAmount());
// 验证数据库
Optional<Order> savedOrder = orderRepository.findById(orderDTO.getId());
assertTrue(savedOrder.isPresent());
}
}
4.3 性能测试
JMeter性能测试:
// 性能测试工具类
@Service
@Slf4j
public class PerformanceTestService {
// 压力测试
public void stressTest(String url, int threads, int iterations) {
ExecutorService executor = Executors.newFixedThreadPool(threads);
CountDownLatch latch = new CountDownLatch(threads);
AtomicLong successCount = new AtomicLong(0);
AtomicLong failCount = new AtomicLong(0);
List<Long> responseTimes = new CopyOnWriteArrayList<>();
for (int i = 0; i < threads; i++) {
executor.submit(() -> {
try {
for (int j = 0; j < iterations; j++) {
long startTime = System.currentTimeMillis();
try {
// 发送HTTP请求
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
if (response.getStatusCode().is2xxSuccessful()) {
successCount.incrementAndGet();
} else {
failCount.incrementAndGet();
}
} catch (Exception e) {
failCount.incrementAndGet();
}
long responseTime = System.currentTimeMillis() - startTime;
responseTimes.add(responseTime);
}
} finally {
latch.countDown();
}
});
}
try {
latch.await();
executor.shutdown();
// 统计结果
log.info("成功请求数: {}", successCount.get());
log.info("失败请求数: {}", failCount.get());
log.info("平均响应时间: {} ms",
responseTimes.stream().mapToLong(Long::longValue).average().orElse(0));
log.info("最大响应时间: {} ms",
responseTimes.stream().mapToLong(Long::longValue).max().orElse(0));
log.info("最小响应时间: {} ms",
responseTimes.stream().mapToLong(Long::longValue).min().orElse(0));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
5. 最佳实践
5.1 代码规范
代码规范检查:
// 使用Checkstyle、PMD、SpotBugs等工具
// 代码规范示例
// 1. 命名规范
public class OrderService { // 类名使用大驼峰
private static final int MAX_RETRY_COUNT = 3; // 常量使用大写下划线
private Long orderId; // 变量名使用小驼峰
public void createOrder() { // 方法名使用小驼峰
// 方法实现
}
}
// 2. 注释规范
/**
* 订单服务类
*
* @author YourName
* @version 1.0
* @since 2024-01-01
*/
public class OrderService {
/**
* 创建订单
*
* @param request 创建订单请求
* @return 订单DTO
* @throws UserNotFoundException 用户不存在异常
*/
public OrderDTO createOrder(CreateOrderRequest request) {
// 实现
return null;
}
}
// 3. 异常处理规范
public class OrderService {
public OrderDTO createOrder(CreateOrderRequest request) {
try {
// 业务逻辑
return processOrder(request);
} catch (UserNotFoundException e) {
log.error("用户不存在: {}", request.getUserId(), e);
throw e;
} catch (Exception e) {
log.error("创建订单失败", e);
throw new OrderCreationException("创建订单失败", e);
}
}
}
5.2 设计模式应用
常用设计模式:
// 策略模式
public interface PaymentStrategy {
void pay(BigDecimal amount);
}
@Component
public class AlipayStrategy implements PaymentStrategy {
@Override
public void pay(BigDecimal amount) {
// 支付宝支付逻辑
}
}
@Component
public class WechatPayStrategy implements PaymentStrategy {
@Override
public void pay(BigDecimal amount) {
// 微信支付逻辑
}
}
@Service
public class PaymentService {
private final Map<String, PaymentStrategy> strategies;
public PaymentService(List<PaymentStrategy> strategyList) {
this.strategies = strategyList.stream()
.collect(Collectors.toMap(
s -> s.getClass().getSimpleName().replace("Strategy", "").toLowerCase(),
Function.identity()
));
}
public void pay(String paymentType, BigDecimal amount) {
PaymentStrategy strategy = strategies.get(paymentType.toLowerCase());
if (strategy == null) {
throw new IllegalArgumentException("不支持的支付方式: " + paymentType);
}
strategy.pay(amount);
}
}
// 观察者模式
public interface OrderObserver {
void onOrderCreated(Order order);
}
@Component
public class EmailNotificationObserver implements OrderObserver {
@Override
public void onOrderCreated(Order order) {
// 发送邮件通知
}
}
@Component
public class InventoryObserver implements OrderObserver {
@Override
public void onOrderCreated(Order order) {
// 更新库存
}
}
@Service
public class OrderService {
private final List<OrderObserver> observers = new ArrayList<>();
public void addObserver(OrderObserver observer) {
observers.add(observer);
}
public void createOrder(Order order) {
// 创建订单
// 通知观察者
observers.forEach(observer -> observer.onOrderCreated(order));
}
}