Browse Source

Random 改为使用 ThreadLocalRandom

James 3 years ago
parent
commit
96f59286ae

+ 22 - 21
src/main/java/com/jfinal/captcha/CaptchaRender.java

@@ -24,7 +24,7 @@ import java.awt.RenderingHints;
 import java.awt.geom.QuadCurve2D;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
-import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
 import javax.imageio.ImageIO;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.Cookie;
@@ -38,10 +38,9 @@ import com.jfinal.render.RenderException;
  * CaptchaRender.
  */
 public class CaptchaRender extends Render {
-	
+
 	protected static String captchaName = "_jfinal_captcha";
-	protected static final Random random = new Random(System.nanoTime());
-	
+
 	// 默认的验证码大小
 	protected static final int WIDTH = 108, HEIGHT = 40;
 	// 验证码随机字符数组
@@ -62,7 +61,7 @@ public class CaptchaRender extends Render {
 		new Font("Impact", Font.BOLD, 32),
 		new Font(Font.MONOSPACED, Font.BOLD, 40)
 	};*/
-	
+
 	/**
 	 * 设置 captchaName
 	 */
@@ -72,14 +71,14 @@ public class CaptchaRender extends Render {
 		}
 		CaptchaRender.captchaName = captchaName;
 	}
-	
+
 	/**
 	 * 生成验证码
 	 */
 	public void render() {
 		Captcha captcha = createCaptcha();
 		CaptchaManager.me().getCaptchaCache().put(captcha);
-		
+
 		Cookie cookie = new Cookie(captchaName, captcha.getKey());
 		cookie.setMaxAge(-1);
 		cookie.setPath("/");
@@ -88,12 +87,12 @@ public class CaptchaRender extends Render {
 		response.setHeader("Cache-Control","no-cache");
 		response.setDateHeader("Expires", 0);
 		response.setContentType("image/jpeg");
-		
+
 		ServletOutputStream sos = null;
 		try {
 			BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
 			drawGraphic(captcha.getValue(), image);
-			
+
 			sos = response.getOutputStream();
 			ImageIO.write(image, "jpeg", sos);
 		} catch (IOException e) {
@@ -108,7 +107,7 @@ public class CaptchaRender extends Render {
 			}
 		}
 	}
-	
+
 	protected Captcha createCaptcha() {
 		String captchaKey = getCaptchaKeyFromCookie();
 		if (StrKit.isBlank(captchaKey)) {
@@ -116,7 +115,7 @@ public class CaptchaRender extends Render {
 		}
 		return new Captcha(captchaKey, getRandomString(), Captcha.DEFAULT_EXPIRE_TIME);
 	}
-	
+
 	protected String getCaptchaKeyFromCookie() {
 		Cookie[] cookies = request.getCookies();
 		if (cookies != null) {
@@ -128,29 +127,31 @@ public class CaptchaRender extends Render {
 		}
 		return null;
 	}
-	
+
 	protected String getRandomString() {
+		ThreadLocalRandom random = ThreadLocalRandom.current();
 		char[] randomChars = new char[4];
 		for (int i=0; i<randomChars.length; i++) {
 			randomChars[i] = charArray[random.nextInt(charArray.length)];
 		}
 		return String.valueOf(randomChars);
 	}
-	
+
 	protected void drawGraphic(String randomString, BufferedImage image){
 		// 获取图形上下文
 		Graphics2D g = image.createGraphics();
-		
+
 		g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
 		// 图形抗锯齿
 		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 		// 字体抗锯齿
 		g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
-		
+
 		// 设定背景色
 		g.setColor(getRandColor(210, 250));
 		g.fillRect(0, 0, WIDTH, HEIGHT);
-		
+
+		ThreadLocalRandom random = ThreadLocalRandom.current();
 		//绘制小字符背景
 		Color color = null;
 		for(int i = 0; i < 20; i++){
@@ -160,7 +161,7 @@ public class CaptchaRender extends Render {
 			g.drawString(rand, random.nextInt(WIDTH), random.nextInt(HEIGHT));
 			color = null;
 		}
-		
+
 		//设定字体
 		g.setFont(RANDOM_FONT[random.nextInt(RANDOM_FONT.length)]);
 		// 绘制验证码
@@ -193,12 +194,12 @@ public class CaptchaRender extends Render {
 		// 销毁图像
 		g.dispose();
 	}
-	
+
 	/*
 	 * 给定范围获得随机颜色
 	 */
 	protected Color getRandColor(int fc, int bc) {
-		Random random = new Random();
+		ThreadLocalRandom random = ThreadLocalRandom.current();
 		if (fc > 255)
 			fc = 255;
 		if (bc > 255)
@@ -208,7 +209,7 @@ public class CaptchaRender extends Render {
 		int b = fc + random.nextInt(bc - fc);
 		return new Color(r, g, b);
 	}
-	
+
 	/**
 	 * 校验用户输入的验证码是否正确
 	 * @param controller 控制器
@@ -223,7 +224,7 @@ public class CaptchaRender extends Render {
 		}
 		return false;
 	}
-	
+
 	/**
 	 * 校验用户输入的验证码是否正确
 	 * @param captchaKey 验证码 key,在不支持 cookie 的情况下可通过传参给服务端

+ 14 - 14
src/main/java/com/jfinal/ext/render/CaptchaRender.java

@@ -21,7 +21,7 @@ import java.awt.Font;
 import java.awt.Graphics;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
-import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
 import javax.imageio.ImageIO;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.Cookie;
@@ -33,24 +33,24 @@ import com.jfinal.render.Render;
 
 /**
  * CaptchaRender.本验证码实现已被 Deprecated 不建使用.
- * 建议使用新版本的 Controller.renderCaptcha() 既简单又美观,并且还提供了 
+ * 建议使用新版本的 Controller.renderCaptcha() 既简单又美观,并且还提供了
  * Controller.validateCaptcha(para)与 Validator.validateCaptcha(para)支持
  */
 @Deprecated
 public class CaptchaRender extends Render {
-	
+
 	private static final int WIDTH = 80, HEIGHT = 26;
 	private static final String[] strArr = {"3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"};
-	
+
 	private String captchaName;
-	
+
 	public CaptchaRender(String captchaName) {
 		if (StrKit.isBlank(captchaName)) {
 			throw new IllegalArgumentException("captchaName can not be blank");
 		}
 		this.captchaName = captchaName;
 	}
-	
+
 	public void render() {
 		BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
 		String vCode = drawGraphic(image);
@@ -65,7 +65,7 @@ public class CaptchaRender extends Render {
         response.setHeader("Cache-Control","no-cache");
         response.setDateHeader("Expires", 0);
         response.setContentType("image/jpeg");
-        
+
         ServletOutputStream sos = null;
         try {
 			sos = response.getOutputStream();
@@ -84,7 +84,7 @@ public class CaptchaRender extends Render {
 		// 获取图形上下文
 		Graphics g = image.createGraphics();
 		// 生成随机类
-		Random random = new Random();
+		ThreadLocalRandom random = ThreadLocalRandom.current();
 		// 设定背景色
 		g.setColor(getRandColor(200, 250));
 		g.fillRect(0, 0, WIDTH, HEIGHT);
@@ -114,15 +114,15 @@ public class CaptchaRender extends Render {
 
 		// 图象生效
 		g.dispose();
-		
+
 		return sRand;
 	}
-	
+
 	/*
 	 * 给定范围获得随机颜色
 	 */
 	private Color getRandColor(int fc, int bc) {
-		Random random = new Random();
+		ThreadLocalRandom random = ThreadLocalRandom.current();
 		if (fc > 255)
 			fc = 255;
 		if (bc > 255)
@@ -132,16 +132,16 @@ public class CaptchaRender extends Render {
 		int b = fc + random.nextInt(bc - fc);
 		return new Color(r, g, b);
 	}
-	
+
 	public static boolean validate(Controller controller, String userInputCaptcha, String captchaName) {
 		if (StrKit.isBlank(userInputCaptcha)) {
 			return false;
 		}
-		
+
 		userInputCaptcha = userInputCaptcha.toUpperCase();
 		userInputCaptcha = HashKit.md5(userInputCaptcha);
 		boolean result = userInputCaptcha.equals(controller.getCookie(captchaName));
-		if (result == true) {
+		if (result) {
 			controller.removeCookie(captchaName);
 		}
 		return result;

+ 18 - 24
src/main/java/com/jfinal/json/Jackson.java

@@ -23,7 +23,7 @@ import com.jfinal.kit.TimeKit;
 
 /**
  * Json 转换 jackson 实现.
- * 
+ *
  * json 到 java 类型转换规则: http://wiki.fasterxml.com/JacksonInFiveMinutes
  * JSON TYPE				JAVA TYPE
  * object					LinkedHashMap<String,Object>
@@ -35,50 +35,44 @@ import com.jfinal.kit.TimeKit;
  * null						null
  */
 public class Jackson extends Json {
-	
+
 	// Jackson 生成 json 的默认行为是生成 null value,可设置此值全局改变默认行为
 	private static boolean defaultGenerateNullValue = true;
-	
+
 	// generateNullValue 通过设置此值,可临时改变默认生成 null value 的行为
 	protected Boolean generateNullValue = null;
-	
-	protected ObjectMapper objectMapper;
-	
-	public Jackson() {
-		objectMapper = new ObjectMapper();
-		config();
-	}
-	
+
+	protected static final ObjectMapper objectMapper = new ObjectMapper();
+
 	// https://gitee.com/jfinal/jfinal-weixin/issues/I875U
-	@SuppressWarnings("deprecation")
-	protected void config() {
+	static {
 		objectMapper.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
 		objectMapper.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
-		
+
 		// 没有 getter 方法时不抛异常
 		objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
 	}
-	
+
 	public static void setDefaultGenerateNullValue(boolean defaultGenerateNullValue) {
 		Jackson.defaultGenerateNullValue = defaultGenerateNullValue;
 	}
-	
+
 	public Jackson setGenerateNullValue(boolean generateNullValue) {
 		this.generateNullValue = generateNullValue;
 		return this;
 	}
-	
+
 	/**
 	 * 通过获取 ObjectMapper 进行更个性化设置,满足少数特殊情况
 	 */
 	public ObjectMapper getObjectMapper() {
 		return objectMapper;
 	}
-	
+
 	public static Jackson getJson() {
 		return new Jackson();
 	}
-	
+
 	public String toJson(Object object) {
 		try {
 			// 优先使用对象级的属性 datePattern, 然后才是全局性的 defaultDatePattern
@@ -86,19 +80,19 @@ public class Jackson extends Json {
 			if (dp != null) {
 				objectMapper.setDateFormat(TimeKit.getSimpleDateFormat(dp));
 			}
-			
+
 			// 优先使用对象属性 generateNullValue,决定转换 json时是否生成 null value
-			Boolean pnv = generateNullValue != null ? generateNullValue : defaultGenerateNullValue;
-			if (pnv == false) {
+			boolean pnv = generateNullValue != null ? generateNullValue : defaultGenerateNullValue;
+			if (!pnv) {
 				objectMapper.setSerializationInclusion(Include.NON_NULL);
 			}
-			
+
 			return objectMapper.writeValueAsString(object);
 		} catch (Exception e) {
 			throw e instanceof RuntimeException ? (RuntimeException)e : new RuntimeException(e);
 		}
 	}
-	
+
 	public <T> T parse(String jsonString, Class<T> type) {
 		try {
 			return objectMapper.readValue(jsonString, type);

+ 20 - 21
src/main/java/com/jfinal/token/TokenManager.java

@@ -18,9 +18,9 @@ package com.jfinal.token;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Random;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.concurrent.ThreadLocalRandom;
 import com.jfinal.core.Const;
 import com.jfinal.core.Controller;
 import com.jfinal.kit.StrKit;
@@ -29,27 +29,26 @@ import com.jfinal.kit.StrKit;
  * TokenManager.
  */
 public class TokenManager {
-	
+
 	private static ITokenCache tokenCache;
-	private static Random random = new Random();
-	
+
 	private TokenManager() {
-		
+
 	}
-	
+
 	public static void init(ITokenCache tokenCache) {
 		if (tokenCache == null) {
 			return;
 		}
-		
+
 		TokenManager.tokenCache = tokenCache;
-		
+
 		long halfTimeOut = Const.MIN_SECONDS_OF_TOKEN_TIME_OUT * 1000 / 2;	// Token最小过期时间的一半时间作为任务运行的间隔时间
 		new Timer("TokenManager", true).schedule(new TimerTask() {public void run() {removeTimeOutToken();}},
 							 halfTimeOut,
 							 halfTimeOut);
 	}
-	
+
 	/**
 	 * Create Token.
 	 * @param Controller
@@ -58,18 +57,18 @@ public class TokenManager {
 	 */
 	public static String createToken(Controller controller, String tokenName, int secondsOfTimeOut) {
 		if (tokenCache == null) {
-			String tokenId = String.valueOf(random.nextLong());
+			String tokenId = String.valueOf(ThreadLocalRandom.current().nextLong());
 			controller.setAttr(tokenName, tokenId);
 			controller.setSessionAttr(tokenName, tokenId);
 			createTokenHiddenField(controller, tokenName, tokenId);
-			
+
 			return tokenId;
 		}
 		else {
 			return createTokenByGenerator(controller, tokenName, secondsOfTimeOut);
 		}
 	}
-	
+
 	/**
 	 * 使用 #(token) 指令,将 token 隐藏域输出到页面表单之中,表单提交的时候该表单域会被提交
 	 */
@@ -78,12 +77,12 @@ public class TokenManager {
 		sb.append("<input type='hidden' name='").append(tokenName).append("' id='").append(tokenName).append("' value='").append(tokenId).append("' />");
 		controller.setAttr("token", sb.toString());
 	}
-	
+
 	private static String createTokenByGenerator(Controller controller, String tokenName, int secondsOfTimeOut) {
 		if (secondsOfTimeOut < Const.MIN_SECONDS_OF_TOKEN_TIME_OUT) {
 			secondsOfTimeOut = Const.MIN_SECONDS_OF_TOKEN_TIME_OUT;
 		}
-		
+
 		String tokenId = null;
 		Token token = null;
 		int safeCounter = 8;
@@ -91,17 +90,17 @@ public class TokenManager {
 			if (safeCounter-- == 0) {
 				throw new RuntimeException("Can not create tokenId.");
 			}
-			tokenId = String.valueOf(random.nextLong());
+			tokenId = String.valueOf(ThreadLocalRandom.current().nextLong());
 			token = new Token(tokenId, System.currentTimeMillis() + (secondsOfTimeOut * 1000));
 		} while(tokenId == null || tokenCache.contains(token));
-		
+
 		controller.setAttr(tokenName, tokenId);
 		tokenCache.put(token);
 		createTokenHiddenField(controller, tokenName, tokenId);
-		
+
 		return tokenId;
 	}
-	
+
 	/**
 	 * Check token to prevent resubmit.
 	 * @param tokenName the token name used in view's form
@@ -121,13 +120,13 @@ public class TokenManager {
 			return result;
 		}
 	}
-	
+
 	private static void removeTimeOutToken() {
 		List<Token> tokenInCache = tokenCache.getAll();
 		if (tokenInCache == null) {
 			return;
 		}
-		
+
 		List<Token> timeOutTokens = new ArrayList<Token>();
 		long currentTime = System.currentTimeMillis();
 		// find and save all time out tokens
@@ -136,7 +135,7 @@ public class TokenManager {
 				timeOutTokens.add(token);
 			}
 		}
-		
+
 		// remove all time out tokens
 		for (Token token : timeOutTokens) {
 			tokenCache.remove(token);