jfinal enjoy 3.5
This commit is contained in:
parent
49d53e9f55
commit
13f2d302c3
10
pom.xml
10
pom.xml
@ -73,8 +73,8 @@
|
|||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.6.1</version>
|
<version>3.6.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>1.6</source>
|
<source>1.7</source>
|
||||||
<target>1.6</target>
|
<target>1.7</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
@ -91,8 +91,8 @@
|
|||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<!-- 安装源码到本地仓库 -->
|
<!-- 安装源码到本地仓库 -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-source-plugin</artifactId>
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
<version>2.1.2</version>
|
<version>2.1.2</version>
|
||||||
@ -105,7 +105,7 @@
|
|||||||
</goals>
|
</goals>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
@ -22,6 +22,9 @@ import java.util.Map;
|
|||||||
import com.jfinal.kit.HashKit;
|
import com.jfinal.kit.HashKit;
|
||||||
import com.jfinal.kit.StrKit;
|
import com.jfinal.kit.StrKit;
|
||||||
import com.jfinal.kit.SyncWriteMap;
|
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.expr.ast.MethodKit;
|
||||||
import com.jfinal.template.source.ClassPathSourceFactory;
|
import com.jfinal.template.source.ClassPathSourceFactory;
|
||||||
import com.jfinal.template.source.ISource;
|
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) {
|
public Template getTemplate(String fileName) {
|
||||||
if (fileName.charAt(0) != '/') {
|
if (fileName.charAt(0) != '/') {
|
||||||
@ -172,9 +175,9 @@ public class Engine {
|
|||||||
/**
|
/**
|
||||||
* Get template by string content
|
* Get template by string content
|
||||||
*
|
*
|
||||||
* 重要:StringSource 中的 key = HashKit.md5(content),也即 key
|
* 重要:StringSource 中的 cacheKey = HashKit.md5(content),也即 cacheKey
|
||||||
* 与 content 有紧密的对应关系,当 content 发生变化时 key 值也相应变化
|
* 与 content 有紧密的对应关系,当 content 发生变化时 cacheKey 值也相应变化
|
||||||
* 因此,原先 key 所对应的 Template 缓存对象已无法被获取,当 getTemplateByString(String)
|
* 因此,原先 cacheKey 所对应的 Template 缓存对象已无法被获取,当 getTemplateByString(String)
|
||||||
* 的 String 参数的数量不确定时会引发内存泄漏
|
* 的 String 参数的数量不确定时会引发内存泄漏
|
||||||
*
|
*
|
||||||
* 当 getTemplateByString(String, boolean) 中的 String 参数的
|
* 当 getTemplateByString(String, boolean) 中的 String 参数的
|
||||||
@ -188,37 +191,37 @@ public class Engine {
|
|||||||
return buildTemplateBySource(new StringSource(content, cache));
|
return buildTemplateBySource(new StringSource(content, cache));
|
||||||
}
|
}
|
||||||
|
|
||||||
String key = HashKit.md5(content);
|
String cacheKey = HashKit.md5(content);
|
||||||
Template template = templateCache.get(key);
|
Template template = templateCache.get(cacheKey);
|
||||||
if (template == null) {
|
if (template == null) {
|
||||||
template = buildTemplateBySource(new StringSource(content, cache));
|
template = buildTemplateBySource(new StringSource(content, cache));
|
||||||
templateCache.put(key, template);
|
templateCache.put(cacheKey, template);
|
||||||
} else if (devMode) {
|
} else if (devMode) {
|
||||||
if (template.isModified()) {
|
if (template.isModified()) {
|
||||||
template = buildTemplateBySource(new StringSource(content, cache));
|
template = buildTemplateBySource(new StringSource(content, cache));
|
||||||
templateCache.put(key, template);
|
templateCache.put(cacheKey, template);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get template with implementation of ISource
|
* Get template by implementation of ISource
|
||||||
*/
|
*/
|
||||||
public Template getTemplate(ISource source) {
|
public Template getTemplate(ISource source) {
|
||||||
String key = source.getKey();
|
String cacheKey = source.getCacheKey();
|
||||||
if (key == null) { // key 为 null 则不缓存,详见 ISource.getKey() 注释
|
if (cacheKey == null) { // cacheKey 为 null 则不缓存,详见 ISource.getCacheKey() 注释
|
||||||
return buildTemplateBySource(source);
|
return buildTemplateBySource(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
Template template = templateCache.get(key);
|
Template template = templateCache.get(cacheKey);
|
||||||
if (template == null) {
|
if (template == null) {
|
||||||
template = buildTemplateBySource(source);
|
template = buildTemplateBySource(source);
|
||||||
templateCache.put(key, template);
|
templateCache.put(cacheKey, template);
|
||||||
} else if (devMode) {
|
} else if (devMode) {
|
||||||
if (template.isModified()) {
|
if (template.isModified()) {
|
||||||
template = buildTemplateBySource(source);
|
template = buildTemplateBySource(source);
|
||||||
templateCache.put(key, template);
|
templateCache.put(cacheKey, template);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 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) {
|
public Engine addSharedFunction(String fileName) {
|
||||||
config.addSharedFunction(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) {
|
public Engine addSharedFunction(String... fileNames) {
|
||||||
config.addSharedFunction(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) {
|
public Engine removeSharedMethod(String methodName) {
|
||||||
config.removeSharedMethod(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) {
|
public void removeTemplateCache(String cacheKey) {
|
||||||
templateCache.remove(templateKey);
|
templateCache.remove(cacheKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -500,6 +503,45 @@ public class Engine {
|
|||||||
public static void removeExtensionMethod(Class<?> targetClass, Class<?> extensionClass) {
|
public static void removeExtensionMethod(Class<?> targetClass, Class<?> extensionClass) {
|
||||||
MethodKit.removeExtensionMethod(targetClass, 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,11 +16,8 @@
|
|||||||
|
|
||||||
package com.jfinal.template.expr.ast;
|
package com.jfinal.template.expr.ast;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import com.jfinal.kit.HashKit;
|
import com.jfinal.kit.HashKit;
|
||||||
import com.jfinal.kit.StrKit;
|
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.TemplateException;
|
||||||
import com.jfinal.template.stat.Location;
|
import com.jfinal.template.stat.Location;
|
||||||
import com.jfinal.template.stat.ParseException;
|
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);
|
throw new TemplateException("Can not accessed by \"" + fieldName + "\" field from null target", location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
Class<?> targetClass = target.getClass();
|
Class<?> targetClass = target.getClass();
|
||||||
Long key = buildFieldKey(targetClass);
|
Object key = FieldKeyBuilder.instance.getFieldKey(targetClass, getterNameHash);
|
||||||
|
FieldGetter fieldGetter = FieldKit.getFieldGetter(key, targetClass, fieldName);
|
||||||
MethodInfo getter;
|
if (fieldGetter.notNull()) {
|
||||||
try {
|
return fieldGetter.get(target, fieldName);
|
||||||
getter = MethodKit.getGetterMethod(key, targetClass, getterName);
|
}
|
||||||
|
} catch (TemplateException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw e;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new TemplateException(e.getMessage(), location, 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()) {
|
if (scope.getCtrl().isNullSafe()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expr instanceof Id) {
|
if (expr instanceof Id) {
|
||||||
String id = ((Id)expr).getId();
|
String id = ((Id)expr).getId();
|
||||||
throw new TemplateException("public field not found: \"" + id + "." + fieldName + "\" and public getter method not found: \"" + id + "." + getterName + "()\"", location);
|
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);
|
throw new TemplateException("public field not found: \"" + fieldName + "\" and public getter method not found: \"" + getterName + "()\"", location);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long buildFieldKey(Class<?> targetClass) {
|
// private Long buildFieldKey(Class<?> targetClass) {
|
||||||
return targetClass.getName().hashCode() ^ getterNameHash;
|
// return targetClass.getName().hashCode() ^ getterNameHash;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
58
src/main/java/com/jfinal/template/expr/ast/FieldGetter.java
Normal file
58
src/main/java/com/jfinal/template/expr/ast/FieldGetter.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
270
src/main/java/com/jfinal/template/expr/ast/FieldGetters.java
Normal file
270
src/main/java/com/jfinal/template/expr/ast/FieldGetters.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
128
src/main/java/com/jfinal/template/expr/ast/FieldKeyBuilder.java
Normal file
128
src/main/java/com/jfinal/template/expr/ast/FieldKeyBuilder.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -16,39 +16,122 @@
|
|||||||
|
|
||||||
package com.jfinal.template.expr.ast;
|
package com.jfinal.template.expr.ast;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
import com.jfinal.kit.SyncWriteMap;
|
import com.jfinal.kit.SyncWriteMap;
|
||||||
|
import com.jfinal.template.expr.ast.FieldGetters.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FieldKit
|
* FieldKit
|
||||||
*/
|
*/
|
||||||
public class FieldKit {
|
public class FieldKit {
|
||||||
|
|
||||||
private static final HashMap<Long, Object> fieldCache = new SyncWriteMap<Long, Object>(512, 0.5F);
|
private static FieldGetter[] getters = init();
|
||||||
|
|
||||||
public static Field getField(Long key, Class<?> targetClass, String fieldName) {
|
private static final HashMap<Object, FieldGetter> fieldGetterCache = new SyncWriteMap<Object, FieldGetter>(1024, 0.25F);
|
||||||
Object field = fieldCache.get(key);
|
|
||||||
if (field == null) {
|
/**
|
||||||
field = doGetField(targetClass, fieldName);
|
* 初始化官方默认 FieldGetter
|
||||||
if (field != null) {
|
*
|
||||||
fieldCache.put(key, field);
|
* 注意:
|
||||||
|
* 默认不启用 IsMethodFieldGetter,用户可以通过下面的代码启用:
|
||||||
|
* Engine.addLastFieldGetter(new FieldGetters.IsMethodFieldGetter());
|
||||||
|
*
|
||||||
|
* 也可以通过直接调用 target.isXxx() 方法来达到与 target.xxx 表达式相同的目的
|
||||||
|
*/
|
||||||
|
private static FieldGetter[] init() {
|
||||||
|
LinkedList<FieldGetter> ret = new LinkedList<FieldGetter>();
|
||||||
|
|
||||||
|
ret.addLast(new GetterMethodFieldGetter(null));
|
||||||
|
// ret.addLast(new ModelFieldGetter());
|
||||||
|
// ret.addLast(new RecordFieldGetter());
|
||||||
|
ret.addLast(new MapFieldGetter());
|
||||||
|
ret.addLast(new RealFieldGetter(null));
|
||||||
|
ret.addLast(new ArrayLengthGetter());
|
||||||
|
// ret.addLast(new IsMethodFieldGetter());
|
||||||
|
|
||||||
|
return ret.toArray(new FieldGetter[ret.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FieldGetter getFieldGetter(Object key, Class<?> targetClass, String fieldName) {
|
||||||
|
FieldGetter fieldGetter = fieldGetterCache.get(key);
|
||||||
|
if (fieldGetter == null) {
|
||||||
|
fieldGetter = doGetFieldGetter(targetClass, fieldName); // 已确保不会返回 null
|
||||||
|
fieldGetterCache.putIfAbsent(key, fieldGetter);
|
||||||
|
}
|
||||||
|
return fieldGetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FieldGetter doGetFieldGetter(Class<?> targetClass, String fieldName) {
|
||||||
|
FieldGetter ret;
|
||||||
|
for (FieldGetter fieldGetter : getters) {
|
||||||
|
ret = fieldGetter.takeOver(targetClass, fieldName);
|
||||||
|
if (ret != null) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NullFieldGetter.me;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addFieldGetter(int index, FieldGetter fieldGetter) {
|
||||||
|
addFieldGetter(fieldGetter, index, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addFieldGetterToLast(FieldGetter fieldGetter) {
|
||||||
|
addFieldGetter(fieldGetter, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addFieldGetterToFirst(FieldGetter fieldGetter) {
|
||||||
|
addFieldGetter(fieldGetter, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当 Integer index 不为 null 时,boolean addLast 为无效参数
|
||||||
|
private static synchronized void addFieldGetter(FieldGetter fieldGetter, Integer index, boolean addLast) {
|
||||||
|
checkParameter(fieldGetter);
|
||||||
|
|
||||||
|
LinkedList<FieldGetter> ret = getCurrentFieldGetters();
|
||||||
|
if (index != null) {
|
||||||
|
ret.add(index, fieldGetter);
|
||||||
} else {
|
} else {
|
||||||
// 对于不存在的 Field,只进行一次获取操作,主要为了支持 null safe,未来需要考虑内存泄漏风险
|
if (addLast) {
|
||||||
fieldCache.put(key, Boolean.FALSE);
|
ret.addLast(fieldGetter);
|
||||||
|
} else {
|
||||||
|
ret.addFirst(fieldGetter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return field instanceof Field ? (Field)field : null;
|
getters = ret.toArray(new FieldGetter[ret.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Field doGetField(Class<?> targetClass, String fieldName) {
|
private static LinkedList<FieldGetter> getCurrentFieldGetters() {
|
||||||
Field[] fs = targetClass.getFields();
|
LinkedList<FieldGetter> ret = new LinkedList<FieldGetter>();
|
||||||
for (Field f : fs) {
|
for (FieldGetter fieldGetter : getters) {
|
||||||
if (f.getName().equals(fieldName)) {
|
ret.add(fieldGetter);
|
||||||
return f;
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkParameter(FieldGetter fieldGetter) {
|
||||||
|
if (fieldGetter == null) {
|
||||||
|
throw new IllegalArgumentException("The parameter fieldGetter can not be null");
|
||||||
|
}
|
||||||
|
for (FieldGetter fg : getters) {
|
||||||
|
if (fg.getClass() == fieldGetter.getClass()) {
|
||||||
|
throw new RuntimeException("FieldGetter already exists : " + fieldGetter.getClass().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
}
|
||||||
|
|
||||||
|
public static synchronized void removeFieldGetter(Class<? extends FieldGetter> fieldGetterClass) {
|
||||||
|
LinkedList<FieldGetter> ret = getCurrentFieldGetters();
|
||||||
|
|
||||||
|
for (Iterator<FieldGetter> it = ret.iterator(); it.hasNext();) {
|
||||||
|
if (it.next().getClass() == fieldGetterClass) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getters = ret.toArray(new FieldGetter[ret.size()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,3 +139,6 @@ public class FieldKit {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ import com.jfinal.template.stat.Scope;
|
|||||||
* 如果在未来通过结合 #dynamic(boolean) 指令来优化,需要在 Ctrl 中引入一个
|
* 如果在未来通过结合 #dynamic(boolean) 指令来优化,需要在 Ctrl 中引入一个
|
||||||
* boolean dynamic = false 变量,而不能在 Env、Scope 引入该变量
|
* boolean dynamic = false 变量,而不能在 Env、Scope 引入该变量
|
||||||
*
|
*
|
||||||
* 还需要引入一个 NullMethodInfo 以及 isNull() 方法,此优化复杂度提高不少,
|
* 还需要引入一个 NullMethodInfo 以及 notNull() 方法,此优化复杂度提高不少,
|
||||||
* 暂时不做此优化
|
* 暂时不做此优化
|
||||||
*/
|
*/
|
||||||
public class Method extends Expr {
|
public class Method extends Expr {
|
||||||
@ -76,28 +76,26 @@ public class Method extends Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Object[] argValues = exprList.evalExprList(scope);
|
Object[] argValues = exprList.evalExprList(scope);
|
||||||
MethodInfo methodInfo;
|
|
||||||
try {
|
try {
|
||||||
methodInfo = MethodKit.getMethod(target.getClass(), methodName, argValues);
|
|
||||||
} catch (Exception e) {
|
MethodInfo methodInfo = MethodKit.getMethod(target.getClass(), methodName, argValues);
|
||||||
throw new TemplateException(e.getMessage(), location, e);
|
if (methodInfo != null) {
|
||||||
|
return methodInfo.invoke(target, argValues);
|
||||||
}
|
}
|
||||||
if (methodInfo == null) {
|
|
||||||
if (scope.getCtrl().isNullSafe()) {
|
if (scope.getCtrl().isNullSafe()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
throw new TemplateException(buildMethodNotFoundSignature("public method not found: " + target.getClass().getName() + ".", methodName, argValues), location);
|
throw new TemplateException(buildMethodNotFoundSignature("public method not found: " + target.getClass().getName() + ".", methodName, argValues), location);
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
} catch (TemplateException e) {
|
||||||
return methodInfo.invoke(target, argValues);
|
throw e;
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw e;
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
Throwable t = e.getTargetException();
|
Throwable t = e.getTargetException();
|
||||||
if (t != null) {
|
if (t == null) {t = e;}
|
||||||
throw new TemplateException(t.getMessage(), location, t);
|
throw new TemplateException(t.getMessage(), location, t);
|
||||||
} else {
|
|
||||||
throw new TemplateException(e.getMessage(), location, e);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new TemplateException(e.getMessage(), location, e);
|
throw new TemplateException(e.getMessage(), location, e);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
package com.jfinal.template.expr.ast;
|
package com.jfinal.template.expr.ast;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ public class MethodInfo {
|
|||||||
this.paraTypes = method.getParameterTypes();
|
this.paraTypes = method.getParameterTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object invoke(Object target, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
public Object invoke(Object target, Object... args) throws ReflectiveOperationException {
|
||||||
if (isVarArgs) {
|
if (isVarArgs) {
|
||||||
return invokeVarArgsMethod(target, args);
|
return invokeVarArgsMethod(target, args);
|
||||||
} else {
|
} else {
|
||||||
@ -49,7 +48,7 @@ public class MethodInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Object invokeVarArgsMethod(Object target, Object[] argValues) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
protected Object invokeVarArgsMethod(Object target, Object[] argValues) throws ReflectiveOperationException {
|
||||||
Object[] finalArgValues = new Object[paraTypes.length];
|
Object[] finalArgValues = new Object[paraTypes.length];
|
||||||
|
|
||||||
int fixedParaLength = paraTypes.length - 1;
|
int fixedParaLength = paraTypes.length - 1;
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package com.jfinal.template.expr.ast;
|
package com.jfinal.template.expr.ast;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,7 +36,7 @@ public class MethodInfoExt extends MethodInfo {
|
|||||||
// this.paraTypes = newParaTypes;
|
// this.paraTypes = newParaTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object invoke(Object target, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
public Object invoke(Object target, Object... args) throws ReflectiveOperationException {
|
||||||
Object[] finalArgs = new Object[args.length + 1];
|
Object[] finalArgs = new Object[args.length + 1];
|
||||||
finalArgs[0] = target;
|
finalArgs[0] = target;
|
||||||
|
|
||||||
|
@ -43,17 +43,20 @@ public abstract class MethodKeyBuilder {
|
|||||||
* 如果希望将 configEngine(Engine me) 中的 Engine 切换到 StrictMethodKeyBuilder,
|
* 如果希望将 configEngine(Engine me) 中的 Engine 切换到 StrictMethodKeyBuilder,
|
||||||
* 需要在 YourJFinalConfig extends JFinalConfig 中利用如下代码块才能生效:
|
* 需要在 YourJFinalConfig extends JFinalConfig 中利用如下代码块才能生效:
|
||||||
* static {
|
* static {
|
||||||
* MethodKeyBuilder.useStrictMethodKeyBuilder();
|
* MethodKeyBuilder.setToStrictMethodKeyBuilder();
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* 原因是在 com.jfinal.core.Config 中 new Engine() 时 useStrictMethodKeyBuilder()
|
* 原因是在 com.jfinal.core.Config 中 new Engine() 时 setToStrictMethodKeyBuilder()
|
||||||
* 方法并未生效,所以 extension method 生成 method key 时仍然使用的是 FastMethodKeyBuilder
|
* 方法并未生效,所以 extension method 生成 method key 时仍然使用的是 FastMethodKeyBuilder
|
||||||
* 以至于在运行时,使用 StrictMethodKeyBuilder 生成的 key 找不到 extension method
|
* 以至于在运行时,使用 StrictMethodKeyBuilder 生成的 key 找不到 extension method
|
||||||
*
|
*
|
||||||
|
* 后续版本考虑在调用 setToStrictMethodKeyBuilder() 以后重新初始化一下 MethodKit 中的变量
|
||||||
|
* 原先的 static 初始化方式重构出 synchronized void init() 方法来方便调用
|
||||||
|
*
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public static void useStrictMethodKeyBuilder() {
|
public static void setToStrictMethodKeyBuilder() {
|
||||||
MethodKeyBuilder.instance = new StrictMethodKeyBuilder();
|
instance = new StrictMethodKeyBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,7 +66,7 @@ public abstract class MethodKeyBuilder {
|
|||||||
if (methodKeyBuilder == null) {
|
if (methodKeyBuilder == null) {
|
||||||
throw new IllegalArgumentException("methodKeyBuilder can not be null");
|
throw new IllegalArgumentException("methodKeyBuilder can not be null");
|
||||||
}
|
}
|
||||||
MethodKeyBuilder.instance = methodKeyBuilder;
|
instance = methodKeyBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,7 +41,7 @@ public class MethodKit {
|
|||||||
private static final Set<String> forbiddenMethods = new HashSet<String>(64);
|
private static final Set<String> forbiddenMethods = new HashSet<String>(64);
|
||||||
private static final Set<Class<?>> forbiddenClasses = new HashSet<Class<?>>(64);
|
private static final Set<Class<?>> forbiddenClasses = new HashSet<Class<?>>(64);
|
||||||
private static final Map<Class<?>, Class<?>> primitiveMap = new HashMap<Class<?>, Class<?>>(64);
|
private static final Map<Class<?>, Class<?>> primitiveMap = new HashMap<Class<?>, Class<?>>(64);
|
||||||
private static final Map<Long, Object> methodCache = new SyncWriteMap<Long, Object>(2048, 0.25F);
|
private static final SyncWriteMap<Long, Object> methodCache = new SyncWriteMap<Long, Object>(2048, 0.25F);
|
||||||
|
|
||||||
// 初始化在模板中调用 method 时所在的被禁止使用类
|
// 初始化在模板中调用 method 时所在的被禁止使用类
|
||||||
static {
|
static {
|
||||||
@ -94,6 +94,10 @@ public class MethodKit {
|
|||||||
return forbiddenClasses.contains(clazz);
|
return forbiddenClasses.contains(clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void addForbiddenClass(Class<?> clazz) {
|
||||||
|
forbiddenClasses.add(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isForbiddenMethod(String methodName) {
|
public static boolean isForbiddenMethod(String methodName) {
|
||||||
return forbiddenMethods.contains(methodName);
|
return forbiddenMethods.contains(methodName);
|
||||||
}
|
}
|
||||||
@ -109,10 +113,10 @@ public class MethodKit {
|
|||||||
if (method == null) {
|
if (method == null) {
|
||||||
method = doGetMethod(key, targetClass, methodName, argTypes);
|
method = doGetMethod(key, targetClass, methodName, argTypes);
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
methodCache.put(key, method);
|
methodCache.putIfAbsent(key, method);
|
||||||
} else {
|
} else {
|
||||||
// 对于不存在的 Method,只进行一次获取操作,主要为了支持 null safe,未来需要考虑内存泄漏风险
|
// 对于不存在的 Method,只进行一次获取操作,主要为了支持 null safe,未来需要考虑内存泄漏风险
|
||||||
methodCache.put(key, Boolean.FALSE);
|
methodCache.putIfAbsent(key, Void.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return method instanceof MethodInfo ? (MethodInfo)method : null;
|
return method instanceof MethodInfo ? (MethodInfo)method : null;
|
||||||
@ -121,19 +125,19 @@ public class MethodKit {
|
|||||||
/**
|
/**
|
||||||
* 获取 getter 方法
|
* 获取 getter 方法
|
||||||
* 使用与 Field 相同的 key,避免生成两次 key值
|
* 使用与 Field 相同的 key,避免生成两次 key值
|
||||||
*/
|
* ---> jfinal 3.5 已将此功能转移至 FieldKit
|
||||||
public static MethodInfo getGetterMethod(Long key, Class<?> targetClass, String methodName) {
|
public static MethodInfo getGetterMethod(Long key, Class<?> targetClass, String methodName) {
|
||||||
Object getterMethod = methodCache.get(key);
|
Object getterMethod = methodCache.get(key);
|
||||||
if (getterMethod == null) {
|
if (getterMethod == null) {
|
||||||
getterMethod = doGetMethod(key, targetClass, methodName, NULL_ARG_TYPES);
|
getterMethod = doGetMethod(key, targetClass, methodName, NULL_ARG_TYPES);
|
||||||
if (getterMethod != null) {
|
if (getterMethod != null) {
|
||||||
methodCache.put(key, getterMethod);
|
methodCache.putIfAbsent(key, getterMethod);
|
||||||
} else {
|
} else {
|
||||||
methodCache.put(key, Boolean.FALSE);
|
methodCache.putIfAbsent(key, Void.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getterMethod instanceof MethodInfo ? (MethodInfo)getterMethod : null;
|
return getterMethod instanceof MethodInfo ? (MethodInfo)getterMethod : null;
|
||||||
}
|
} */
|
||||||
|
|
||||||
static Class<?>[] getArgTypes(Object[] argValues) {
|
static Class<?>[] getArgTypes(Object[] argValues) {
|
||||||
if (argValues == null || argValues.length == 0) {
|
if (argValues == null || argValues.length == 0) {
|
||||||
@ -280,7 +284,7 @@ public class MethodKit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MethodInfoExt mie = new MethodInfoExt(objectOfExtensionClass, key, extensionClass/* targetClass */, method);
|
MethodInfoExt mie = new MethodInfoExt(objectOfExtensionClass, key, extensionClass/* targetClass */, method);
|
||||||
methodCache.put(key, mie);
|
methodCache.putIfAbsent(key, mie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,14 @@ import com.jfinal.template.stat.Scope;
|
|||||||
* SharedMethod
|
* SharedMethod
|
||||||
*
|
*
|
||||||
* 用法:
|
* 用法:
|
||||||
* engine.addSharedMethod(object);
|
* engine.addSharedMethod(new StrKit());
|
||||||
* engine.addSharedStaticMethod(Xxx.class);
|
* engine.addSharedStaticMethod(MyKit.class);
|
||||||
* #(method(para))
|
*
|
||||||
|
* #if (notBlank(para))
|
||||||
|
* ....
|
||||||
|
* #end
|
||||||
|
*
|
||||||
|
* 上面代码中的 notBlank 方法来自 StrKit
|
||||||
*/
|
*/
|
||||||
public class SharedMethod extends Expr {
|
public class SharedMethod extends Expr {
|
||||||
|
|
||||||
@ -48,14 +53,20 @@ public class SharedMethod extends Expr {
|
|||||||
|
|
||||||
public Object eval(Scope scope) {
|
public Object eval(Scope scope) {
|
||||||
Object[] argValues = exprList.evalExprList(scope);
|
Object[] argValues = exprList.evalExprList(scope);
|
||||||
SharedMethodInfo sharedMethodInfo = sharedMethodKit.getSharedMethodInfo(methodName, argValues);
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
SharedMethodInfo sharedMethodInfo = sharedMethodKit.getSharedMethodInfo(methodName, argValues);
|
||||||
|
if (sharedMethodInfo != null) {
|
||||||
|
return sharedMethodInfo.invoke(argValues);
|
||||||
|
} else {
|
||||||
// ShareMethod 相当于是固定的静态的方法,不支持 null safe,null safe 只支持具有动态特征的用法
|
// ShareMethod 相当于是固定的静态的方法,不支持 null safe,null safe 只支持具有动态特征的用法
|
||||||
if (sharedMethodInfo == null) {
|
|
||||||
throw new TemplateException(Method.buildMethodNotFoundSignature("Shared method not found: ", methodName, argValues), location);
|
throw new TemplateException(Method.buildMethodNotFoundSignature("Shared method not found: ", methodName, argValues), location);
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
return sharedMethodInfo.invoke(argValues);
|
} catch (TemplateException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw e;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new TemplateException(e.getMessage(), location, e);
|
throw new TemplateException(e.getMessage(), location, e);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import com.jfinal.kit.HashKit;
|
import com.jfinal.kit.HashKit;
|
||||||
@ -54,9 +53,9 @@ public class SharedMethodKit {
|
|||||||
if (method == null) {
|
if (method == null) {
|
||||||
method = doGetSharedMethodInfo(methodName, argTypes);
|
method = doGetSharedMethodInfo(methodName, argTypes);
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
methodCache.put(key, method);
|
methodCache.putIfAbsent(key, method);
|
||||||
}
|
}
|
||||||
// shared method 不支持 null safe,不缓存: methodCache.put(key, Boolean.FALSE)
|
// shared method 不支持 null safe,不缓存: methodCache.putIfAbsent(key, Void.class)
|
||||||
}
|
}
|
||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
@ -174,7 +173,7 @@ public class SharedMethodKit {
|
|||||||
this.target = target;
|
this.target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object invoke(Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
public Object invoke(Object... args) throws ReflectiveOperationException {
|
||||||
return super.invoke(target, args);
|
return super.invoke(target, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,23 +57,25 @@ public class StaticMethod extends Expr {
|
|||||||
|
|
||||||
public Object eval(Scope scope) {
|
public Object eval(Scope scope) {
|
||||||
Object[] argValues = exprList.evalExprList(scope);
|
Object[] argValues = exprList.evalExprList(scope);
|
||||||
MethodInfo methodInfo;
|
|
||||||
try {
|
|
||||||
methodInfo = MethodKit.getMethod(clazz, methodName, argValues);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new TemplateException(e.getMessage(), location, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// StaticMethod 是固定的存在,不支持 null safe,null safe 只支持具有动态特征的用法
|
try {
|
||||||
if (methodInfo == null) {
|
MethodInfo methodInfo = MethodKit.getMethod(clazz, methodName, argValues);
|
||||||
throw new TemplateException(Method.buildMethodNotFoundSignature("public static method not found: " + clazz.getName() + "::", methodName, argValues), location);
|
|
||||||
}
|
if (methodInfo != null) {
|
||||||
if (!methodInfo.isStatic()) {
|
if (methodInfo.isStatic()) {
|
||||||
|
return methodInfo.invoke(null, argValues);
|
||||||
|
} else {
|
||||||
throw new TemplateException(Method.buildMethodNotFoundSignature("Not public static method: " + clazz.getName() + "::", methodName, argValues), location);
|
throw new TemplateException(Method.buildMethodNotFoundSignature("Not public static method: " + clazz.getName() + "::", methodName, argValues), location);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// StaticMethod 是固定的存在,不支持 null safe,null safe 只支持具有动态特征的用法
|
||||||
|
throw new TemplateException(Method.buildMethodNotFoundSignature("public static method not found: " + clazz.getName() + "::", methodName, argValues), location);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
} catch (TemplateException e) {
|
||||||
return methodInfo.invoke(null, argValues);
|
throw e;
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw e;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new TemplateException(e.getMessage(), location, e);
|
throw new TemplateException(e.getMessage(), location, e);
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,8 @@ public class RenderDirective extends Directive {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
subStat.stat.exec(subStat.env, scope, writer);
|
subStat.exec(null, scope, writer); // subStat.stat.exec(subStat.env, scope, writer);
|
||||||
|
|
||||||
scope.getCtrl().setJumpNone();
|
scope.getCtrl().setJumpNone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,16 +139,21 @@ public class RenderDirective extends Directive {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SubStat {
|
public static class SubStat extends Stat {
|
||||||
SubEnv env;
|
public SubEnv env;
|
||||||
Stat stat;
|
public Stat stat;
|
||||||
ISource source;
|
public ISource source;
|
||||||
|
|
||||||
SubStat(SubEnv env, Stat stat, ISource source) {
|
public SubStat(SubEnv env, Stat stat, ISource source) {
|
||||||
this.env = env;
|
this.env = env;
|
||||||
this.stat = stat;
|
this.stat = stat;
|
||||||
this.source = source;
|
this.source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exec(Env env, Scope scope, Writer writer) {
|
||||||
|
stat.exec(this.env, scope, writer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -160,8 +166,8 @@ public class RenderDirective extends Directive {
|
|||||||
*
|
*
|
||||||
* 注意: #render 子模板中定义的模板函数无法在父模板中调用
|
* 注意: #render 子模板中定义的模板函数无法在父模板中调用
|
||||||
*/
|
*/
|
||||||
private static class SubEnv extends Env {
|
public static class SubEnv extends Env {
|
||||||
Env parentEnv;
|
public Env parentEnv;
|
||||||
|
|
||||||
public SubEnv(Env parentEnv) {
|
public SubEnv(Env parentEnv) {
|
||||||
super(parentEnv.getEngineConfig());
|
super(parentEnv.getEngineConfig());
|
||||||
@ -171,6 +177,7 @@ public class RenderDirective extends Directive {
|
|||||||
/**
|
/**
|
||||||
* 接管父类 getFunction(),先从子模板中找模板函数,找不到再去父模板中找
|
* 接管父类 getFunction(),先从子模板中找模板函数,找不到再去父模板中找
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public Define getFunction(String functionName) {
|
public Define getFunction(String functionName) {
|
||||||
Define func = functionMap.get(functionName);
|
Define func = functionMap.get(functionName);
|
||||||
return func != null ? func : parentEnv.getFunction(functionName);
|
return func != null ? func : parentEnv.getFunction(functionName);
|
||||||
|
@ -105,7 +105,7 @@ public class ClassPathSource implements ISource {
|
|||||||
return finalFileName;
|
return finalFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKey() {
|
public String getCacheKey() {
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ public class FileSource implements ISource {
|
|||||||
return lastModified != new File(finalFileName).lastModified();
|
return lastModified != new File(finalFileName).lastModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKey() {
|
public String getCacheKey() {
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,12 +27,12 @@ public interface ISource {
|
|||||||
boolean isModified();
|
boolean isModified();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* key used to cache, return null if do not cache the template
|
* cache key used to cache, return null if do not cache the template
|
||||||
*
|
*
|
||||||
* 注意:如果不希望缓存从该 ISource 解析出来的 Template 对象
|
* 注意:如果不希望缓存从该 ISource 解析出来的 Template 对象
|
||||||
* 让 getKey() 返回 null 值即可
|
* 让 getCacheKey() 返回 null 值即可
|
||||||
*/
|
*/
|
||||||
String getKey();
|
String getCacheKey();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* content of ISource
|
* content of ISource
|
||||||
|
@ -25,7 +25,7 @@ import com.jfinal.template.EngineConfig;
|
|||||||
*/
|
*/
|
||||||
public class StringSource implements ISource {
|
public class StringSource implements ISource {
|
||||||
|
|
||||||
private String key;
|
private String cacheKey;
|
||||||
private StringBuilder content;
|
private StringBuilder content;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +38,7 @@ public class StringSource implements ISource {
|
|||||||
throw new IllegalArgumentException("content can not be blank");
|
throw new IllegalArgumentException("content can not be blank");
|
||||||
}
|
}
|
||||||
this.content = new StringBuilder(content);
|
this.content = new StringBuilder(content);
|
||||||
this.key = cache ? HashKit.md5(content) : null; // 不缓存只要将 key 值赋为 null 即可
|
this.cacheKey = cache ? HashKit.md5(content) : null; // 不缓存只要将 cacheKey 值赋为 null 即可
|
||||||
}
|
}
|
||||||
|
|
||||||
public StringSource(StringBuilder content, boolean cache) {
|
public StringSource(StringBuilder content, boolean cache) {
|
||||||
@ -46,15 +46,15 @@ public class StringSource implements ISource {
|
|||||||
throw new IllegalArgumentException("content can not be blank");
|
throw new IllegalArgumentException("content can not be blank");
|
||||||
}
|
}
|
||||||
this.content = content;
|
this.content = content;
|
||||||
this.key = cache ? HashKit.md5(content.toString()) : null; // 不缓存只要将 key 值赋为 null 即可
|
this.cacheKey = cache ? HashKit.md5(content.toString()) : null; // 不缓存只要将 cacheKey 值赋为 null 即可
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isModified() {
|
public boolean isModified() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKey() {
|
public String getCacheKey() {
|
||||||
return key;
|
return cacheKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StringBuilder getContent() {
|
public StringBuilder getContent() {
|
||||||
@ -67,8 +67,8 @@ public class StringSource implements ISource {
|
|||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Key : ").append(key).append("\n");
|
sb.append("cacheKey : ").append(cacheKey).append("\n");
|
||||||
sb.append("Content : ").append(content).append("\n");
|
sb.append("content : ").append(content).append("\n");
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ public class Parser {
|
|||||||
tokenList.add(EOF);
|
tokenList.add(EOF);
|
||||||
StatList statList = statList();
|
StatList statList = statList();
|
||||||
if (peek() != EOF) {
|
if (peek() != EOF) {
|
||||||
throw new ParseException("Syntax error: can not match " + peek().value(), getLocation(peek().row));
|
throw new ParseException("Syntax error: can not match \"#" + peek().value() + "\"", getLocation(peek().row));
|
||||||
}
|
}
|
||||||
return statList;
|
return statList;
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,8 @@ public class Output extends Stat {
|
|||||||
}
|
}
|
||||||
} catch(TemplateException e) {
|
} catch(TemplateException e) {
|
||||||
throw e;
|
throw e;
|
||||||
|
} catch(ParseException e) {
|
||||||
|
throw e;
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
throw new TemplateException(e.getMessage(), location, e);
|
throw new TemplateException(e.getMessage(), location, e);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import com.jfinal.template.stat.ParseException;
|
|||||||
import com.jfinal.template.stat.Scope;
|
import com.jfinal.template.stat.Scope;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SetLocal 设置全局变量,全局作用域是指本次请求的整个 template
|
* SetGlobal 设置全局变量,全局作用域是指本次请求的整个 template
|
||||||
*
|
*
|
||||||
* 适用于极少数的在内层作用域中希望直接操作顶层作用域的场景
|
* 适用于极少数的在内层作用域中希望直接操作顶层作用域的场景
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user