欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

springboot自定義注解RateLimiter限流注解技術文檔詳解

 更新時間:2025年07月25日 15:44:14   作者:yzhSWJ  
文章介紹了限流技術的概念、作用及實現(xiàn)方式,通過Spring AOP攔截方法、緩存存儲計數(shù)器,結合注解、枚舉、異常類等核心組件,實現(xiàn)對訪問頻率的控制,旨在防止攻擊、保障系統(tǒng)穩(wěn)定、提升用戶體驗及節(jié)約成本

什么是限流

限流是一種控制系統(tǒng)訪問頻率的技術手段,就像高速公路的收費站控制車流量一樣。

生活場景類比:

  • 銀行ATM機:每張卡每天最多取款5次
  • 手機驗證碼:每個手機號每分鐘最多發(fā)送1條
  • 網(wǎng)站登錄:每個IP每分鐘最多嘗試5次

技術價值:

  1. 防止惡意攻擊:阻止暴力破解、惡意爬蟲
  2. 保護系統(tǒng)穩(wěn)定:避免瞬間大量請求壓垮服務器
  3. 提升用戶體驗:確保正常用戶的訪問質(zhì)量
  4. 節(jié)約成本:減少不必要的資源消耗

系統(tǒng)架構

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   用戶請求      │───→│   限流切面      │───→│   業(yè)務接口      │
│   (HTTP API)   │    │  (AOP攔截)     │    │  (Controller)   │
└─────────────────┘    └─────────────────┘    └─────────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │   限流服務      │
                    │ (核心邏輯處理)  │
                    └─────────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │   緩存存儲      │
                    │ (EhCache/Redis) │
                    └─────────────────┘

工作流程:

  1. 用戶發(fā)起HTTP請求
  2. Spring AOP切面攔截帶有@RateLimiter注解的方法
  3. 限流服務根據(jù)注解配置生成限流鍵
  4. 從緩存中獲取當前訪問次數(shù)
  5. 判斷是否超過限制,決定放行或拒絕
  6. 更新緩存中的計數(shù)器

核心組件詳解

1. 限流注解 (@RateLimiter)

這是系統(tǒng)的核心注解,定義了限流的各種參數(shù):

package cn.jbolt.config.anno.rateLimiter;

import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface RateLimiter {

    /**
     * 緩存前綴 - 用于區(qū)分不同業(yè)務的限流數(shù)據(jù)
     */
    String prefix() default "jblimit:";

    /**
     * 時間窗口(秒) - 限流的時間范圍
     */
    int time() default 60;

    /**
     * 允許訪問次數(shù) - 時間窗口內(nèi)最大訪問次數(shù)
     */
    @AliasFor(attribute = "count")
    int value() default 12;

    /**
     * 限制類型 - 決定按什么維度限流
     */
    RateLimitType limitType() default RateLimitType.DEFAULT;

    /**
     * 限制提示消息 - 觸發(fā)限流時返回的錯誤信息
     */
    String msg() default "操作過于頻繁,請稍后重試";

    /**
     * 允許訪問次數(shù) - 與value互為別名
     */
    @AliasFor(attribute = "value")
    int count() default 12;

    /**
     * 自定義鍵 - 當limitType為CUSTOM時使用
     */
    String customKey() default "";

    /**
     * 是否啟用 - 可用于動態(tài)開關限流功能
     */
    boolean enabled() default true;

    /**
     * 額外的時間窗口限制(秒)
     * 實現(xiàn)雙重限流:比如1秒最多1次 + 1分鐘最多10次
     */
    int extraTime() default -1;

    /**
     * 額外時間窗口內(nèi)的允許訪問次數(shù)
     */
    int extraCount() default -1;

    /**
     * 額外限制的提示消息
     */
    String extraMsg() default "";
}

2. 限流類型枚舉 (RateLimitType)

package cn.jbolt.config.anno.rateLimiter;

public enum RateLimitType {
    /**
     * 默認限制(全局)
     * 所有請求共享一個計數(shù)器
     */
    DEFAULT,
    
    /**
     * 基于IP地址限制
     * 每個IP獨立計數(shù)
     */
    IP,
    
    /**
     * 基于用戶ID限制
     * 每個登錄用戶獨立計數(shù)
     */
    USER,
    
    /**
     * 基于自定義KEY限制
     * 根據(jù)業(yè)務邏輯自定義限流維度
     */
    CUSTOM
}

3. 限流異常類 (RateLimitException)

package cn.jbolt.config.exception;

public class RateLimitException extends RuntimeException {
    
    private final String message;
    private final int retryAfter;
    
    public RateLimitException(String message) {
        this(message, 0);
    }
    
    public RateLimitException(String message, int retryAfter) {
        super(message);
        this.message = message;
        this.retryAfter = retryAfter;
    }
    
    @Override
    public String getMessage() {
        return message;
    }
    
    public int getRetryAfter() {
        return retryAfter;
    }
}

4. 全局異常處理器 (RateLimitExceptionHandler)

package cn.jbolt.config.handler;

import cn.jbolt.config.exception.RateLimitException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class RateLimitExceptionHandler {
    
    @ExceptionHandler(RateLimitException.class)
    public ResponseEntity<Map<String, Object>> handleRateLimitException(
            RateLimitException e, HttpServletResponse response) {
        
        Map<String, Object> result = new HashMap<>();
        result.put("code", HttpStatus.TOO_MANY_REQUESTS.value());
        result.put("message", e.getMessage());
        result.put("data", null);
        
        // 設置HTTP響應頭,告訴客戶端多久后可以重試
        if (e.getRetryAfter() > 0) {
            response.setHeader("Retry-After", String.valueOf(e.getRetryAfter()));
        }
        response.setHeader("X-RateLimit-Window", "60");
        
        return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(result);
    }
}

5. IP工具類 (IpUtils)

package cn.jbolt.util;

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

public class IpUtils {
    
    private static final String[] IP_HEADER_NAMES = {
        "X-Forwarded-For",
        "X-Real-IP", 
        "Proxy-Client-IP",
        "WL-Proxy-Client-IP",
        "HTTP_CLIENT_IP",
        "HTTP_X_FORWARDED_FOR"
    };
    
    private static final String UNKNOWN = "unknown";
    private static final String LOCALHOST_IPV4 = "127.0.0.1";
    private static final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1";
    
    /**
     * 獲取客戶端真實IP地址
     * 處理代理服務器、負載均衡器等場景
     */
    public static String getClientIp(HttpServletRequest request) {
        if (request == null) {
            return UNKNOWN;
        }
        
        String ip = null;
        
        // 依次檢查各種可能的IP頭
        for (String header : IP_HEADER_NAMES) {
            ip = request.getHeader(header);
            if (isValidIp(ip)) {
                break;
            }
        }
        
        // 如果頭信息中沒有找到,則使用getRemoteAddr
        if (!isValidIp(ip)) {
            ip = request.getRemoteAddr();
            if (LOCALHOST_IPV6.equals(ip)) {
                ip = LOCALHOST_IPV4;
            }
        }
        
        // 處理多個IP的情況(X-Forwarded-For可能包含多個IP)
        if (StringUtils.hasText(ip) && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        
        return StringUtils.hasText(ip) ? ip : UNKNOWN;
    }
    
    /**
     * 檢查IP是否有效
     */
    private static boolean isValidIp(String ip) {
        return StringUtils.hasText(ip) && !UNKNOWN.equalsIgnoreCase(ip);
    }
}

技術實現(xiàn)原理

1. AOP切面攔截

系統(tǒng)使用Spring AOP在方法執(zhí)行前進行攔截,這是一個核心的限流切面類:

package cn.jbolt.config.aspect;

import cn.jbolt.config.anno.rateLimiter.RateLimiter;
import cn.jbolt.config.anno.rateLimiter.RateLimitType;
import cn.jbolt.config.exception.RateLimitException;
import cn.jbolt.util.IpUtils;
import cn.jbolt.util.cache.RateLimiterCache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class RateLimiterAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(RateLimiterAspect.class);
    
    @Around("@annotation(rateLimiter)")
    public Object around(ProceedingJoinPoint point, RateLimiter rateLimiter) throws Throwable {
        
        // 檢查是否啟用限流
        if (!rateLimiter.enabled()) {
            return point.proceed();
        }
        
        // 獲取HTTP請求對象
        HttpServletRequest request = getCurrentRequest();
        if (request == null) {
            logger.warn("無法獲取HttpServletRequest,跳過限流檢查");
            return point.proceed();
        }
        
        // 生成限流鍵
        String limitKey = generateLimitKey(point, rateLimiter, request);
        
        // 執(zhí)行主要限流檢查
        checkRateLimit(limitKey, rateLimiter.time(), rateLimiter.count(), rateLimiter.msg());
        
        // 執(zhí)行額外限流檢查(如果配置了)
        if (rateLimiter.extraTime() > 0 && rateLimiter.extraCount() > 0) {
            String extraLimitKey = limitKey + ":extra";
            String extraMsg = rateLimiter.extraMsg().isEmpty() ? rateLimiter.msg() : rateLimiter.extraMsg();
            checkRateLimit(extraLimitKey, rateLimiter.extraTime(), rateLimiter.extraCount(), extraMsg);
        }
        
        // 所有限流檢查通過,繼續(xù)執(zhí)行業(yè)務方法
        return point.proceed();
    }
    
    /**
     * 執(zhí)行限流檢查
     */
    private void checkRateLimit(String key, int timeWindow, int maxCount, String message) {
        try {
            // 增加計數(shù)器并獲取當前訪問次數(shù)
            int currentCount = RateLimiterCache.incrementAndGet(key, timeWindow, TimeUnit.SECONDS);
            
            logger.debug("限流檢查: key={}, 當前次數(shù)={}, 限制次數(shù)={}", key, currentCount, maxCount);
            
            // 檢查是否超過限制
            if (currentCount > maxCount) {
                long ttl = RateLimiterCache.getTtl(key);
                logger.warn("觸發(fā)限流: key={}, 當前次數(shù)={}, 限制次數(shù)={}, 剩余時間={}秒", 
                          key, currentCount, maxCount, ttl);
                throw new RateLimitException(message, (int) ttl);
            }
            
        } catch (RateLimitException e) {
            throw e;
        } catch (Exception e) {
            logger.error("限流檢查異常: key={}", key, e);
            // 限流服務異常時,選擇放行而不是阻塞
        }
    }
    
    /**
     * 生成限流鍵
     */
    private String generateLimitKey(ProceedingJoinPoint point, RateLimiter rateLimiter, HttpServletRequest request) {
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(rateLimiter.prefix());
        
        // 添加方法簽名
        String methodSignature = point.getSignature().toShortString();
        keyBuilder.append(methodSignature);
        
        // 根據(jù)限流類型添加不同的標識
        switch (rateLimiter.limitType()) {
            case IP:
                keyBuilder.append(":ip:").append(IpUtils.getClientIp(request));
                break;
            case USER:
                String userId = getCurrentUserId(request);
                keyBuilder.append(":user:").append(userId != null ? userId : "anonymous");
                break;
            case CUSTOM:
                keyBuilder.append(":custom:").append(rateLimiter.customKey());
                break;
            case DEFAULT:
            default:
                keyBuilder.append(":default:global");
                break;
        }
        
        // 添加時間窗口,確保不同時間窗口的限流獨立
        keyBuilder.append(":").append(rateLimiter.time());
        
        String finalKey = keyBuilder.toString();
        logger.debug("生成限流鍵: {}", finalKey);
        return finalKey;
    }
    
    /**
     * 獲取當前HTTP請求
     */
    private HttpServletRequest getCurrentRequest() {
        try {
            ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            return attrs != null ? attrs.getRequest() : null;
        } catch (Exception e) {
            logger.warn("獲取HttpServletRequest失敗", e);
            return null;
        }
    }
    
    /**
     * 獲取當前用戶ID
     * 這里需要根據(jù)實際的用戶認證體系來實現(xiàn)
     */
    private String getCurrentUserId(HttpServletRequest request) {
        // 方案1:從Session中獲取
        Object userId = request.getSession().getAttribute("userId");
        if (userId != null) {
            return userId.toString();
        }
        
        // 方案2:從JWT Token中獲取
        String token = request.getHeader("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            // 解析JWT獲取用戶ID
            // return JwtUtils.getUserIdFromToken(token);
        }
        
        // 方案3:從請求參數(shù)中獲取
        String userIdParam = request.getParameter("userId");
        if (userIdParam != null) {
            return userIdParam;
        }
        
        return null;
    }
}

2. 緩存數(shù)據(jù)結構

系統(tǒng)使用一個包裝類來存儲緩存數(shù)據(jù):

package cn.jbolt.util.cache;

import java.io.Serializable;
import java.util.concurrent.TimeUnit;

public class CacheWrapper implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    private Object value;
    private long timestamp;
    private long durationMillis;
    
    public CacheWrapper() {
    }
    
    public CacheWrapper(Object value, long duration, TimeUnit unit) {
        this.value = value;
        this.timestamp = System.currentTimeMillis();
        this.durationMillis = unit.toMillis(duration);
    }
    
    /**
     * 檢查是否已過期
     */
    public boolean isExpired() {
        return System.currentTimeMillis() - timestamp > durationMillis;
    }
    
    /**
     * 獲取剩余過期時間(毫秒)
     */
    public long getRemainingTime() {
        long elapsed = System.currentTimeMillis() - timestamp;
        return Math.max(0, durationMillis - elapsed);
    }
    
    // getter和setter方法
    public Object getValue() {
        return value;
    }
    
    public void setValue(Object value) {
        this.value = value;
    }
    
    public long getTimestamp() {
        return timestamp;
    }
    
    public void setTimestamp(long timestamp) {
        this.timestamp = timestamp;
    }
    
    public long getDurationMillis() {
        return durationMillis;
    }
    
    public void setDurationMillis(long durationMillis) {
        this.durationMillis = durationMillis;
    }
}

完整代碼示例

1. 控制器示例

package cn.jbolt.controller;

import cn.jbolt.config.anno.rateLimiter.RateLimiter;
import cn.jbolt.config.anno.rateLimiter.RateLimitType;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class DemoController {
    
    /**
     * 登錄接口 - 防止暴力破解
     * 每個IP每分鐘最多嘗試5次
     */
    @PostMapping("/login")
    @RateLimiter(
        limitType = RateLimitType.IP,
        time = 60, 
        count = 5,
        msg = "登錄嘗試過于頻繁,請1分鐘后重試"
    )
    public Result login(@RequestBody LoginRequest request) {
        // 登錄邏輯
        if (isValidUser(request.getUsername(), request.getPassword())) {
            return Result.success("登錄成功");
        } else {
            return Result.error("用戶名或密碼錯誤");
        }
    }
    
    /**
     * 發(fā)送驗證碼 - 防止惡意發(fā)送
     * 每個IP每分鐘最多3次
     */
    @PostMapping("/sms/send")
    @RateLimiter(
        limitType = RateLimitType.IP,
        time = 60, 
        count = 3,
        msg = "驗證碼發(fā)送過于頻繁,請稍后重試"
    )
    public Result sendSms(@RequestBody SmsRequest request) {
        // 發(fā)送短信邏輯
        boolean success = smsService.sendCode(request.getPhone());
        return success ? Result.success("發(fā)送成功") : Result.error("發(fā)送失敗");
    }
    
    /**
     * 查詢接口 - 防止爬蟲
     * 每個IP每分鐘最多100次
     */
    @GetMapping("/products")
    @RateLimiter(
        limitType = RateLimitType.IP,
        time = 60, 
        count = 100,
        msg = "查詢過于頻繁,請稍后重試"
    )
    public Result getProducts(@RequestParam(defaultValue = "1") int page) {
        // 查詢商品邏輯
        List<Product> products = productService.getProducts(page);
        return Result.success(products);
    }
    
    /**
     * 用戶操作 - 防止頻繁操作
     * 每個用戶每分鐘最多30次
     */
    @PostMapping("/user/update")
    @RateLimiter(
        limitType = RateLimitType.USER,
        time = 60, 
        count = 30,
        msg = "操作過于頻繁,請稍后重試"
    )
    public Result updateUser(@RequestBody UserUpdateRequest request) {
        // 更新用戶信息邏輯
        boolean success = userService.updateUser(request);
        return success ? Result.success("更新成功") : Result.error("更新失敗");
    }
    
    /**
     * 關鍵操作 - 嚴格限流
     * 1秒最多1次 + 1分鐘最多5次
     */
    @PostMapping("/transfer")
    @RateLimiter(
        limitType = RateLimitType.USER,
        time = 1, count = 1, msg = "操作過于頻繁,請稍后再試",
        extraTime = 60, extraCount = 5, extraMsg = "您在1分鐘內(nèi)的操作次數(shù)已達上限"
    )
    public Result transfer(@RequestBody TransferRequest request) {
        // 轉賬邏輯
        boolean success = transferService.transfer(request);
        return success ? Result.success("轉賬成功") : Result.error("轉賬失敗");
    }
    
    /**
     * 自定義限流 - 按商品限制
     * 每個商品每分鐘最多下單20次
     */
    @PostMapping("/order/{productId}")
    @RateLimiter(
        limitType = RateLimitType.CUSTOM,
        customKey = "product_order",
        time = 60, 
        count = 20,
        msg = "該商品下單過于頻繁,請稍后重試"
    )
    public Result createOrder(@PathVariable String productId, @RequestBody OrderRequest request) {
        // 創(chuàng)建訂單邏輯
        Order order = orderService.createOrder(productId, request);
        return Result.success(order);
    }
    
    // 輔助方法
    private boolean isValidUser(String username, String password) {
        // 實際的用戶驗證邏輯
        return "admin".equals(username) && "123456".equals(password);
    }
}

2. 統(tǒng)一返回對象

package cn.jbolt.common;

public class Result {
    private int code;
    private String message;
    private Object data;
    
    public static Result success(Object data) {
        Result result = new Result();
        result.code = 200;
        result.message = "success";
        result.data = data;
        return result;
    }
    
    public static Result error(String message) {
        Result result = new Result();
        result.code = 500;
        result.message = message;
        result.data = null;
        return result;
    }
    
    // getter和setter方法
    public int getCode() {
        return code;
    }
    
    public void setCode(int code) {
        this.code = code;
    }
    
    public String getMessage() {
        return message;
    }
    
    public void setMessage(String message) {
        this.message = message;
    }
    
    public Object getData() {
        return data;
    }
    
    public void setData(Object data) {
        this.data = data;
    }
}

使用指南

1. 基本使用

// 最簡單的用法 - 使用默認配置
@RateLimiter(limitType = RateLimitType.IP)
public String simpleApi() {
    return "success";
}

// 自定義時間窗口和次數(shù)
@RateLimiter(
    limitType = RateLimitType.IP,
    time = 60,    // 60秒
    count = 100   // 最多100次
)
public String customApi() {
    return "success";
}

2. 不同場景的配置建議

// 登錄接口 - 嚴格限制
@RateLimiter(
    limitType = RateLimitType.IP,
    time = 60, count = 5,
    msg = "登錄嘗試過于頻繁,請1分鐘后重試"
)

// 查詢接口 - 適中限制
@RateLimiter(
    limitType = RateLimitType.IP,
    time = 60, count = 100,
    msg = "查詢過于頻繁,請稍后重試"
)

// 用戶操作 - 按用戶限制
@RateLimiter(
    limitType = RateLimitType.USER,
    time = 60, count = 30,
    msg = "操作過于頻繁,請稍后重試"
)

// 全局保護 - 系統(tǒng)級限制
@RateLimiter(
    limitType = RateLimitType.DEFAULT,
    time = 60, count = 200,
    msg = "系統(tǒng)繁忙,請稍后重試"
)

3. 雙重限流配置

// 嚴格的雙重限流:秒級 + 分鐘級
@RateLimiter(
    limitType = RateLimitType.IP,
    time = 1, count = 1, msg = "請求過于頻繁,請稍后再試",
    extraTime = 60, extraCount = 10, extraMsg = "您在1分鐘內(nèi)的請求次數(shù)已達上限"
)

// 適中的雙重限流:分鐘級 + 小時級
@RateLimiter(
    limitType = RateLimitType.IP,
    time = 60, count = 100, msg = "1分鐘內(nèi)請求過多",
    extraTime = 3600, extraCount = 1000, extraMsg = "1小時內(nèi)請求過多"
)

總結

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • SpringBoot參數(shù)驗證的幾種方式小結

    SpringBoot參數(shù)驗證的幾種方式小結

    在日常的接口開發(fā)中,為了防止非法參數(shù)對業(yè)務造成影響,經(jīng)常需要對接口的參數(shù)進行校驗,例如登錄的時候需要校驗用戶名和密碼是否為空,所以本文介紹了SpringBoot參數(shù)驗證的幾種方式,需要的朋友可以參考下
    2024-07-07
  • 使用Feign?logging?開啟調(diào)用日志

    使用Feign?logging?開啟調(diào)用日志

    這篇文章主要介紹了使用Feign?logging?開啟調(diào)用日志,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Mybatis配置之properties和settings標簽的用法

    Mybatis配置之properties和settings標簽的用法

    這篇文章主要介紹了Mybatis配置之properties和settings標簽的用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java傳引用問題舉例詳解

    Java傳引用問題舉例詳解

    引用傳遞是Java中一個重要的概念,它與值傳遞不同,可以使得函數(shù)或方法可以修改傳入的對象,這篇文章主要給大家介紹了關于Java傳引用問題的相關資料,需要的朋友可以參考下
    2024-07-07
  • 淺析Java多線程同步synchronized

    淺析Java多線程同步synchronized

    本篇文章給大家詳細分析了Java多線程同步synchronized的相關知識點,需要的讀者們可以參考學習下。
    2018-02-02
  • Java Map 在put值時value值不被覆蓋的解決辦法

    Java Map 在put值時value值不被覆蓋的解決辦法

    這篇文章主要介紹了Java Map 在put值時value值不被覆蓋的解決辦法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-04-04
  • 詳解Spring Cloud中Hystrix 線程隔離導致ThreadLocal數(shù)據(jù)丟失

    詳解Spring Cloud中Hystrix 線程隔離導致ThreadLocal數(shù)據(jù)丟失

    這篇文章主要介紹了詳解Spring Cloud中Hystrix 線程隔離導致ThreadLocal數(shù)據(jù)丟失,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • java多線程編程之Synchronized塊同步方法

    java多線程編程之Synchronized塊同步方法

    這篇文章主要介紹了java多線程編程之Synchronized塊同步方法,synchronized關鍵字又稱同步鎖,當方法執(zhí)行完后,會自動釋放鎖鎖,只有一個線程能進入此方法,看看下文中各種例子對synchronized的詳細解釋
    2015-12-12
  • Java中的代理原理及代理使用示例

    Java中的代理原理及代理使用示例

    這篇文章主要介紹了Java中的代理原理及代理使用示例,本文講解了Java Socket編程中加入代理的2種方法,需要的朋友可以參考下
    2015-03-03
  • IntelliJ IDEA如何增加右鍵菜單

    IntelliJ IDEA如何增加右鍵菜單

    本文描述了在IntelliJ IDEA安裝后右鍵文件夾沒有相關選項的解決方案,通過打開注冊表編輯器并新建菜單項,將IntelliJ IDEA添加到右鍵菜單中,并設置其圖標和執(zhí)行路徑,最后,通過測試驗證功能是否正常
    2025-01-01

最新評論