Spring Security基于IP的访问控制与黑名单配置指南

 更新时间:2026年06月25日 08:46:51   作者:知远漫谈  
在现代 Web 应用程序中,安全性是至关重要的组成部分,Spring Security 作为 Java 生态中最主流的安全框架,提供了强大而灵活的身份认证和授权机制,本文将深入探讨如何使用 Spring Security 实现基于 IP 的访问控制与黑名单管理,需要的朋友可以参考下

引言

在现代 Web 应用程序中,安全性是至关重要的组成部分。随着互联网攻击手段的不断演进,保护系统资源、防止恶意请求和未授权访问已成为开发者的首要任务之一。Spring Security 作为 Java 生态中最主流的安全框架,提供了强大而灵活的身份认证(Authentication)和授权(Authorization)机制。其中,基于 IP 地址的访问控制是一种简单但非常有效的安全策略,尤其适用于防御暴力 破解、爬虫滥用、DDoS 攻击等场景。

本文将深入探讨如何使用 Spring Security 实现基于 IP 的访问控制与黑名单管理,涵盖从基础配置到高级自定义实现的全过程,并结合实际代码示例展示如何构建一个可扩展、高性能的 IP 控制体系。同时,我们将引入 mermaid 图表来帮助理解架构设计与流程逻辑,确保你不仅能“照着做”,更能“懂原理”。

为什么需要基于 IP 的访问控制?

在传统的用户身份验证模型中,系统通常依赖用户名/密码或 Token 进行认证。然而,在某些情况下,攻击者可能绕过这些机制,例如:

  • 暴力尝试登录接口(如 /login
  • 频繁调用公开 API 接口造成服务压力
  • 使用代理池发起分布式请求进行数据抓取
  • 利用已知漏洞反复探测敏感路径

此时,仅靠用户级别的权限控制已经不足以应对风险。引入 IP 层面的访问控制 可以提供一道额外防线,有效识别并阻止来自可疑来源的请求。

根据 OWASP Top Ten 的建议,输入验证和访问控制是防范常见安全威胁的关键措施之一。IP 黑名单正是访问控制的一种具体实践。

Spring Security 简介与核心组件回顾

Spring Security 是一个功能全面的安全框架,它通过过滤器链(Filter Chain)拦截 HTTP 请求,执行一系列安全检查。其核心组件包括:

  • SecurityFilterChain:定义安全过滤器的顺序和规则。
  • AuthenticationManager:处理认证逻辑。
  • AccessDecisionManager:决定是否允许访问受保护资源。
  • HttpServletRequest:可用于获取客户端 IP 地址。

我们将在这些组件的基础上,扩展出一套完整的基于 IP 的访问控制系统。

架构设计:IP 访问控制的整体思路

要实现基于 IP 的访问控制,我们需要考虑以下几个关键点:

  1. 如何获取客户端真实 IP 地址?
  2. 如何判断该 IP 是否在黑名单中?
  3. 如何动态管理黑名单(增删改查)?
  4. 如何与其他安全机制协同工作?
  5. 如何保证性能,避免每次请求都查询数据库?

我们可以采用如下架构:

这个流程展示了典型的“缓存先行 + 后端持久化”模式,能够显著提升响应速度并降低数据库负载。

获取客户端真实 IP 地址

由于现代应用常部署在反向代理(如 Nginx、Cloudflare)之后,直接使用 request.getRemoteAddr() 可能只能获取到代理服务器的 IP。因此,我们必须优先读取标准的 HTTP 头字段,如:

  • X-Forwarded-For
  • X-Real-IP
  • X-Forwarded-Host

下面是一个工具类,用于安全地提取客户端真实 IP:

import javax.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;

public class IpAddressUtil {

    public static String getClientIpAddress(HttpServletRequest request) {
        // 优先从 X-Forwarded-For 中获取
        String xff = request.getHeader("X-Forwarded-For");
        if (StringUtils.hasText(xff)) {
            // X-Forwarded-For 可能包含多个 IP,第一个为原始客户端
            return xff.split(",")[0].trim();
        }

        String realIp = request.getHeader("X-Real-IP");
        if (StringUtils.hasText(realIp)) {
            return realIp.trim();
        }

        // 最后回退到远程地址
        return request.getRemoteAddr();
    }
}

注意:X-Forwarded-For 头可以被伪造,因此在生产环境中应配合可信代理白名单使用,或仅在内部网络中信任这些头信息。

自定义过滤器实现 IP 黑名单拦截

Spring Security 允许我们在过滤器链中插入自定义逻辑。我们将创建一个 IpBlockingFilter,用于在认证之前检查 IP 是否被禁止。

步骤一:定义黑名单存储接口

首先定义一个通用接口,便于后续切换不同实现(内存、数据库、Redis):

import java.util.Set;

public interface IpBlacklistService {
    boolean isBlocked(String ipAddress);
    void blockIp(String ipAddress);
    void unblockIp(String ipAddress);
    Set<String> getAllBlockedIps();
}

步骤二:内存实现(适用于测试)

import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class InMemoryIpBlacklistService implements IpBlacklistService {

    private final Set<String> blockedIps = Collections.newSetFromMap(new ConcurrentHashMap<>());

    @Override
    public boolean isBlocked(String ipAddress) {
        return blockedIps.contains(ipAddress);
    }

    @Override
    public void blockIp(String ipAddress) {
        blockedIps.add(ipAddress);
    }

    @Override
    public void unblockIp(String ipAddress) {
        blockedIps.remove(ipAddress);
    }

    @Override
    public Set<String> getAllBlockedIps() {
        return new HashSet<>(blockedIps);
    }
}

步骤三:创建自定义过滤器

import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class IpBlockingFilter extends OncePerRequestFilter {

    private final IpBlacklistService ipBlacklistService;

    // 定义不需要拦截的路径(如静态资源)
    private final AntPathRequestMatcher allowedRequestMatcher = new AntPathRequestMatcher("/public/**");

    public IpBlockingFilter(IpBlacklistService ipBlacklistService) {
        this.ipBlacklistService = ipBlacklistService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        // 跳过特定路径
        if (allowedRequestMatcher.matches(request)) {
            filterChain.doFilter(request, response);
            return;
        }

        String clientIp = IpAddressUtil.getClientIpAddress(request);

        if (ipBlacklistService.isBlocked(clientIp)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.getWriter().write("{\"error\": \"Access denied by IP blacklist\"}");
            response.setContentType("application/json;charset=UTF-8");
            return;
        }

        filterChain.doFilter(request, response);
    }
}

使用 OncePerRequestFilter 可确保每个请求只执行一次,避免重复过滤。

将自定义过滤器注册进 Spring Security

接下来我们需要将 IpBlockingFilter 添加到 Spring Security 的过滤器链中。推荐将其放在 UsernamePasswordAuthenticationFilter 之前,以便尽早拦截非法请求。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final IpBlacklistService ipBlacklistService;

    public SecurityConfig(IpBlacklistService ipBlacklistService) {
        this.ipBlacklistService = ipBlacklistService;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .requestMatchers("/login").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            )
            .logout(logout -> logout.permitAll())
            // 插入我们的 IP 拦截过滤器
            .addFilterBefore(new IpBlockingFilter(ipBlacklistService),
                            UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

这样,所有进入系统的请求都会先经过 IP 黑名单检查,若命中则直接返回 403 错误。

数据库存储黑名单(JPA 实现)

内存存储适合开发调试,但在集群环境下无法共享状态。更稳健的做法是将黑名单持久化到数据库。

实体类定义

import jakarta.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "ip_blacklist")
public class BlockedIpEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private String ipAddress;

    @Column
    private String reason;

    @Column(nullable = false)
    private LocalDateTime blockedAt;

    // 构造函数
    public BlockedIpEntity() {
        this.blockedAt = LocalDateTime.now();
    }

    public BlockedIpEntity(String ipAddress, String reason) {
        this();
        this.ipAddress = ipAddress;
        this.reason = reason;
    }

    // getter 和 setter 省略...
}

Repository 接口

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface BlockedIpRepository extends JpaRepository<BlockedIpEntity, Long> {
    boolean existsByIpAddress(String ipAddress);
}

数据库实现的服务层

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class DatabaseIpBlacklistService implements IpBlacklistService {

    @Autowired
    private BlockedIpRepository repository;

    @Override
    public boolean isBlocked(String ipAddress) {
        return repository.existsByIpAddress(ipAddress);
    }

    @Override
    public void blockIp(String ipAddress) {
        if (!isBlocked(ipAddress)) {
            BlockedIpEntity entity = new BlockedIpEntity(ipAddress, "Manual block");
            repository.save(entity);
        }
    }

    @Override
    public void unblockIp(String ipAddress) {
        repository.deleteByIpAddress(ipAddress);
    }

    @Override
    public Set<String> getAllBlockedIps() {
        List<BlockedIpEntity> all = repository.findAll();
        return all.stream()
                  .map(BlockedIpEntity::getIpAddress)
                  .collect(Collectors.toCollection(HashSet::new));
    }
}

注意:这里假设你的项目已正确配置了 Spring Data JPA 和数据库连接。你可以参考 Spring Data JPA 官方文档 来完成初始化设置。

性能优化:引入缓存机制(Caffeine)

频繁查询数据库会影响性能。我们可以使用本地缓存(如 Caffeine)来减少数据库访问次数。

添加依赖(Maven)

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

缓存增强版服务

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;

@Service
public class CachedDatabaseIpBlacklistService implements IpBlacklistService {

    private final DatabaseIpBlacklistService databaseService;
    private final Cache<String, Boolean> cache;

    public CachedDatabaseIpBlacklistService(DatabaseIpBlacklistService databaseService) {
        this.databaseService = databaseService;
        this.cache = Caffeine.newBuilder()
                             .expireAfterWrite(10, TimeUnit.MINUTES)
                             .maximumSize(10000)
                             .build();
    }

    @Override
    public boolean isBlocked(String ipAddress) {
        return cache.get(ipAddress, key -> databaseService.isBlocked(key));
    }

    @Override
    public void blockIp(String ipAddress) {
        databaseService.blockIp(ipAddress);
        cache.put(ipAddress, true); // 显式放入缓存
    }

    @Override
    public void unblockIp(String ipAddress) {
        databaseService.unblockIp(ipAddress);
        cache.invalidate(ipAddress);
    }

    @Override
    public Set<String> getAllBlockedIps() {
        return databaseService.getAllBlockedIps();
    }
}

使用缓存后,99% 的查询将在毫秒内完成,极大提升了系统吞吐量。

动态管理黑名单:REST API 接口

为了方便运维人员操作,我们可以暴露一组 REST 接口用于管理黑名单。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Set;

@RestController
@RequestMapping("/api/admin/ip-blacklist")
public class IpBlacklistController {

    @Autowired
    private IpBlacklistService ipBlacklistService;

    @GetMapping
    public ResponseEntity<Set<String>> getBlockedIps() {
        return ResponseEntity.ok(ipBlacklistService.getAllBlockedIps());
    }

    @PostMapping("/block")
    public ResponseEntity<String> blockIp(@RequestParam String ip) {
        ipBlacklistService.blockIp(ip);
        return ResponseEntity.ok("IP blocked: " + ip);
    }

    @PostMapping("/unblock")
    public ResponseEntity<String> unblockIp(@RequestParam String ip) {
        ipBlacklistService.unblockIp(ip);
        return ResponseEntity.ok("IP unblocked: " + ip);
    }
}

配合前端界面或命令行工具,即可实现可视化管理。

分布式环境下的挑战与解决方案

在微服务或多实例部署中,单机内存缓存不再适用。此时可考虑以下方案:

方案一:使用 Redis 作为共享黑名单存储

Redis 提供高性能的键值存储,支持过期时间,非常适合用于 IP 黑名单。

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.Set;

@Service
public class RedisIpBlacklistService implements IpBlacklistService {

    private final StringRedisTemplate redisTemplate;
    private static final String BLACKLIST_PREFIX = "blacklist:ip:";

    public RedisIpBlacklistService(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public boolean isBlocked(String ipAddress) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(BLACKLIST_PREFIX + ipAddress));
    }

    @Override
    public void blockIp(String ipAddress) {
        redisTemplate.opsForValue().set(BLACKLIST_PREFIX + ipAddress, "1", Duration.ofDays(7));
    }

    @Override
    public void unblockIp(String ipAddress) {
        redisTemplate.delete(BLACKLIST_PREFIX + ipAddress);
    }

    @Override
    public Set<String> getAllBlockedIps() {
        // 实际项目中可通过 SCAN 命令遍历所有键
        throw new UnsupportedOperationException("SCAN not implemented for large datasets");
    }
}

方案二:结合消息队列实现跨节点同步

当使用内存存储时,可通过 Kafka 或 RabbitMQ 广播“IP 封禁”事件,通知其他节点更新本地缓存。

这种方式适合对实时性要求较高的系统。

基于频率的自动封禁机制(IP 限流)

除了手动添加黑名单外,我们还可以根据行为特征自动封禁异常 IP。例如:同一 IP 在 1 分钟内尝试登录失败超过 5 次,则自动加入黑名单。

自定义监听器捕获失败登录事件

import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.stereotype.Component;

@Component
public class LoginFailureEventListener implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> {

    private final IpLoginAttemptService attemptService;

    public LoginFailureEventListener(IpLoginAttemptService attemptService) {
        this.attemptService = attemptService;
    }

    @Override
    public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent event) {
        var details = (org.springframework.security.web.authentication.WebAuthenticationDetails) event.getAuthentication().getDetails();
        String ip = details.getRemoteAddress();

        attemptService.recordFailure(ip);

        if (attemptService.getFailures(ip) >= 5) {
            attemptService.blockIp(ip);
            System.out.println("Automatically blocked IP due to repeated failures: " + ip);
        }
    }
}

登录尝试记录服务(基于 Caffeine)

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;

@Service
public class IpLoginAttemptService {

    private final Cache<String, Integer> loginFailures;
    private final IpBlacklistService ipBlacklistService;

    public IpLoginAttemptService(IpBlacklistService ipBlacklistService) {
        this.ipBlacklistService = ipBlacklistService;
        this.loginFailures = Caffeine.newBuilder()
                                     .expireAfterWrite(1, TimeUnit.MINUTES)
                                     .maximumSize(1000)
                                     .build();
    }

    public void recordFailure(String ip) {
        loginFailures.asMap().merge(ip, 1, Integer::sum);
    }

    public int getFailures(String ip) {
        return loginFailures.getIfPresent(ip) != null ? loginFailures.getIfPresent(ip) : 0;
    }

    public void blockIp(String ip) {
        ipBlacklistService.blockIp(ip);
        loginFailures.invalidate(ip); // 清除计数
    }

    public void clearFailures(String ip) {
        loginFailures.invalidate(ip);
    }
}

成功登录后也应调用 clearFailures(ip) 以重置计数。

单元测试:验证 IP 过滤器行为

良好的测试是保障系统稳定的基石。下面我们编写一个简单的单元测试来验证 IpBlockingFilter 的功能。

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;

class IpBlockingFilterTest {

    private IpBlacklistService ipBlacklistService;
    private IpBlockingFilter filter;

    @BeforeEach
    void setUp() {
        ipBlacklistService = mock(IpBlacklistService.class);
        filter = new IpBlockingFilter(ipBlacklistService);
    }

    @Test
    void shouldBlockWhenIpIsInBlacklist() throws Exception {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        MockFilterChain chain = new MockFilterChain();

        request.setRemoteAddr("192.168.1.100");

        when(ipBlacklistService.isBlocked("192.168.1.100")).thenReturn(true);

        filter.doFilter(request, response, chain);

        assertThat(response.getStatus()).isEqualTo(403);
        assertThat(chain.getRequest()).isNull(); // 表示未继续传递
    }

    @Test
    void shouldAllowWhenIpNotInBlacklist() throws Exception {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        MockFilterChain chain = new MockFilterChain();

        request.setRemoteAddr("192.168.1.101");

        when(ipBlacklistService.isBlocked("192.168.1.101")).thenReturn(false);

        filter.doFilter(request, response, chain);

        assertThat(response.getStatus()).isNotEqualTo(403);
        assertThat(chain.getRequest()).isNotNull();
    }
}

使用 spring-boot-starter-test 可轻松运行上述测试。

高级技巧:与 Spring Cloud Gateway 结合

如果你的应用使用了 Spring Cloud Gateway 作为统一入口网关,那么更适合将 IP 黑名单逻辑前置到网关层,避免流量到达下游服务。

示例:自定义 GlobalFilter

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class GatewayIpBlockingFilter implements GlobalFilter, Ordered {

    private final IpBlacklistService ipBlacklistService;

    public GatewayIpBlockingFilter(IpBlacklistService ipBlacklistService) {
        this.ipBlacklistService = ipBlacklistService;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String ip = IpAddressUtil.getClientIpAddress(exchange.getRequest());
        if (ipBlacklistService.isBlocked(ip)) {
            exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -100; // 非常高的优先级
    }
}

最佳实践总结

实践说明
✅ 优先使用可信代理头避免 X-Forwarded-For 被伪造
✅ 使用缓存减少数据库压力提升响应速度
✅ 支持动态更新无需重启即可生效
✅ 日志记录封禁行为便于审计与排查
✅ 设置自动解封机制如 TTL 过期自动移除
✅ 结合限流与行为分析实现智能风控

安全监控建议

仅仅封禁 IP 并不足够,还应建立完善的监控体系:

  • 记录被拦截的 IP 和时间
  • 统计高频攻击源地域分布
  • 设置告警规则(如每分钟超过 100 次拦截触发邮件通知)
  • 可视化仪表盘展示趋势

推荐结合 ELK(Elasticsearch + Logstash + Kibana)或 Prometheus + Grafana 实现日志聚合与可视化。

替代方案对比

方案优点缺点适用场景
内存存储快速、无依赖不支持集群单机测试
数据库存储持久化、可审计查询慢中小规模
Redis 存储高性能、支持 TTL需维护中间件分布式系统
CDN 层拦截减轻源站压力配置复杂高流量网站

选择哪种方案取决于你的业务规模与架构复杂度。

结语

通过本文的学习,你应该已经掌握了如何在 Spring Security 中实现基于 IP 的访问控制与黑名单管理。无论是简单的内存存储,还是复杂的分布式缓存方案,都可以根据实际需求灵活选择。

记住:安全不是一次性工程,而是一个持续改进的过程。定期审查黑名单、分析攻击模式、升级防护策略,才能真正构筑坚固的防线。

“最好的防御,是让攻击者根本不知道你的存在。” —— 但这并不妨碍我们为可能出现的风险做好万全准备。

现在,就动手为你自己的系统加上这道“隐形防火墙”吧!

以上就是Spring Security基于IP的访问控制与黑名单配置指南的详细内容,更多关于Spring Security IP访问控制与黑名单配置的资料请关注脚本之家其它相关文章!

相关文章

  • 在Spring-Boot中如何使用@Value注解注入集合类

    在Spring-Boot中如何使用@Value注解注入集合类

    这篇文章主要介绍了在Spring-Boot中如何使用@Value注解注入集合类的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • 一文搞懂Java的ThreadPoolExecutor原理

    一文搞懂Java的ThreadPoolExecutor原理

    都说经典的就是好的,这句话放在Java的ThreadPoolExecutor上那是一点都没错,像现在数据库连接的池化实现,或者像Tomcat这种WEB服务器的线程管理,处处都有着ThreadPoolExecutor的影子,本篇文章将结合源码实现,对ThreadPoolExecutor的原理进行一个深入学习
    2023-06-06
  • 解决idea创建版本时只有Java21和Java17选项

    解决idea创建版本时只有Java21和Java17选项

    你是否在使用IntelliJ IDEA创建新项目时遇到了只有Java 21和Java 17的选项?别担心,我们的指南将为你提供解决方案,通过简单的步骤,你将能够选择你需要的任何Java版本,继续阅读,让我们开始吧!
    2024-03-03
  • 分布式组件Gateway技术栈系统性理解和操作

    分布式组件Gateway技术栈系统性理解和操作

    这篇文章主要介绍了分布式组件Gateway技术栈系统性理解和操作,要真正理解和使用Gateway技术栈,需要从架构认知、核心机制、实践应用三个维度建立系统性理解,需要的朋友可以参考下
    2026-02-02
  • Java基础学习之运算符相关知识总结

    Java基础学习之运算符相关知识总结

    今天带大家复习Java基础知识,文中对Java运算符相关知识作了详细总结,对正在学习java基础的小伙伴们很有帮助,需要的朋友可以参考下
    2021-05-05
  • springmvc流程图以及配置解析

    springmvc流程图以及配置解析

    这篇文章主要介绍了springmvc流程图以及配置解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • Maven中生命周期深度解析与实战指南

    Maven中生命周期深度解析与实战指南

    这篇文章主要为大家详细介绍了Maven 生命周期实战指南,包含核心概念、阶段详解、Spring Boot 特化场景及企业级实践建议,希望对大家有一定的帮助
    2025-08-08
  • Java中CompletableFuture 的详细介绍

    Java中CompletableFuture 的详细介绍

    这篇文章主要介绍了Java中的CompletableFuture,通过创建 CompletableFuture 的对象的工厂方法展开详细的内容介绍,需要的小伙伴可以参考一下
    2022-05-05
  • 不使用Math.random方法生成随机数(随机数生成器)

    不使用Math.random方法生成随机数(随机数生成器)

    不调用Math.random方法产生自己的随机数,现代计算机运行速度很快,在主线程等待一定毫秒数时,其他线程就会执行run方法中的while循环,一般会执行数十万次
    2014-01-01
  • 基于swagger参数与实体中参数不一致的原因分析

    基于swagger参数与实体中参数不一致的原因分析

    这篇文章主要介绍了基于swagger参数与实体中参数不一致的原因分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11

最新评论