在用戶使用網站建立新聞時,因爲網絡波動,致使發送了多個建立新聞的請求,使得系統中存在了冗餘的數據。java
因爲個人應用是單例的,因此採用guava來作緩存。web
import com.csdc.cett.exception.RequestException; import com.csdc.cett.util.MD5; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Enumeration; import java.util.concurrent.TimeUnit; /** * 處理重複請求,保持請求的冪等性 * 使用雙重檢查鎖定(DCL) * * @author ksyzz * @since <pre>2019/06/20</pre> */ @Component public class RequestInterceptor implements HandlerInterceptor { private static volatile Cache<String, String> loadingCache = CacheBuilder.newBuilder() .maximumSize(500) .expireAfterWrite(5, TimeUnit.SECONDS) .build(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 只須要保持用戶登陸後的POST請求的冪等性,此處要排除文件分片上傳 if ("POST".equals(request.getMethod()) && request.getHeader("Auth-Token") != null && !request.getRequestURI().contains("/upload/files")) { // 要求:用戶5秒內不能重複提交相同url,相同參數的請求 // 存儲方式爲 md5(URI+Auth-Token+RequestParams+InputStream) StringBuilder md5 = new StringBuilder(); // Auth-Token爲用戶身份標識 String token = request.getHeader("Auth-Token"); md5.append(request.getRequestURI()); md5.append(token); Enumeration<String> parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()) { String key = parameterNames.nextElement(); String parameter = request.getParameter(key); md5.append("&").append(key).append("=").append(parameter); } String cacheKey = MD5.getHashString(md5.toString()); // 校驗是否已經提交過請求 if (loadingCache.getIfPresent(cacheKey) == null) { synchronized (loadingCache) { if (loadingCache.getIfPresent(cacheKey) == null) { // 此處value可不設置 loadingCache.put(cacheKey, ""); return true; } } } throw new RequestException("請勿重複提交"); } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }