diff --git a/pom.xml b/pom.xml
index 4774c6e..07d4164 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,8 +73,8 @@
maven-compiler-plugin
3.6.1
- 1.6
- 1.6
+ 1.7
+ 1.7
@@ -91,21 +91,21 @@
-
-
- org.apache.maven.plugins
- maven-source-plugin
- 2.1.2
-
-
- attach-sources
- verify
-
- jar-no-fork
-
-
-
-
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.1.2
+
+
+ attach-sources
+ verify
+
+ jar-no-fork
+
+
+
+
org.apache.maven.plugins
diff --git a/src/main/java/com/jfinal/template/Engine.java b/src/main/java/com/jfinal/template/Engine.java
index 96b8c3c..d908b35 100644
--- a/src/main/java/com/jfinal/template/Engine.java
+++ b/src/main/java/com/jfinal/template/Engine.java
@@ -22,6 +22,9 @@ import java.util.Map;
import com.jfinal.kit.HashKit;
import com.jfinal.kit.StrKit;
import com.jfinal.kit.SyncWriteMap;
+import com.jfinal.template.expr.ast.FieldGetter;
+import com.jfinal.template.expr.ast.FieldKeyBuilder;
+import com.jfinal.template.expr.ast.FieldKit;
import com.jfinal.template.expr.ast.MethodKit;
import com.jfinal.template.source.ClassPathSourceFactory;
import com.jfinal.template.source.ISource;
@@ -126,7 +129,7 @@ public class Engine {
}
/**
- * Get template with file name
+ * Get template by file name
*/
public Template getTemplate(String fileName) {
if (fileName.charAt(0) != '/') {
@@ -172,9 +175,9 @@ public class Engine {
/**
* Get template by string content
*
- * 重要:StringSource 中的 key = HashKit.md5(content),也即 key
- * 与 content 有紧密的对应关系,当 content 发生变化时 key 值也相应变化
- * 因此,原先 key 所对应的 Template 缓存对象已无法被获取,当 getTemplateByString(String)
+ * 重要:StringSource 中的 cacheKey = HashKit.md5(content),也即 cacheKey
+ * 与 content 有紧密的对应关系,当 content 发生变化时 cacheKey 值也相应变化
+ * 因此,原先 cacheKey 所对应的 Template 缓存对象已无法被获取,当 getTemplateByString(String)
* 的 String 参数的数量不确定时会引发内存泄漏
*
* 当 getTemplateByString(String, boolean) 中的 String 参数的
@@ -188,37 +191,37 @@ public class Engine {
return buildTemplateBySource(new StringSource(content, cache));
}
- String key = HashKit.md5(content);
- Template template = templateCache.get(key);
+ String cacheKey = HashKit.md5(content);
+ Template template = templateCache.get(cacheKey);
if (template == null) {
template = buildTemplateBySource(new StringSource(content, cache));
- templateCache.put(key, template);
+ templateCache.put(cacheKey, template);
} else if (devMode) {
if (template.isModified()) {
template = buildTemplateBySource(new StringSource(content, cache));
- templateCache.put(key, template);
+ templateCache.put(cacheKey, template);
}
}
return template;
}
/**
- * Get template with implementation of ISource
+ * Get template by implementation of ISource
*/
public Template getTemplate(ISource source) {
- String key = source.getKey();
- if (key == null) { // key 为 null 则不缓存,详见 ISource.getKey() 注释
+ String cacheKey = source.getCacheKey();
+ if (cacheKey == null) { // cacheKey 为 null 则不缓存,详见 ISource.getCacheKey() 注释
return buildTemplateBySource(source);
}
- Template template = templateCache.get(key);
+ Template template = templateCache.get(cacheKey);
if (template == null) {
template = buildTemplateBySource(source);
- templateCache.put(key, template);
+ templateCache.put(cacheKey, template);
} else if (devMode) {
if (template.isModified()) {
template = buildTemplateBySource(source);
- templateCache.put(key, template);
+ templateCache.put(cacheKey, template);
}
}
return template;
@@ -236,7 +239,7 @@ public class Engine {
}
/**
- * Add shared function with file
+ * Add shared function by file
*/
public Engine addSharedFunction(String fileName) {
config.addSharedFunction(fileName);
@@ -252,7 +255,7 @@ public class Engine {
}
/**
- * Add shared function with files
+ * Add shared function by files
*/
public Engine addSharedFunction(String... fileNames) {
config.addSharedFunction(fileNames);
@@ -336,7 +339,7 @@ public class Engine {
}
/**
- * Remove shared Method with method name
+ * Remove shared Method by method name
*/
public Engine removeSharedMethod(String methodName) {
config.removeSharedMethod(methodName);
@@ -360,10 +363,10 @@ public class Engine {
}
/**
- * Remove template cache with template key
+ * Remove template cache by cache key
*/
- public void removeTemplateCache(String templateKey) {
- templateCache.remove(templateKey);
+ public void removeTemplateCache(String cacheKey) {
+ templateCache.remove(cacheKey);
}
/**
@@ -500,6 +503,45 @@ public class Engine {
public static void removeExtensionMethod(Class> targetClass, Class> extensionClass) {
MethodKit.removeExtensionMethod(targetClass, extensionClass);
}
+
+ /**
+ * 添加 FieldGetter 实现类到指定的位置
+ *
+ * 系统当前默认 FieldGetter 实现类及其位置如下:
+ * GetterMethodFieldGetter ---> 调用 getter 方法取值
+ * ModelFieldGetter ---> 调用 Model.get(String) 方法取值
+ * RecordFieldGetter ---> 调用 Record.get(String) 方法取值
+ * MapFieldGetter ---> 调用 Map.get(String) 方法取值
+ * RealFieldGetter ---> 直接获取 public 型的 object.field 值
+ * ArrayLengthGetter ---> 获取数组长度
+ *
+ * 根据以上次序,如果要插入 IsMethodFieldGetter 到 GetterMethodFieldGetter
+ * 之后的代码如下:
+ * Engine.addFieldGetter(1, new IsMethodFieldGetter());
+ *
+ * 注:IsMethodFieldGetter 系统已经提供,只是默认没有启用。该实现类通过调用
+ * target.isXxx() 方法获取 target.xxx 表达式的值,其中 xxx 字段必须是
+ * Boolean/boolean 类型
+ */
+ public static void addFieldGetter(int index, FieldGetter fieldGetter) {
+ FieldKit.addFieldGetter(index, fieldGetter);
+ }
+
+ public static void addFieldGetterToLast(FieldGetter fieldGetter) {
+ FieldKit.addFieldGetterToLast(fieldGetter);
+ }
+
+ public static void addFieldGetterToFirst(FieldGetter fieldGetter) {
+ FieldKit.addFieldGetterToFirst(fieldGetter);
+ }
+
+ public static void removeFieldGetter(Class extends FieldGetter> fieldGetterClass) {
+ FieldKit.removeFieldGetter(fieldGetterClass);
+ }
+
+ public static void setToFastFieldKeyBuilder() {
+ FieldKeyBuilder.setToFastFieldKeyBuilder();
+ }
}
diff --git a/src/main/java/com/jfinal/template/expr/ast/Field.java b/src/main/java/com/jfinal/template/expr/ast/Field.java
index 09a15a1..929863b 100644
--- a/src/main/java/com/jfinal/template/expr/ast/Field.java
+++ b/src/main/java/com/jfinal/template/expr/ast/Field.java
@@ -16,11 +16,8 @@
package com.jfinal.template.expr.ast;
-import java.lang.reflect.Array;
import com.jfinal.kit.HashKit;
import com.jfinal.kit.StrKit;
-// import com.jfinal.plugin.activerecord.Model;
-// import com.jfinal.plugin.activerecord.Record;
import com.jfinal.template.TemplateException;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
@@ -68,49 +65,26 @@ public class Field extends Expr {
throw new TemplateException("Can not accessed by \"" + fieldName + "\" field from null target", location);
}
- Class> targetClass = target.getClass();
- Long key = buildFieldKey(targetClass);
- MethodInfo getter;
try {
- getter = MethodKit.getGetterMethod(key, targetClass, getterName);
+ Class> targetClass = target.getClass();
+ Object key = FieldKeyBuilder.instance.getFieldKey(targetClass, getterNameHash);
+ FieldGetter fieldGetter = FieldKit.getFieldGetter(key, targetClass, fieldName);
+ if (fieldGetter.notNull()) {
+ return fieldGetter.get(target, fieldName);
+ }
+ } catch (TemplateException e) {
+ throw e;
+ } catch (ParseException e) {
+ throw e;
} catch (Exception e) {
throw new TemplateException(e.getMessage(), location, e);
}
- try {
- if (getter != null) {
- return getter.invoke(target, ExprList.NULL_OBJECT_ARRAY);
- }
-// if (target instanceof Model) {
-// return ((Model>)target).get(fieldName);
-// }
-// if (target instanceof Record) {
-// return ((Record)target).get(fieldName);
-// }
- if (target instanceof java.util.Map) {
- return ((java.util.Map, ?>)target).get(fieldName);
- }
- // if (target instanceof com.jfinal.kit.Ret) {
- // return ((com.jfinal.kit.Ret)target).get(fieldName);
- // }
- java.lang.reflect.Field field = FieldKit.getField(key, targetClass, fieldName);
- if (field != null) {
- return field.get(target);
- }
-
- // 支持获取数组长度: array.length
- if ("length".equals(fieldName) && target.getClass().isArray()) {
- return Array.getLength(target);
- }
- } catch (Exception e) {
- throw new TemplateException(e.getMessage(), location, e);
- }
if (scope.getCtrl().isNullSafe()) {
return null;
}
-
if (expr instanceof Id) {
String id = ((Id)expr).getId();
throw new TemplateException("public field not found: \"" + id + "." + fieldName + "\" and public getter method not found: \"" + id + "." + getterName + "()\"", location);
@@ -118,9 +92,9 @@ public class Field extends Expr {
throw new TemplateException("public field not found: \"" + fieldName + "\" and public getter method not found: \"" + getterName + "()\"", location);
}
- private Long buildFieldKey(Class> targetClass) {
- return targetClass.getName().hashCode() ^ getterNameHash;
- }
+ // private Long buildFieldKey(Class> targetClass) {
+ // return targetClass.getName().hashCode() ^ getterNameHash;
+ // }
}
diff --git a/src/main/java/com/jfinal/template/expr/ast/FieldGetter.java b/src/main/java/com/jfinal/template/expr/ast/FieldGetter.java
new file mode 100644
index 0000000..5f2f474
--- /dev/null
+++ b/src/main/java/com/jfinal/template/expr/ast/FieldGetter.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.jfinal.template.expr.ast;
+
+/**
+ * FieldGetter 用于支持 target.field 表达式的取值,
+ * 以及支持用户扩展自定义的 FieldGetter 实现方式
+ */
+public abstract class FieldGetter {
+
+ /**
+ * 接管 target.fieldName 表达式,如果可以接管则返回接管对象,否则返回 null
+ * @param targetClass target.fieldName 表达式中 target 的 Class 类型
+ * @param fieldName target.fieldName 表达式中的 fieldName 部分
+ * @return 如果可以接管 targetClass.fieldName 则返回接管对象,否则返回 null
+ */
+ public abstract FieldGetter takeOver(Class> targetClass, String fieldName);
+
+ /**
+ * 获取 target.fieldName 表达式的值
+ * @param target 目标对象
+ * @param fieldName 字段名称
+ * @return target.fieldName 表达式的值
+ */
+ public abstract Object get(Object target, String fieldName) throws Exception;
+
+ /**
+ * 仅仅 NullFieldGetter 会覆盖此方法并返回 false
+ *
+ * 用于消除 FieldKit.getFieldGetter(...) 中的 instanceof 判断,
+ * 并且让 Map fieldGetterCache 的 value 值不必使用 Object 类型,
+ * 还消除了 Field.exec(...) 中的 null 值判断
+ */
+ public boolean notNull() {
+ return true;
+ }
+}
+
+
+
+
+
+
+
diff --git a/src/main/java/com/jfinal/template/expr/ast/FieldGetters.java b/src/main/java/com/jfinal/template/expr/ast/FieldGetters.java
new file mode 100644
index 0000000..3114e6d
--- /dev/null
+++ b/src/main/java/com/jfinal/template/expr/ast/FieldGetters.java
@@ -0,0 +1,270 @@
+/**
+ * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.jfinal.template.expr.ast;
+
+import java.lang.reflect.Array;
+import com.jfinal.kit.StrKit;
+
+/**
+ * FieldGetters 封装官方默认 FieldGetter 实现
+ */
+public class FieldGetters {
+
+ /**
+ * NullFieldGetter
+ *
+ * 用于消除 FieldKit.getFieldGetter(...) 中的 instanceof 判断,并且让 Map fieldGetterCache
+ * 中的 value 不必使用 Object 类型。还消除了 Field.exec(...) 中的 null 值判断
+ */
+ public static class NullFieldGetter extends FieldGetter {
+
+ public static final NullFieldGetter me = new NullFieldGetter();
+
+ public boolean notNull() {
+ return false;
+ }
+
+ public FieldGetter takeOver(Class> targetClass, String fieldName) {
+ throw new RuntimeException("The method takeOver(Class, String) of NullFieldGetter should not be invoked");
+ }
+
+ public Object get(Object target, String fieldName) throws Exception {
+ throw new RuntimeException("The method get(Object, String) of NullFieldGetter should not be invoked");
+ }
+ }
+
+ /**
+ * GetterMethodFieldGetter
+ *
+ * 使用 getter 方法获取 target.field 表达式的值
+ */
+ public static class GetterMethodFieldGetter extends FieldGetter {
+
+ protected java.lang.reflect.Method getterMethod;
+
+ public GetterMethodFieldGetter(java.lang.reflect.Method getterMethod) {
+ this.getterMethod = getterMethod;
+ }
+
+ 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.getParameterTypes().length == 0) {
+ if (MethodKit.isForbiddenMethod(getterName)) {
+ throw new RuntimeException("Forbidden method: " + getterName);
+ }
+
+ return new GetterMethodFieldGetter(method);
+ }
+ }
+
+ return null;
+ }
+
+ public Object get(Object target, String fieldName) throws Exception {
+ return getterMethod.invoke(target, ExprList.NULL_OBJECT_ARRAY);
+ }
+
+ public String toString() {
+ return getterMethod.toString();
+ }
+ }
+
+ /**
+ * IsMethodFieldGetter
+ *
+ * 使用 target.isXxx() 方法获取值,默认不启用该功能,用户可以通过如下方式启用:
+ * Engine.addLastFieldGetter(new FieldGetters.IsMethodFieldGetter());
+ */
+ public static class IsMethodFieldGetter extends FieldGetter {
+
+ protected java.lang.reflect.Method isMethod;
+
+ // 此构造方法仅为了方便在 Engine.addFieldGetter(...) 添加时不用为构造方法传参
+ public IsMethodFieldGetter() {
+ }
+
+ public IsMethodFieldGetter(java.lang.reflect.Method isMethod) {
+ this.isMethod = isMethod;
+ }
+
+ public FieldGetter takeOver(Class> targetClass, String fieldName) {
+ if (MethodKit.isForbiddenClass(targetClass)) {
+ throw new RuntimeException("Forbidden class: " + targetClass.getName());
+ }
+
+ 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) {
+ Class> returnType = method.getReturnType();
+ if (returnType == Boolean.class || returnType == boolean.class) {
+ return new IsMethodFieldGetter(method);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public Object get(Object target, String fieldName) throws Exception {
+ return isMethod.invoke(target, ExprList.NULL_OBJECT_ARRAY);
+ }
+
+ public String toString() {
+ return isMethod.toString();
+ }
+ }
+
+ /**
+ * ModelFieldGetter
+ *
+ * 使用 Model.get(String) 获取值
+
+ public static class ModelFieldGetter extends FieldGetter {
+
+ // 所有 Model 可以共享 ModelFieldGetter 获取属性
+ static final ModelFieldGetter singleton = new ModelFieldGetter();
+
+ public FieldGetter takeOver(Class> targetClass, String fieldName) {
+ if (com.jfinal.plugin.activerecord.Model.class.isAssignableFrom(targetClass)) {
+ return singleton;
+ } else {
+ return null;
+ }
+ }
+
+ public Object get(Object target, String fieldName) throws Exception {
+ return ((com.jfinal.plugin.activerecord.Model>)target).get(fieldName);
+ }
+ } */
+
+ /**
+ * RecordFieldGetter
+ *
+ * 使用 Record.get(String) 获取值
+
+ public static class RecordFieldGetter extends FieldGetter {
+
+ // 所有 Record 可以共享 RecordFieldGetter 获取属性
+ static final RecordFieldGetter singleton = new RecordFieldGetter();
+
+ public FieldGetter takeOver(Class> targetClass, String fieldName) {
+ if (com.jfinal.plugin.activerecord.Record.class.isAssignableFrom(targetClass)) {
+ return singleton;
+ } else {
+ return null;
+ }
+ }
+
+ public Object get(Object target, String fieldName) throws Exception {
+ return ((com.jfinal.plugin.activerecord.Record)target).get(fieldName);
+ }
+ } */
+
+ /**
+ * MapFieldGetter
+ *
+ * 使用 Map.get(Object) 获取值
+ */
+ public static class MapFieldGetter extends FieldGetter {
+
+ // 所有 Map 可以共享 MapFieldGetter 获取属性
+ static final MapFieldGetter singleton = new MapFieldGetter();
+
+ public FieldGetter takeOver(Class> targetClass, String fieldName) {
+ if (java.util.Map.class.isAssignableFrom(targetClass)) {
+ return singleton;
+ } else {
+ return null;
+ }
+ }
+
+ public Object get(Object target, String fieldName) throws Exception {
+ return ((java.util.Map, ?>)target).get(fieldName);
+ }
+ }
+
+ /**
+ * RealFieldGetter
+ *
+ * 使用 target.field 获取值
+ */
+ public static class RealFieldGetter extends FieldGetter {
+
+ protected java.lang.reflect.Field field;
+
+ public RealFieldGetter(java.lang.reflect.Field field) {
+ this.field = field;
+ }
+
+ public FieldGetter takeOver(Class> targetClass, String fieldName) {
+ java.lang.reflect.Field[] fieldArray = targetClass.getFields();
+ for (java.lang.reflect.Field field : fieldArray) {
+ if (field.getName().equals(fieldName)) {
+ return new RealFieldGetter(field);
+ }
+ }
+
+ return null;
+ }
+
+ public Object get(Object target, String fieldName) throws Exception {
+ return field.get(target);
+ }
+
+ public String toString() {
+ return field.toString();
+ }
+ }
+
+ /**
+ * ArrayLengthGetter
+ *
+ * 获取数组长度: array.length
+ */
+ public static class ArrayLengthGetter extends FieldGetter {
+
+ // 所有数组可以共享 ArrayLengthGetter 获取属性
+ static final ArrayLengthGetter singleton = new ArrayLengthGetter();
+
+ public FieldGetter takeOver(Class> targetClass, String fieldName) {
+ if ("length".equals(fieldName) && targetClass.isArray()) {
+ return singleton;
+ } else {
+ return null;
+ }
+ }
+
+ public Object get(Object target, String fieldName) throws Exception {
+ return Array.getLength(target);
+ }
+ }
+}
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/jfinal/template/expr/ast/FieldKeyBuilder.java b/src/main/java/com/jfinal/template/expr/ast/FieldKeyBuilder.java
new file mode 100644
index 0000000..6231139
--- /dev/null
+++ b/src/main/java/com/jfinal/template/expr/ast/FieldKeyBuilder.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.jfinal.template.expr.ast;
+
+/**
+ * FieldKeyBuilder
+ *
+ * 用于生成缓存 FieldGetter 的 key
+ */
+public abstract class FieldKeyBuilder {
+
+ public abstract Object getFieldKey(Class> targetClass, long fieldFnv1a64Hash);
+
+ // 假定是超大规模项目,并且假定其 Map/Model/Record + field 组合数量超级庞大,默认使用 StrictFieldKeyBuilder
+ static FieldKeyBuilder instance = new StrictFieldKeyBuilder();
+
+ public static FieldKeyBuilder getInstance() {
+ return instance;
+ }
+
+ /**
+ * 设置为官方提供的 FastFieldKeyBuilder 实现,性能更高
+ */
+ public static void setToFastFieldKeyBuilder() {
+ instance = new FastFieldKeyBuilder();
+ }
+
+ /**
+ * 设置为自定义 FieldKeyBuilder
+ */
+ public static void setFieldKeyBuilder(FieldKeyBuilder fieldKeyBuilder) {
+ if (fieldKeyBuilder == null) {
+ throw new IllegalArgumentException("fieldKeyBuilder can not be null");
+ }
+ instance = fieldKeyBuilder;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * FastFieldKeyBuilder
+ */
+ public static class FastFieldKeyBuilder extends FieldKeyBuilder {
+ public Object getFieldKey(Class> targetClass, long fieldFnv1a64Hash) {
+ return targetClass.getName().hashCode() ^ fieldFnv1a64Hash;
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * StrictFieldKeyBuilder
+ */
+ public static class StrictFieldKeyBuilder extends FieldKeyBuilder {
+ public Object getFieldKey(Class> targetClass, long fieldFnv1a64Hash) {
+ return new FieldKey(targetClass.getName().hashCode(), fieldFnv1a64Hash);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * FieldKey
+ *
+ * FieldKey 用于封装 targetClass、fieldName 这两部分的 hash 值,
+ * 确保不会出现 key 值碰撞
+ *
+ * 这两部分 hash 值在不同 class 与 field 的组合下出现碰撞的
+ * 概率完全可以忽略不计
+ *
+ * 备忘:
+ * 可以考虑用 ThreadLocal 重用 FieldKey 对象,但要注意放入 Map fieldGetterCache
+ * 中的 FieldKey 对象需要 clone 出来,确保线程安全。由于 FieldKey 占用空间不大,
+ * 所以 ThreadLocal 方案大概率并无优势,从 ThreadLocal 中获取数据时,除了耗时也无法
+ * 避免创建对象
+ */
+ public static class FieldKey {
+
+ final long classHash;
+ final long fieldHash;
+
+ public FieldKey(long classHash, long fieldHash) {
+ this.classHash = classHash;
+ this.fieldHash = fieldHash;
+ }
+
+ public int hashCode() {
+ return (int)(classHash ^ fieldHash);
+ }
+
+ /**
+ * FieldKey 的核心价值在于此 equals 方法通过比较两部分 hash 值,避免超大规模场景下可能的 key 值碰撞
+ *
+ * 不必判断 if (fieldKey instanceof FieldKey),因为所有 key 类型必须要相同
+ * 不必判断 if (this == fieldKey),因为每次用于取值的 FieldKey 都是新建的
+ */
+ public boolean equals(Object fieldKey) {
+ FieldKey fk = (FieldKey)fieldKey;
+ return classHash == fk.classHash && fieldHash == fk.fieldHash;
+ }
+
+ public String toString() {
+ return "classHash = " + classHash + "\nfieldHash = " + fieldHash;
+ }
+ }
+}
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/jfinal/template/expr/ast/FieldKit.java b/src/main/java/com/jfinal/template/expr/ast/FieldKit.java
index f233f38..84f1558 100644
--- a/src/main/java/com/jfinal/template/expr/ast/FieldKit.java
+++ b/src/main/java/com/jfinal/template/expr/ast/FieldKit.java
@@ -16,39 +16,122 @@
package com.jfinal.template.expr.ast;
-import java.lang.reflect.Field;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
import com.jfinal.kit.SyncWriteMap;
+import com.jfinal.template.expr.ast.FieldGetters.*;
/**
* FieldKit
*/
public class FieldKit {
- private static final HashMap fieldCache = new SyncWriteMap(512, 0.5F);
+ private static FieldGetter[] getters = init();
- public static Field getField(Long key, Class> targetClass, String fieldName) {
- Object field = fieldCache.get(key);
- if (field == null) {
- field = doGetField(targetClass, fieldName);
- if (field != null) {
- fieldCache.put(key, field);
- } else {
- // 对于不存在的 Field,只进行一次获取操作,主要为了支持 null safe,未来需要考虑内存泄漏风险
- fieldCache.put(key, Boolean.FALSE);
- }
- }
- return field instanceof Field ? (Field)field : null;
+ private static final HashMap