enjoy 4.3 release ^_^

This commit is contained in:
James
2019-07-03 11:35:34 +08:00
parent c88f7baad9
commit 1939fb4cc0
17 changed files with 839 additions and 118 deletions

View File

@@ -279,6 +279,11 @@ public class Engine {
return this;
}
public Engine removeSharedObject(String name) {
config.removeSharedObject(name);
return this;
}
/**
* Set output directive factory
*/
@@ -521,10 +526,10 @@ public class Engine {
*
* 系统当前默认 FieldGetter 实现类及其位置如下:
* GetterMethodFieldGetter ---> 调用 getter 方法取值
* RealFieldGetter ---> 直接获取 public 型的 object.field 值
* ModelFieldGetter ---> 调用 Model.get(String) 方法取值
* RecordFieldGetter ---> 调用 Record.get(String) 方法取值
* MapFieldGetter ---> 调用 Map.get(String) 方法取值
* RealFieldGetter ---> 直接获取 public 型的 object.field 值
* MapFieldGetter ---> 调用 Map.get(String) 方法取值
* ArrayLengthGetter ---> 获取数组长度
*
* 根据以上次序,如果要插入 IsMethodFieldGetter 到 GetterMethodFieldGetter
@@ -551,8 +556,19 @@ public class Engine {
FieldKit.removeFieldGetter(fieldGetterClass);
}
public static void setToFastFieldKeyBuilder() {
FieldKeyBuilder.setToFastFieldKeyBuilder();
public static void setFastFieldKeyBuilder(boolean enable) {
FieldKeyBuilder.setFastFieldKeyBuilder(enable);
}
/**
* 设置极速模式
*
* 极速模式将生成代理对象来消除 java.lang.reflect.Method.invoke(...) 调用,
* 性能提升 12.9%
*/
public static void setFastMode(boolean fastMode) {
FieldKit.setFastMode(fastMode);
FieldKeyBuilder.setFastFieldKeyBuilder(fastMode);
}
}

View File

@@ -209,10 +209,16 @@ public class EngineConfig {
sharedObjectMap.put(name, object);
}
Map<String, Object> getSharedObjectMap() {
public Map<String, Object> getSharedObjectMap() {
return sharedObjectMap;
}
public synchronized void removeSharedObject(String name) {
if (sharedObjectMap != null) {
sharedObjectMap.remove(name);
}
}
/**
* Set output directive factory
*/

View File

@@ -108,7 +108,7 @@ public class Template {
public StringBuilder renderToStringBuilder(Map<?, ?> data) {
FastStringWriter fsw = new FastStringWriter();
render(data, fsw);
return fsw.getBuffer();
return fsw.toStringBuilder();
}
/**

View File

@@ -0,0 +1,181 @@
package com.jfinal.template.expr.ast;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.jfinal.kit.StrKit;
import com.jfinal.proxy.ProxyClassLoader;
/**
* 使用 jfinal proxy 机制消除 java.lang.reflect.Method.invoke(...)
* 提升性能,并且同时支持动态类型的 field 表达式取值
*/
public class FastFieldGetter extends FieldGetter {
protected static ProxyGenerator generator = new ProxyGenerator();
protected static ProxyCompiler compiler = new ProxyCompiler();
protected static ProxyClassLoader classLoader = new ProxyClassLoader();
protected static Map<Class<?>, Proxy> cache = new ConcurrentHashMap<>(512, 0.25F);
protected static boolean outputCompileError = false;
protected Proxy proxy;
protected java.lang.reflect.Method getterMethod;
public FastFieldGetter(Proxy proxy, java.lang.reflect.Method getterMethod) {
this.proxy = proxy;
this.getterMethod = getterMethod;
}
/**
* 仅用于配置 Engine.addFieldGetter(0, new FastFieldGetter());
*/
public FastFieldGetter() {
this(null, null);
}
public FieldGetter takeOver(Class<?> targetClass, String fieldName) {
if (MethodKit.isForbiddenClass(targetClass)) {
throw new RuntimeException("Forbidden class: " + targetClass.getName());
}
String getterName = "get" + StrKit.firstCharToUpperCase(fieldName);
java.lang.reflect.Method[] methodArray = targetClass.getMethods();
for (java.lang.reflect.Method method : methodArray) {
if (method.getName().equals(getterName) && method.getParameterCount() == 0) {
Proxy proxy = cache.get(targetClass);
if (proxy == null) {
synchronized (targetClass) {
proxy = cache.get(targetClass);
if (proxy == null) {
try {
proxy = createProxy(targetClass, fieldName);
} catch (Throwable e) {
return null;
}
cache.putIfAbsent(targetClass, proxy);
}
}
}
return new FastFieldGetter(proxy, method);
}
}
return null;
}
public Object get(Object target, String fieldName) throws Exception {
// return getterMethod.invoke(target, ExprList.NULL_OBJECT_ARRAY);
return proxy.getValue(target, fieldName);
}
protected Proxy createProxy(Class<?> targetClass, String fieldName) {
ProxyClass proxyClass = new ProxyClass(targetClass);
String sourceCode = generator.generate(proxyClass);
// System.out.println(sourceCode);
proxyClass.setSourceCode(sourceCode);
compiler.compile(proxyClass);
Class<?> retClass = classLoader.loadProxyClass(proxyClass);
proxyClass.setClazz(retClass);
try {
return (Proxy)retClass.newInstance();
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public String toString() {
return getterMethod.toString();
}
// ---------
/**
* 代理接口
*/
public static interface Proxy {
public Object getValue(Object target, String fieldName);
}
// ---------
/**
* 代理类
*/
static class ProxyClass extends com.jfinal.proxy.ProxyClass {
private String name; // 类名
public ProxyClass(Class<?> target) {
super(target);
name = target.getSimpleName() + "$$EnhancerByJFinal_FieldGetter";
}
public String getName() {
return name;
}
}
// ---------
/**
* 代理生成器
*/
static class ProxyGenerator {
String generate(ProxyClass proxyClass) {
StringBuilder ret = new StringBuilder(1024);
Class<?> targetClass = proxyClass.getTarget();
String className = proxyClass.getName();
ret.append("package ").append(proxyClass.getPkg()).append(";\n\n");
ret.append("import com.jfinal.template.expr.ast.FastFieldGetter.Proxy;\n\n");
ret.append("public class ").append(className).append(" implements Proxy {\n\n");
ret.append("\tpublic Object getValue(Object target, String fieldName) {\n");
ret.append("\t\tint hash = fieldName.hashCode();\n");
ret.append("\t\tswitch (hash) {\n");
java.lang.reflect.Method[] methodArray = targetClass.getMethods();
for (java.lang.reflect.Method method : methodArray) {
String mn = method.getName();
if (method.getParameterCount() == 0 && mn.startsWith("get") && (!mn.equals("getClass"))) {
String fieldName = StrKit.firstCharToLowerCase(mn.substring(3));
ret.append("\t\tcase ").append(fieldName.hashCode()).append(" :\n");
ret.append("\t\t\treturn ((").append(targetClass.getName()).append(")target).").append(mn).append("();\n");
}
}
ret.append("\t\tdefault :\n");
ret.append("\t\t\tthrow new RuntimeException(\"Can not access the field \\\"\" + target.getClass().getName() + \".\" + fieldName + \"\\\"\");\n");
ret.append("\t\t}\n");
ret.append("\t}\n");
ret.append("}\n");
return ret.toString();
}
}
// ---------
public static void setOutputCompileError(boolean outputCompileError) {
FastFieldGetter.outputCompileError = outputCompileError;
}
/**
* 代理编译器
*/
static class ProxyCompiler extends com.jfinal.proxy.ProxyCompiler {
@Override
protected void outputCompileError(Boolean result, javax.tools.DiagnosticCollector<javax.tools.JavaFileObject> collector) {
if (outputCompileError) {
super.outputCompileError(result, collector);
}
}
}
}

View File

@@ -68,7 +68,7 @@ public class FieldGetters {
String getterName = "get" + StrKit.firstCharToUpperCase(fieldName);
java.lang.reflect.Method[] methodArray = targetClass.getMethods();
for (java.lang.reflect.Method method : methodArray) {
if (method.getName().equals(getterName) && method.getParameterTypes().length == 0) {
if (method.getName().equals(getterName) && method.getParameterCount() == 0) {
// if (MethodKit.isForbiddenMethod(getterName)) {
// throw new RuntimeException("Forbidden method: " + getterName);
// }
@@ -115,7 +115,7 @@ public class FieldGetters {
String isMethodName = "is" + StrKit.firstCharToUpperCase(fieldName);
java.lang.reflect.Method[] methodArray = targetClass.getMethods();
for (java.lang.reflect.Method method : methodArray) {
if (method.getName().equals(isMethodName) && method.getParameterTypes().length == 0) {
if (method.getName().equals(isMethodName) && method.getParameterCount() == 0) {
Class<?> returnType = method.getReturnType();
if (returnType == Boolean.class || returnType == boolean.class) {
return new IsMethodFieldGetter(method);

View File

@@ -33,10 +33,14 @@ public abstract class FieldKeyBuilder {
}
/**
* 设置为官方提供的 FastFieldKeyBuilder 实现,性能更高
* 开启 FastFieldKeyBuilder性能更高
*/
public static void setToFastFieldKeyBuilder() {
instance = new FastFieldKeyBuilder();
public static void setFastFieldKeyBuilder(boolean enable) {
if (enable) {
instance = new FastFieldKeyBuilder();
} else {
instance = new StrictFieldKeyBuilder();
}
}
/**

View File

@@ -140,6 +140,36 @@ public class FieldKit {
public static void clearCache() {
fieldGetterCache.clear();
}
/**
* 设置极速模式
*
* 极速模式将生成代理对象来消除 java.lang.reflect.Method.invoke(...) 调用,
* 性能提升 12.9%
*/
public static synchronized void setFastMode(boolean fastMode) {
if (fastMode) {
if ( !contains(FastFieldGetter.class) ) {
addFieldGetterToFirst(new FastFieldGetter());
}
} else {
if (contains(FastFieldGetter.class)) {
removeFieldGetter(FastFieldGetter.class);
}
}
}
/**
* 判断是否包含某个 FieldGetter
*/
public static boolean contains(Class<? extends FieldGetter> fieldGetterClass) {
for (FieldGetter fg : getters) {
if (fg.getClass() == fieldGetterClass) {
return true;
}
}
return false;
}
}

View File

@@ -16,9 +16,12 @@
package com.jfinal.template.ext.directive;
import java.io.IOException;
import com.jfinal.template.Directive;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
/**
@@ -29,45 +32,67 @@ import com.jfinal.template.stat.Scope;
public class EscapeDirective extends Directive {
public void exec(Env env, Scope scope, Writer writer) {
Object value = exprList.eval(scope);
if (value != null) {
write(writer, escape(value.toString()));
try {
Object value = exprList.eval(scope);
if (value instanceof String) {
escape((String)value, writer);
} else if (value instanceof Number) {
Class<?> c = value.getClass();
if (c == Integer.class) {
writer.write((Integer)value);
} else if (c == Long.class) {
writer.write((Long)value);
} else if (c == Double.class) {
writer.write((Double)value);
} else if (c == Float.class) {
writer.write((Float)value);
} else if (c == Short.class) {
writer.write((Short)value);
} else {
writer.write(value.toString());
}
} else if (value != null) {
escape(value.toString(), writer);
}
} catch(TemplateException | ParseException e) {
throw e;
} catch(Exception e) {
throw new TemplateException(e.getMessage(), location, e);
}
}
// TODO 挪到 StrKit 中
private String escape(String str) {
if (str == null || str.length() == 0) {
return str;
private void escape(String str, Writer w) throws IOException {
int len = str.length();
if (len == 0) {
return ;
}
int len = str.length();
StringBuilder ret = new StringBuilder(len * 2);
for (int i = 0; i < len; i++) {
char cur = str.charAt(i);
switch (cur) {
case '<':
ret.append("&lt;");
w.write("&lt;");
break;
case '>':
ret.append("&gt;");
w.write("&gt;");
break;
case '"':
ret.append("&quot;");
w.write("&quot;");
break;
case '\'':
// ret.append("&apos;"); // IE 不支持 &apos; 考虑 &#39;
ret.append("&#39;");
// w.write("&apos;"); // IE 不支持 &apos; 考虑 &#39;
w.write("&#39;");
break;
case '&':
ret.append("&amp;");
w.write("&amp;");
break;
default:
ret.append(cur);
w.write(str, i, 1);
break;
}
}
return ret.toString();
}
}

View File

@@ -16,102 +16,153 @@
package com.jfinal.template.io;
import java.io.IOException;
import java.io.Writer;
/**
* FastStringWriter
*
* <pre>
* 由 JDK 中 StringWriter 改造而来,在其基础之上做了如下改变:
* 1StringBuffer 属性改为 StringBuilder避免了前者的 synchronized 操作
* 2添加了 MAX_SIZE 属性
* 3去掉了 close() 方法声明中的 throws IOException并添加了代码原先该方法中无任何代码
* 由 JDK 中 Writer 改造而来,在其基础之上做了如下改变:
* 1添加 char[] value 直接保存 char 值
* 2添加 int len 记录数据长度
* 3去掉 synchronized 操作
* 4添加 MAX_BUFFER_SIZE限定 value 被重用的最大长度
* 5去掉了 close() 方法声明中的 throws IOException并添加缓存回收逻辑
* </pre>
*/
public class FastStringWriter extends Writer {
private StringBuilder buf;
private char[] value;
private int len;
public FastStringWriter() {
buf = new StringBuilder();
}
public FastStringWriter(int initialSize) {
if (initialSize < 0) {
throw new IllegalArgumentException("Negative buffer size");
}
buf = new StringBuilder(initialSize);
}
public void write(int c) {
buf.append((char) c);
}
public void write(char cbuf[], int off, int len) {
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
buf.append(cbuf, off, len);
}
public void write(String str) {
buf.append(str);
}
public void write(String str, int off, int len) {
buf.append(str.substring(off, off + len));
}
public FastStringWriter append(CharSequence csq) {
if (csq == null) {
write("null");
} else {
write(csq.toString());
}
return this;
}
public FastStringWriter append(CharSequence csq, int start, int end) {
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
}
public FastStringWriter append(char c) {
write(c);
return this;
}
public String toString() {
return buf.toString();
}
public StringBuilder getBuffer() {
return buf;
}
public void flush() {
}
static int MAX_SIZE = 1024 * 64;
/**
* 由 StringWriter.close() 改造而来,原先该方法中无任何代码 ,改造如下:
* 1去掉 throws IOException
* 2添加 buf 空间释放处理逻辑
* 3添加 buf.setLength(0),以便于配合 ThreadLocal 回收利用
*/
public void close() {
if (buf.length() > MAX_SIZE) {
buf = new StringBuilder(MAX_SIZE / 2); // 释放空间占用过大的 buf
} else {
buf.setLength(0);
private static int MAX_BUFFER_SIZE = 1024 * 256; // 1024 * 64;
public static void setMaxBufferSize(int maxBufferSize) {
int min = 256;
if (maxBufferSize < min) {
throw new IllegalArgumentException("maxBufferSize must more than " + min);
}
}
MAX_BUFFER_SIZE = maxBufferSize;
}
@Override
public void close() /* throws IOException */ {
len = 0;
// 释放空间占用过大的缓存
if (value.length > MAX_BUFFER_SIZE) {
value = new char[Math.max(256, MAX_BUFFER_SIZE / 2)];
}
}
public String toString() {
return new String(value, 0, len);
}
public StringBuilder toStringBuilder() {
return new StringBuilder(len + 64).append(value, 0, len);
}
public FastStringWriter(int capacity) {
value = new char[capacity];
}
public FastStringWriter() {
this(128);
}
/**
* 扩容
*/
protected void expandCapacity(int newLen) {
int newCapacity = Math.max(newLen, value.length * 2);
char[] newValue = new char[newCapacity];
// 复制 value 中的值到 newValue
if (len > 0) {
System.arraycopy(value, 0, newValue, 0, len);
}
value = newValue;
}
@Override
public void write(char buffer[], int offset, int len) throws IOException {
int newLen = this.len + len;
if (newLen > value.length) {
expandCapacity(newLen);
}
System.arraycopy(buffer, offset, value, this.len, len);
this.len = newLen;
}
@Override
public void write(String str, int offset, int len) throws IOException {
int newLen = this.len + len;
if (newLen > value.length) {
expandCapacity(newLen);
}
str.getChars(offset, (offset + len), value, this.len);
this.len = newLen;
}
@Override
public void write(int c) throws IOException {
char[] buffer = {(char)c};
write(buffer, 0, 1);
}
@Override
public void write(char buffer[]) throws IOException {
write(buffer, 0, buffer.length);
}
@Override
public void write(String str) throws IOException {
write(str, 0, str.length());
}
@Override
public Writer append(CharSequence csq) throws IOException {
if (csq instanceof String) {
String str = (String)csq;
write(str, 0, str.length());
return this;
}
if (csq == null)
write("null");
else
write(csq.toString());
return this;
}
@Override
public Writer append(CharSequence csq, int start, int end) throws IOException {
if (csq instanceof String) {
String str = (String)csq;
write(str, start, (end - start));
return this;
}
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
}
@Override
public Writer append(char c) throws IOException {
char[] buffer = {c};
write(buffer, 0, 1);
return this;
}
@Override
public void flush() throws IOException {
}
}

View File

@@ -66,8 +66,6 @@ public class Output extends Stat {
} else {
writer.write(value.toString());
}
} else if (value instanceof Boolean) {
writer.write((Boolean)value);
} else if (value != null) {
writer.write(value.toString());
}