mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
add LockUtil and fix Cache
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
* 【core 】 CollUtil.newHashSet重载歧义,更换为set方法
|
* 【core 】 CollUtil.newHashSet重载歧义,更换为set方法
|
||||||
* 【core 】 增加ListUtil,增加Hash32、Hash64、Hash128接口
|
* 【core 】 增加ListUtil,增加Hash32、Hash64、Hash128接口
|
||||||
* 【crypto 】 BCUtil增加readPemPrivateKey和readPemPublicKey方法
|
* 【crypto 】 BCUtil增加readPemPrivateKey和readPemPublicKey方法
|
||||||
|
* 【cache 】 替换读写锁为StampedLock,增加LockUtil
|
||||||
|
|
||||||
### Bug修复
|
### Bug修复
|
||||||
* 【core 】 修复NumberWordFormatter拼写错误(issue#799@Github)
|
* 【core 】 修复NumberWordFormatter拼写错误(issue#799@Github)
|
||||||
|
@@ -6,9 +6,7 @@ import cn.hutool.core.lang.func.Func0;
|
|||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.StampedLock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 超时和限制大小的缓存的默认实现<br>
|
* 超时和限制大小的缓存的默认实现<br>
|
||||||
@@ -18,31 +16,38 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
|||||||
* <li>实现 <code>prune</code> 策略</li>
|
* <li>实现 <code>prune</code> 策略</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Looly,jodd
|
|
||||||
*
|
|
||||||
* @param <K> 键类型
|
* @param <K> 键类型
|
||||||
* @param <V> 值类型
|
* @param <V> 值类型
|
||||||
|
* @author Looly, jodd
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
protected Map<K, CacheObj<K, V>> cacheMap;
|
protected Map<K, CacheObj<K, V>> cacheMap;
|
||||||
|
|
||||||
private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();
|
private final StampedLock lock = new StampedLock();
|
||||||
private final ReadLock readLock = cacheLock.readLock();
|
|
||||||
private final WriteLock writeLock = cacheLock.writeLock();
|
|
||||||
|
|
||||||
/** 返回缓存容量,<code>0</code>表示无大小限制 */
|
/**
|
||||||
|
* 返回缓存容量,<code>0</code>表示无大小限制
|
||||||
|
*/
|
||||||
protected int capacity;
|
protected int capacity;
|
||||||
/** 缓存失效时长, <code>0</code> 表示无限制,单位毫秒 */
|
/**
|
||||||
|
* 缓存失效时长, <code>0</code> 表示无限制,单位毫秒
|
||||||
|
*/
|
||||||
protected long timeout;
|
protected long timeout;
|
||||||
|
|
||||||
/** 每个对象是否有单独的失效时长,用于决定清理过期对象是否有必要。 */
|
/**
|
||||||
|
* 每个对象是否有单独的失效时长,用于决定清理过期对象是否有必要。
|
||||||
|
*/
|
||||||
protected boolean existCustomTimeout;
|
protected boolean existCustomTimeout;
|
||||||
|
|
||||||
/** 命中数 */
|
/**
|
||||||
|
* 命中数
|
||||||
|
*/
|
||||||
protected int hitCount;
|
protected int hitCount;
|
||||||
/** 丢失数 */
|
/**
|
||||||
|
* 丢失数
|
||||||
|
*/
|
||||||
protected int missCount;
|
protected int missCount;
|
||||||
|
|
||||||
// ---------------------------------------------------------------- put start
|
// ---------------------------------------------------------------- put start
|
||||||
@@ -53,12 +58,11 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void put(K key, V object, long timeout) {
|
public void put(K key, V object, long timeout) {
|
||||||
writeLock.lock();
|
final long stamp = lock.writeLock();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
putWithoutLock(key, object, timeout);
|
putWithoutLock(key, object, timeout);
|
||||||
} finally {
|
} finally {
|
||||||
writeLock.unlock();
|
lock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +89,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
// ---------------------------------------------------------------- get start
|
// ---------------------------------------------------------------- get start
|
||||||
@Override
|
@Override
|
||||||
public boolean containsKey(K key) {
|
public boolean containsKey(K key) {
|
||||||
readLock.lock();
|
final long stamp = lock.readLock();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 不存在或已移除
|
// 不存在或已移除
|
||||||
final CacheObj<K, V> co = cacheMap.get(key);
|
final CacheObj<K, V> co = cacheMap.get(key);
|
||||||
@@ -99,7 +102,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
readLock.unlock();
|
lock.unlockRead(stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 过期
|
// 过期
|
||||||
@@ -111,24 +114,14 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
* @return 命中数
|
* @return 命中数
|
||||||
*/
|
*/
|
||||||
public int getHitCount() {
|
public int getHitCount() {
|
||||||
this.readLock.lock();
|
|
||||||
try {
|
|
||||||
return hitCount;
|
return hitCount;
|
||||||
} finally {
|
|
||||||
this.readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return 丢失数
|
* @return 丢失数
|
||||||
*/
|
*/
|
||||||
public int getMissCount() {
|
public int getMissCount() {
|
||||||
this.readLock.lock();
|
|
||||||
try {
|
|
||||||
return missCount;
|
return missCount;
|
||||||
} finally {
|
|
||||||
this.readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -140,11 +133,11 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
public V get(K key, Func0<V> supplier) {
|
public V get(K key, Func0<V> supplier) {
|
||||||
V v = get(key);
|
V v = get(key);
|
||||||
if (null == v && null != supplier) {
|
if (null == v && null != supplier) {
|
||||||
writeLock.lock();
|
final long stamp = lock.writeLock();
|
||||||
try {
|
try {
|
||||||
// 双重检查锁
|
// 双重检查锁
|
||||||
final CacheObj<K, V> co = cacheMap.get(key);
|
final CacheObj<K, V> co = cacheMap.get(key);
|
||||||
if(null == co || co.isExpired() || null == co.getValue()) {
|
if (null == co || co.isExpired()) {
|
||||||
try {
|
try {
|
||||||
v = supplier.call();
|
v = supplier.call();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -155,7 +148,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
v = co.get(true);
|
v = co.get(true);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
writeLock.unlock();
|
lock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
@@ -163,23 +156,25 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V get(K key, boolean isUpdateLastAccess) {
|
public V get(K key, boolean isUpdateLastAccess) {
|
||||||
readLock.lock();
|
// 尝试读取缓存,使用乐观读锁
|
||||||
|
long stamp = lock.readLock();
|
||||||
try {
|
try {
|
||||||
// 不存在或已移除
|
// 不存在或已移除
|
||||||
final CacheObj<K, V> co = cacheMap.get(key);
|
final CacheObj<K, V> co = cacheMap.get(key);
|
||||||
if (co == null) {
|
if (null == co) {
|
||||||
missCount++;
|
missCount++;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false == co.isExpired()) {
|
if (co.isExpired()) {
|
||||||
|
missCount++;
|
||||||
|
} else{
|
||||||
// 命中
|
// 命中
|
||||||
hitCount++;
|
hitCount++;
|
||||||
return co.get(isUpdateLastAccess);
|
return co.get(isUpdateLastAccess);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
readLock.unlock();
|
lock.unlock(stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 过期
|
// 过期
|
||||||
@@ -199,18 +194,20 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
@Override
|
@Override
|
||||||
public Iterator<CacheObj<K, V>> cacheObjIterator() {
|
public Iterator<CacheObj<K, V>> cacheObjIterator() {
|
||||||
CopiedIter<CacheObj<K, V>> copiedIterator;
|
CopiedIter<CacheObj<K, V>> copiedIterator;
|
||||||
readLock.lock();
|
final long stamp = lock.readLock();
|
||||||
try {
|
try {
|
||||||
copiedIterator = CopiedIter.copyOf(this.cacheMap.values().iterator());
|
copiedIterator = CopiedIter.copyOf(this.cacheMap.values().iterator());
|
||||||
} finally {
|
} finally {
|
||||||
readLock.unlock();
|
lock.unlockRead(stamp);
|
||||||
}
|
}
|
||||||
return new CacheObjIterator<>(copiedIterator);
|
return new CacheObjIterator<>(copiedIterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------- prune start
|
// ---------------------------------------------------------------- prune start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清理实现
|
* 清理实现<br>
|
||||||
|
* 子类实现此方法时无需加锁
|
||||||
*
|
*
|
||||||
* @return 清理数
|
* @return 清理数
|
||||||
*/
|
*/
|
||||||
@@ -218,11 +215,11 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int prune() {
|
public final int prune() {
|
||||||
writeLock.lock();
|
final long stamp = lock.writeLock();
|
||||||
try {
|
try {
|
||||||
return pruneCache();
|
return pruneCache();
|
||||||
} finally {
|
} finally {
|
||||||
writeLock.unlock();
|
lock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ---------------------------------------------------------------- prune end
|
// ---------------------------------------------------------------- prune end
|
||||||
@@ -248,22 +245,12 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
* @return 过期对象清理是否可用,内部使用
|
* @return 过期对象清理是否可用,内部使用
|
||||||
*/
|
*/
|
||||||
protected boolean isPruneExpiredActive() {
|
protected boolean isPruneExpiredActive() {
|
||||||
this.readLock.lock();
|
|
||||||
try {
|
|
||||||
return (timeout != 0) || existCustomTimeout;
|
return (timeout != 0) || existCustomTimeout;
|
||||||
} finally {
|
|
||||||
this.readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFull() {
|
public boolean isFull() {
|
||||||
this.readLock.lock();
|
|
||||||
try {
|
|
||||||
return (capacity > 0) && (cacheMap.size() >= capacity);
|
return (capacity > 0) && (cacheMap.size() >= capacity);
|
||||||
} finally {
|
|
||||||
this.readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -273,42 +260,27 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
writeLock.lock();
|
final long stamp = lock.writeLock();
|
||||||
try {
|
try {
|
||||||
cacheMap.clear();
|
cacheMap.clear();
|
||||||
} finally {
|
} finally {
|
||||||
writeLock.unlock();
|
lock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
this.readLock.lock();
|
|
||||||
try {
|
|
||||||
return cacheMap.size();
|
return cacheMap.size();
|
||||||
} finally {
|
|
||||||
this.readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
this.readLock.lock();
|
|
||||||
try {
|
|
||||||
return cacheMap.isEmpty();
|
return cacheMap.isEmpty();
|
||||||
} finally {
|
|
||||||
this.readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
this.readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.cacheMap.toString();
|
return this.cacheMap.toString();
|
||||||
} finally {
|
|
||||||
this.readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// ---------------------------------------------------------------- common end
|
// ---------------------------------------------------------------- common end
|
||||||
|
|
||||||
@@ -329,12 +301,12 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
* @param withMissCount 是否计数丢失数
|
* @param withMissCount 是否计数丢失数
|
||||||
*/
|
*/
|
||||||
private void remove(K key, boolean withMissCount) {
|
private void remove(K key, boolean withMissCount) {
|
||||||
writeLock.lock();
|
final long stamp = lock.writeLock();
|
||||||
CacheObj<K, V> co;
|
CacheObj<K, V> co;
|
||||||
try {
|
try {
|
||||||
co = removeWithoutLock(key, withMissCount);
|
co = removeWithoutLock(key, withMissCount);
|
||||||
} finally {
|
} finally {
|
||||||
writeLock.unlock();
|
lock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
if (null != co) {
|
if (null != co) {
|
||||||
onRemove(co.key, co.obj);
|
onRemove(co.key, co.obj);
|
||||||
|
@@ -1,15 +1,12 @@
|
|||||||
package cn.hutool.cache.test;
|
package cn.hutool.cache.test;
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import cn.hutool.cache.Cache;
|
import cn.hutool.cache.Cache;
|
||||||
import cn.hutool.cache.impl.FIFOCache;
|
import cn.hutool.cache.impl.FIFOCache;
|
||||||
import cn.hutool.cache.impl.LRUCache;
|
import cn.hutool.cache.impl.LRUCache;
|
||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
import cn.hutool.core.thread.ThreadUtil;
|
import cn.hutool.core.thread.ThreadUtil;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存单元测试
|
* 缓存单元测试
|
||||||
@@ -28,9 +25,7 @@ public class CacheConcurrentTest {
|
|||||||
// 由于缓存容量只有3,当加入第四个元素的时候,根据FIFO规则,最先放入的对象将被移除
|
// 由于缓存容量只有3,当加入第四个元素的时候,根据FIFO规则,最先放入的对象将被移除
|
||||||
|
|
||||||
for (int i = 0; i < threadCount; i++) {
|
for (int i = 0; i < threadCount; i++) {
|
||||||
ThreadUtil.execute(new Runnable() {
|
ThreadUtil.execute(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
cache.put("key1", "value1", System.currentTimeMillis() * 3);
|
cache.put("key1", "value1", System.currentTimeMillis() * 3);
|
||||||
cache.put("key2", "value2", System.currentTimeMillis() * 3);
|
cache.put("key2", "value2", System.currentTimeMillis() * 3);
|
||||||
cache.put("key3", "value3", System.currentTimeMillis() * 3);
|
cache.put("key3", "value3", System.currentTimeMillis() * 3);
|
||||||
@@ -41,17 +36,11 @@ public class CacheConcurrentTest {
|
|||||||
cache.put("key7", "value7", System.currentTimeMillis() * 3);
|
cache.put("key7", "value7", System.currentTimeMillis() * 3);
|
||||||
cache.put("key8", "value8", System.currentTimeMillis() * 3);
|
cache.put("key8", "value8", System.currentTimeMillis() * 3);
|
||||||
Console.log("put all");
|
Console.log("put all");
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < threadCount; i++) {
|
for (int i = 0; i < threadCount; i++) {
|
||||||
ThreadUtil.execute(new Runnable() {
|
ThreadUtil.execute(() -> show(cache));
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
show(cache);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("==============================");
|
System.out.println("==============================");
|
||||||
@@ -66,9 +55,7 @@ public class CacheConcurrentTest {
|
|||||||
|
|
||||||
for (int i = 0; i < threadCount; i++) {
|
for (int i = 0; i < threadCount; i++) {
|
||||||
final int index = i;
|
final int index = i;
|
||||||
ThreadUtil.execute(new Runnable() {
|
ThreadUtil.execute(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
cache.put("key1"+ index, "value1");
|
cache.put("key1"+ index, "value1");
|
||||||
cache.put("key2"+ index, "value2", System.currentTimeMillis() * 3);
|
cache.put("key2"+ index, "value2", System.currentTimeMillis() * 3);
|
||||||
|
|
||||||
@@ -83,7 +70,6 @@ public class CacheConcurrentTest {
|
|||||||
if(size > capacity) {
|
if(size > capacity) {
|
||||||
Console.log("## {} {}", size, capacity);
|
Console.log("## {} {}", size, capacity);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,10 +77,8 @@ public class CacheConcurrentTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void show(Cache<String, String> cache) {
|
private void show(Cache<String, String> cache) {
|
||||||
Iterator<?> its = cache.iterator();
|
|
||||||
|
|
||||||
while (its.hasNext()) {
|
for (Object tt : cache) {
|
||||||
Object tt = its.next();
|
|
||||||
Console.log(tt);
|
Console.log(tt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,11 @@
|
|||||||
package cn.hutool.core.lang;
|
package cn.hutool.core.lang;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.func.Func0;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.StampedLock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
|
||||||
|
|
||||||
import cn.hutool.core.lang.func.Func0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 简单缓存,无超时实现,使用{@link WeakHashMap}实现缓存自动清理
|
* 简单缓存,无超时实现,使用{@link WeakHashMap}实现缓存自动清理
|
||||||
@@ -22,9 +20,8 @@ public class SimpleCache<K, V> implements Serializable{
|
|||||||
/** 池 */
|
/** 池 */
|
||||||
private final Map<K, V> cache = new WeakHashMap<>();
|
private final Map<K, V> cache = new WeakHashMap<>();
|
||||||
|
|
||||||
private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();
|
// 乐观读写锁
|
||||||
private final ReadLock readLock = cacheLock.readLock();
|
private final StampedLock lock = new StampedLock ();
|
||||||
private final WriteLock writeLock = cacheLock.writeLock();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从缓存池中查找值
|
* 从缓存池中查找值
|
||||||
@@ -33,15 +30,12 @@ public class SimpleCache<K, V> implements Serializable{
|
|||||||
* @return 值
|
* @return 值
|
||||||
*/
|
*/
|
||||||
public V get(K key) {
|
public V get(K key) {
|
||||||
// 尝试读取缓存
|
long stamp = lock.readLock();
|
||||||
readLock.lock();
|
|
||||||
V value;
|
|
||||||
try {
|
try {
|
||||||
value = cache.get(key);
|
return cache.get(key);
|
||||||
} finally {
|
} finally {
|
||||||
readLock.unlock();
|
lock.unlockRead(stamp);
|
||||||
}
|
}
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,12 +46,25 @@ public class SimpleCache<K, V> implements Serializable{
|
|||||||
* @return 值对象
|
* @return 值对象
|
||||||
*/
|
*/
|
||||||
public V get(K key, Func0<V> supplier) {
|
public V get(K key, Func0<V> supplier) {
|
||||||
V v = get(key);
|
if(null == supplier){
|
||||||
if (null == v && null != supplier) {
|
return get(key);
|
||||||
writeLock.lock();
|
}
|
||||||
try {
|
|
||||||
// 双重检查锁
|
long stamp = lock.readLock();
|
||||||
|
V v;
|
||||||
|
try{
|
||||||
v = cache.get(key);
|
v = cache.get(key);
|
||||||
|
if (null == v) {
|
||||||
|
// 尝试转换独占写锁
|
||||||
|
long writeStamp = lock.tryConvertToWriteLock(stamp);
|
||||||
|
if(0 == writeStamp){
|
||||||
|
// 转换失败,手动更新为写锁
|
||||||
|
lock.unlockRead(stamp);
|
||||||
|
writeStamp = lock.writeLock();
|
||||||
|
}
|
||||||
|
stamp = writeStamp;
|
||||||
|
v = cache.get(key);
|
||||||
|
// 双重检查,防止在竞争锁的过程中已经有其它线程写入
|
||||||
if(null == v) {
|
if(null == v) {
|
||||||
try {
|
try {
|
||||||
v = supplier.call();
|
v = supplier.call();
|
||||||
@@ -66,9 +73,9 @@ public class SimpleCache<K, V> implements Serializable{
|
|||||||
}
|
}
|
||||||
cache.put(key, v);
|
cache.put(key, v);
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
writeLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock(stamp);
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@@ -80,11 +87,12 @@ public class SimpleCache<K, V> implements Serializable{
|
|||||||
* @return 值
|
* @return 值
|
||||||
*/
|
*/
|
||||||
public V put(K key, V value){
|
public V put(K key, V value){
|
||||||
writeLock.lock();
|
// 独占写锁
|
||||||
|
final long stamp = lock.writeLock();
|
||||||
try {
|
try {
|
||||||
cache.put(key, value);
|
cache.put(key, value);
|
||||||
} finally {
|
} finally {
|
||||||
writeLock.unlock();
|
lock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -96,11 +104,12 @@ public class SimpleCache<K, V> implements Serializable{
|
|||||||
* @return 移除的值
|
* @return 移除的值
|
||||||
*/
|
*/
|
||||||
public V remove(K key) {
|
public V remove(K key) {
|
||||||
writeLock.lock();
|
// 独占写锁
|
||||||
|
final long stamp = lock.writeLock();
|
||||||
try {
|
try {
|
||||||
return cache.remove(key);
|
return cache.remove(key);
|
||||||
} finally {
|
} finally {
|
||||||
writeLock.unlock();
|
lock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,11 +117,12 @@ public class SimpleCache<K, V> implements Serializable{
|
|||||||
* 清空缓存池
|
* 清空缓存池
|
||||||
*/
|
*/
|
||||||
public void clear() {
|
public void clear() {
|
||||||
writeLock.lock();
|
// 独占写锁
|
||||||
|
final long stamp = lock.writeLock();
|
||||||
try {
|
try {
|
||||||
this.cache.clear();
|
this.cache.clear();
|
||||||
} finally {
|
} finally {
|
||||||
writeLock.unlock();
|
lock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,43 @@
|
|||||||
|
package cn.hutool.core.thread.lock;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
import java.util.concurrent.locks.StampedLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 锁相关工具
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.2.5
|
||||||
|
*/
|
||||||
|
public class LockUtil {
|
||||||
|
|
||||||
|
private static NoLock NO_LOCK = new NoLock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建{@link StampedLock}锁
|
||||||
|
*
|
||||||
|
* @return {@link StampedLock}锁
|
||||||
|
*/
|
||||||
|
public static StampedLock createStampLock() {
|
||||||
|
return new StampedLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建{@link ReentrantReadWriteLock}锁
|
||||||
|
*
|
||||||
|
* @param fair 是否公平锁
|
||||||
|
* @return {@link ReentrantReadWriteLock}锁
|
||||||
|
*/
|
||||||
|
public static ReentrantReadWriteLock createReadWriteLock(boolean fair) {
|
||||||
|
return new ReentrantReadWriteLock(fair);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取单例的无锁对象
|
||||||
|
*
|
||||||
|
* @return {@link NoLock}
|
||||||
|
*/
|
||||||
|
public static NoLock getNoLock(){
|
||||||
|
return NO_LOCK;
|
||||||
|
}
|
||||||
|
}
|
@@ -17,7 +17,7 @@ public class NoLock implements Lock{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void lockInterruptibly() throws InterruptedException {
|
public void lockInterruptibly() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -25,8 +25,9 @@ public class NoLock implements Lock{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("NullableProblems")
|
||||||
@Override
|
@Override
|
||||||
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
|
public boolean tryLock(long time, TimeUnit unit) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ public class NoLock implements Lock{
|
|||||||
public void unlock() {
|
public void unlock() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("NullableProblems")
|
||||||
@Override
|
@Override
|
||||||
public Condition newCondition() {
|
public Condition newCondition() {
|
||||||
return null;
|
return null;
|
||||||
|
@@ -0,0 +1,46 @@
|
|||||||
|
package cn.hutool.core.lang;
|
||||||
|
|
||||||
|
import cn.hutool.core.thread.ThreadUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class SimpleCacheTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void putTest(){
|
||||||
|
final SimpleCache<String, String> cache = new SimpleCache<>();
|
||||||
|
ThreadUtil.execute(()->cache.put("key1", "value1"));
|
||||||
|
ThreadUtil.execute(()->cache.get("key1"));
|
||||||
|
ThreadUtil.execute(()->cache.put("key2", "value2"));
|
||||||
|
ThreadUtil.execute(()->cache.get("key2"));
|
||||||
|
ThreadUtil.execute(()->cache.put("key3", "value3"));
|
||||||
|
ThreadUtil.execute(()->cache.get("key3"));
|
||||||
|
ThreadUtil.execute(()->cache.put("key4", "value4"));
|
||||||
|
ThreadUtil.execute(()->cache.get("key4"));
|
||||||
|
ThreadUtil.execute(()->cache.get("key5", ()->"value5"));
|
||||||
|
|
||||||
|
cache.get("key5", ()->"value5");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getTest(){
|
||||||
|
final SimpleCache<String, String> cache = new SimpleCache<>();
|
||||||
|
cache.put("key1", "value1");
|
||||||
|
cache.get("key1");
|
||||||
|
cache.put("key2", "value2");
|
||||||
|
cache.get("key2");
|
||||||
|
cache.put("key3", "value3");
|
||||||
|
cache.get("key3");
|
||||||
|
cache.put("key4", "value4");
|
||||||
|
cache.get("key4");
|
||||||
|
cache.get("key5", ()->"value5");
|
||||||
|
|
||||||
|
Assert.assertEquals("value1", cache.get("key1"));
|
||||||
|
Assert.assertEquals("value2", cache.get("key2"));
|
||||||
|
Assert.assertEquals("value3", cache.get("key3"));
|
||||||
|
Assert.assertEquals("value4", cache.get("key4"));
|
||||||
|
Assert.assertEquals("value5", cache.get("key5"));
|
||||||
|
Assert.assertEquals("value6", cache.get("key6", ()-> "value6"));
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
@@ -14,11 +15,36 @@
|
|||||||
<name>${project.artifactId}</name>
|
<name>${project.artifactId}</name>
|
||||||
<description>Hutool 脚本执行封装</description>
|
<description>Hutool 脚本执行封装</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<jython.version>2.7.0</jython.version>
|
||||||
|
<luaj.version>3.0.1</luaj.version>
|
||||||
|
<groovy.version>3.0.2</groovy.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-core</artifactId>
|
<artifactId>hutool-core</artifactId>
|
||||||
<version>${project.parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.python</groupId>
|
||||||
|
<artifactId>jython</artifactId>
|
||||||
|
<version>${jython.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.luaj</groupId>
|
||||||
|
<artifactId>luaj-jse</artifactId>
|
||||||
|
<version>${luaj.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.groovy</groupId>
|
||||||
|
<artifactId>groovy-all</artifactId>
|
||||||
|
<version>${groovy.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@@ -1,65 +1,64 @@
|
|||||||
package cn.hutool.script;
|
package cn.hutool.script;
|
||||||
|
|
||||||
import java.io.Reader;
|
|
||||||
|
|
||||||
import javax.script.Bindings;
|
import javax.script.Bindings;
|
||||||
import javax.script.Compilable;
|
import javax.script.Compilable;
|
||||||
import javax.script.CompiledScript;
|
import javax.script.CompiledScript;
|
||||||
import javax.script.Invocable;
|
import javax.script.Invocable;
|
||||||
import javax.script.ScriptContext;
|
import javax.script.ScriptContext;
|
||||||
import javax.script.ScriptEngineFactory;
|
import javax.script.ScriptEngineFactory;
|
||||||
import javax.script.ScriptEngineManager;
|
|
||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
|
import java.io.Reader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Javascript引擎类
|
* Javascript引擎类
|
||||||
* @author Looly
|
|
||||||
*
|
*
|
||||||
|
* @author Looly
|
||||||
*/
|
*/
|
||||||
public class JavaScriptEngine extends FullSupportScriptEngine{
|
public class JavaScriptEngine extends FullSupportScriptEngine {
|
||||||
|
|
||||||
public JavaScriptEngine() {
|
public JavaScriptEngine() {
|
||||||
super(new ScriptEngineManager().getEngineByName("javascript"));
|
super(ScriptUtil.getJsEngine());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 引擎实例
|
* 引擎实例
|
||||||
|
*
|
||||||
* @return 引擎实例
|
* @return 引擎实例
|
||||||
*/
|
*/
|
||||||
public static JavaScriptEngine instance(){
|
public static JavaScriptEngine instance() {
|
||||||
return new JavaScriptEngine();
|
return new JavaScriptEngine();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------- Invocable
|
//----------------------------------------------------------------------------------------------- Invocable
|
||||||
@Override
|
@Override
|
||||||
public Object invokeMethod(Object thiz, String name, Object... args) throws ScriptException, NoSuchMethodException {
|
public Object invokeMethod(Object thiz, String name, Object... args) throws ScriptException, NoSuchMethodException {
|
||||||
return ((Invocable)engine).invokeMethod(thiz, name, args);
|
return ((Invocable) engine).invokeMethod(thiz, name, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException {
|
public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException {
|
||||||
return ((Invocable)engine).invokeFunction(name, args);
|
return ((Invocable) engine).invokeFunction(name, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T getInterface(Class<T> clasz) {
|
public <T> T getInterface(Class<T> clasz) {
|
||||||
return ((Invocable)engine).getInterface(clasz);
|
return ((Invocable) engine).getInterface(clasz);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T getInterface(Object thiz, Class<T> clasz) {
|
public <T> T getInterface(Object thiz, Class<T> clasz) {
|
||||||
return ((Invocable)engine).getInterface(thiz, clasz);
|
return ((Invocable) engine).getInterface(thiz, clasz);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------- Compilable
|
//----------------------------------------------------------------------------------------------- Compilable
|
||||||
@Override
|
@Override
|
||||||
public CompiledScript compile(String script) throws ScriptException {
|
public CompiledScript compile(String script) throws ScriptException {
|
||||||
return ((Compilable)engine).compile(script);
|
return ((Compilable) engine).compile(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompiledScript compile(Reader script) throws ScriptException {
|
public CompiledScript compile(Reader script) throws ScriptException {
|
||||||
return ((Compilable)engine).compile(script);
|
return ((Compilable) engine).compile(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------- ScriptEngine
|
//----------------------------------------------------------------------------------------------- ScriptEngine
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
package cn.hutool.script;
|
package cn.hutool.script;
|
||||||
|
|
||||||
import javax.script.ScriptException;
|
|
||||||
|
|
||||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 脚本运行时异常
|
* 脚本运行时异常
|
||||||
*
|
*
|
||||||
@@ -50,7 +50,6 @@ public class ScriptRuntimeException extends RuntimeException {
|
|||||||
super(message);
|
super(message);
|
||||||
this.fileName = fileName;
|
this.fileName = fileName;
|
||||||
this.lineNumber = lineNumber;
|
this.lineNumber = lineNumber;
|
||||||
this.columnNumber = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
package cn.hutool.script;
|
package cn.hutool.script;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.SimpleCache;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
import javax.script.Bindings;
|
import javax.script.Bindings;
|
||||||
import javax.script.Compilable;
|
import javax.script.Compilable;
|
||||||
import javax.script.CompiledScript;
|
import javax.script.CompiledScript;
|
||||||
@@ -12,18 +15,32 @@ import javax.script.ScriptException;
|
|||||||
* 脚本工具类
|
* 脚本工具类
|
||||||
*
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class ScriptUtil {
|
public class ScriptUtil {
|
||||||
|
|
||||||
|
private static final ScriptEngineManager manager = new ScriptEngineManager();
|
||||||
|
private static SimpleCache<String, ScriptEngine> cache = new SimpleCache<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得 {@link ScriptEngine} 实例
|
* 获得 {@link ScriptEngine} 实例
|
||||||
*
|
*
|
||||||
* @param name 脚本名称
|
* @param nameOrExtOrMime 脚本名称
|
||||||
* @return {@link ScriptEngine} 实例
|
* @return {@link ScriptEngine} 实例
|
||||||
*/
|
*/
|
||||||
public static ScriptEngine getScript(String name) {
|
public static ScriptEngine getScript(String nameOrExtOrMime) {
|
||||||
return new ScriptEngineManager().getEngineByName(name);
|
return cache.get(nameOrExtOrMime, ()->{
|
||||||
|
ScriptEngine engine = manager.getEngineByName(nameOrExtOrMime);
|
||||||
|
if (null == engine) {
|
||||||
|
engine = manager.getEngineByExtension(nameOrExtOrMime);
|
||||||
|
}
|
||||||
|
if (null == engine) {
|
||||||
|
engine = manager.getEngineByMimeType(nameOrExtOrMime);
|
||||||
|
}
|
||||||
|
if (null == engine) {
|
||||||
|
throw new NullPointerException(StrUtil.format("Script for [{}] not support !", nameOrExtOrMime));
|
||||||
|
}
|
||||||
|
return engine;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,7 +53,51 @@ public class ScriptUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编译脚本
|
* 获得 JavaScript引擎
|
||||||
|
*
|
||||||
|
* @return Python引擎
|
||||||
|
* @since 5.2.5
|
||||||
|
*/
|
||||||
|
public static ScriptEngine getJsEngine() {
|
||||||
|
return getScript("js");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得 Python引擎<br>
|
||||||
|
* 需要引入org.python:jython
|
||||||
|
*
|
||||||
|
* @return Python引擎
|
||||||
|
* @since 5.2.5
|
||||||
|
*/
|
||||||
|
public static ScriptEngine getPythonEngine() {
|
||||||
|
System.setProperty("python.import.site", "false");
|
||||||
|
return getScript("python");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得Lua引擎<br>
|
||||||
|
* 需要引入org.luaj:luaj-jse
|
||||||
|
*
|
||||||
|
* @return Lua引擎
|
||||||
|
* @since 5.2.5
|
||||||
|
*/
|
||||||
|
public static ScriptEngine getLuaEngine() {
|
||||||
|
return getScript("lua");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得Groovy引擎<br>
|
||||||
|
* 需要引入org.codehaus.groovy:groovy-all
|
||||||
|
*
|
||||||
|
* @return Groovy引擎
|
||||||
|
* @since 5.2.5
|
||||||
|
*/
|
||||||
|
public static ScriptEngine getGroovyEngine() {
|
||||||
|
return getScript("groovy");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行脚本
|
||||||
*
|
*
|
||||||
* @param script 脚本内容
|
* @param script 脚本内容
|
||||||
* @return {@link CompiledScript}
|
* @return {@link CompiledScript}
|
||||||
@@ -45,14 +106,14 @@ public class ScriptUtil {
|
|||||||
*/
|
*/
|
||||||
public static Object eval(String script) throws ScriptRuntimeException {
|
public static Object eval(String script) throws ScriptRuntimeException {
|
||||||
try {
|
try {
|
||||||
return compile(script).eval();
|
return getJsEngine().eval(script);
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
throw new ScriptRuntimeException(e);
|
throw new ScriptRuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编译脚本
|
* 执行脚本
|
||||||
*
|
*
|
||||||
* @param script 脚本内容
|
* @param script 脚本内容
|
||||||
* @param context 脚本上下文
|
* @param context 脚本上下文
|
||||||
@@ -62,14 +123,14 @@ public class ScriptUtil {
|
|||||||
*/
|
*/
|
||||||
public static Object eval(String script, ScriptContext context) throws ScriptRuntimeException {
|
public static Object eval(String script, ScriptContext context) throws ScriptRuntimeException {
|
||||||
try {
|
try {
|
||||||
return compile(script).eval(context);
|
return getJsEngine().eval(script, context);
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
throw new ScriptRuntimeException(e);
|
throw new ScriptRuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编译脚本
|
* 执行脚本
|
||||||
*
|
*
|
||||||
* @param script 脚本内容
|
* @param script 脚本内容
|
||||||
* @param bindings 绑定的参数
|
* @param bindings 绑定的参数
|
||||||
@@ -79,7 +140,7 @@ public class ScriptUtil {
|
|||||||
*/
|
*/
|
||||||
public static Object eval(String script, Bindings bindings) throws ScriptRuntimeException {
|
public static Object eval(String script, Bindings bindings) throws ScriptRuntimeException {
|
||||||
try {
|
try {
|
||||||
return compile(script).eval(bindings);
|
return getJsEngine().eval(script, bindings);
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
throw new ScriptRuntimeException(e);
|
throw new ScriptRuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -95,7 +156,7 @@ public class ScriptUtil {
|
|||||||
*/
|
*/
|
||||||
public static CompiledScript compile(String script) throws ScriptRuntimeException {
|
public static CompiledScript compile(String script) throws ScriptRuntimeException {
|
||||||
try {
|
try {
|
||||||
return compile(getJavaScriptEngine(), script);
|
return compile(getJsEngine(), script);
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
throw new ScriptRuntimeException(e);
|
throw new ScriptRuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -111,7 +172,7 @@ public class ScriptUtil {
|
|||||||
*/
|
*/
|
||||||
public static CompiledScript compile(ScriptEngine engine, String script) throws ScriptException {
|
public static CompiledScript compile(ScriptEngine engine, String script) throws ScriptException {
|
||||||
if (engine instanceof Compilable) {
|
if (engine instanceof Compilable) {
|
||||||
Compilable compEngine = (Compilable) engine;
|
final Compilable compEngine = (Compilable) engine;
|
||||||
return compEngine.compile(script);
|
return compEngine.compile(script);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
package cn.hutool.script.test;
|
package cn.hutool.script.test;
|
||||||
|
|
||||||
import javax.script.CompiledScript;
|
|
||||||
import javax.script.ScriptException;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import cn.hutool.script.ScriptRuntimeException;
|
import cn.hutool.script.ScriptRuntimeException;
|
||||||
import cn.hutool.script.ScriptUtil;
|
import cn.hutool.script.ScriptUtil;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.script.CompiledScript;
|
||||||
|
import javax.script.ScriptEngine;
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 脚本单元测试类
|
* 脚本单元测试类
|
||||||
@@ -30,4 +30,22 @@ public class ScriptUtilTest {
|
|||||||
public void evalTest() {
|
public void evalTest() {
|
||||||
ScriptUtil.eval("print('Script test!');");
|
ScriptUtil.eval("print('Script test!');");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pythonTest() throws ScriptException {
|
||||||
|
final ScriptEngine pythonEngine = ScriptUtil.getPythonEngine();
|
||||||
|
pythonEngine.eval("print('Hello Python')");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void luaTest() throws ScriptException {
|
||||||
|
final ScriptEngine engine = ScriptUtil.getLuaEngine();
|
||||||
|
engine.eval("print('Hello Lua')");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void groovyTest() throws ScriptException {
|
||||||
|
final ScriptEngine engine = ScriptUtil.getGroovyEngine();
|
||||||
|
engine.eval("println 'Hello Groovy'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user