enjoy 3.5

This commit is contained in:
James 2018-08-12 12:15:01 +08:00
parent 3e89651aa4
commit 1ce7068072
16 changed files with 183 additions and 95 deletions

View File

@ -0,0 +1,92 @@
/**
* 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.kit;
import java.util.HashMap;
import java.util.Map;
/**
* SyncWriteMap 同步写 HashMap
* 创建原因是 HashMap扩容时遇到并发修改可能造成 100% CPU 占用
*
* SyncWriteMap 拥有 HashMap 的性能但不保障并发访问的线程安全
* 只用于读多写少且不用保障线程安全的场景
*
* 例如 MethodKit 中用于缓存 MethodInfo cache被写入的数据
* 不用保障是单例读取之后会做 null 值判断
*
* ActionMapping 中的 HashMap 是系统启动时在独立线程内初始化的
* 不存在并发写只存在并发读的情况所以仍然可以使用 HashMap
*/
public class SyncWriteMap<K, V> extends HashMap<K, V> {
private static final long serialVersionUID = -7287230891751869148L;
public SyncWriteMap() {
}
public SyncWriteMap(int initialCapacity) {
super(initialCapacity);
}
public SyncWriteMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
public SyncWriteMap(Map<? extends K, ? extends V> m) {
super(m);
}
@Override
public V put(K key, V value) {
synchronized (this) {
return super.put(key, value);
}
}
@Override
public V putIfAbsent(K key, V value) {
synchronized (this) {
return super.putIfAbsent(key, value);
}
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
synchronized (this) {
super.putAll(m);
}
}
@Override
public V remove(Object key) {
synchronized (this) {
return super.remove(key);
}
}
@Override
public void clear() {
synchronized (this) {
super.clear();
}
}
}

View File

@ -21,11 +21,13 @@ import java.util.HashMap;
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.MethodKit;
import com.jfinal.template.source.ClassPathSourceFactory;
import com.jfinal.template.source.ISource;
import com.jfinal.template.source.ISourceFactory;
import com.jfinal.template.source.StringSource;
import com.jfinal.template.stat.OutputDirectiveFactory;
import com.jfinal.template.stat.Parser;
import com.jfinal.template.stat.ast.Stat;
@ -41,7 +43,7 @@ public class Engine {
public static final String MAIN_ENGINE_NAME = "main";
private static Engine MAIN_ENGINE;
private static Map<String, Engine> engineMap = new HashMap<String, Engine>();
private static Map<String, Engine> engineMap = new HashMap<String, Engine>(64, 0.5F);
// Create main engine
static {
@ -54,7 +56,7 @@ public class Engine {
private EngineConfig config = new EngineConfig();
private ISourceFactory sourceFactory = config.getSourceFactory();
private Map<String, Template> templateCache = new HashMap<String, Template>();
private Map<String, Template> templateCache = new SyncWriteMap<String, Template>(2048, 0.5F);
/**
* Create engine without management of JFinal
@ -276,7 +278,7 @@ public class Engine {
/**
* Set output directive factory
*/
public Engine setOutputDirectiveFactory(IOutputDirectiveFactory outputDirectiveFactory) {
public Engine setOutputDirectiveFactory(OutputDirectiveFactory outputDirectiveFactory) {
config.setOutputDirectiveFactory(outputDirectiveFactory);
return this;
}

View File

@ -35,6 +35,7 @@ import com.jfinal.template.source.ISource;
import com.jfinal.template.source.ISourceFactory;
import com.jfinal.template.source.StringSource;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.OutputDirectiveFactory;
import com.jfinal.template.stat.Parser;
import com.jfinal.template.stat.ast.Define;
import com.jfinal.template.stat.ast.Output;
@ -48,14 +49,14 @@ public class EngineConfig {
WriterBuffer writerBuffer = new WriterBuffer();
private Map<String, Define> sharedFunctionMap = new HashMap<String, Define>();
private Map<String, Define> sharedFunctionMap = createSharedFunctionMap(); // new HashMap<String, Define>(512, 0.25F);
private List<ISource> sharedFunctionSourceList = new ArrayList<ISource>(); // for devMode only
Map<String, Object> sharedObjectMap = null;
private IOutputDirectiveFactory outputDirectiveFactory = OutputDirectiveFactory.me;
private OutputDirectiveFactory outputDirectiveFactory = OutputDirectiveFactory.me;
private ISourceFactory sourceFactory = new FileSourceFactory();
private Map<String, Class<? extends Directive>> directiveMap = new HashMap<String, Class<? extends Directive>>();
private Map<String, Class<? extends Directive>> directiveMap = new HashMap<String, Class<? extends Directive>>(64, 0.5F);
private SharedMethodKit sharedMethodKit = new SharedMethodKit();
private boolean devMode = false;
@ -179,7 +180,7 @@ public class EngineConfig {
* 开发者可直接使用模板注释功能将不需要的 function 直接注释掉
*/
private synchronized void reloadSharedFunctionSourceList() {
Map<String, Define> newMap = new HashMap<String, Define>();
Map<String, Define> newMap = createSharedFunctionMap();
for (int i = 0, size = sharedFunctionSourceList.size(); i < size; i++) {
ISource source = sharedFunctionSourceList.get(i);
String fileName = source instanceof FileSource ? ((FileSource)source).getFileName() : null;
@ -194,9 +195,13 @@ public class EngineConfig {
this.sharedFunctionMap = newMap;
}
private Map<String, Define> createSharedFunctionMap() {
return new HashMap<String, Define>(512, 0.25F);
}
public synchronized void addSharedObject(String name, Object object) {
if (sharedObjectMap == null) {
sharedObjectMap = new HashMap<String, Object>();
sharedObjectMap = new HashMap<String, Object>(64, 0.25F);
} else if (sharedObjectMap.containsKey(name)) {
throw new IllegalArgumentException("Shared object already exists: " + name);
}
@ -210,7 +215,7 @@ public class EngineConfig {
/**
* Set output directive factory
*/
public void setOutputDirectiveFactory(IOutputDirectiveFactory outputDirectiveFactory) {
public void setOutputDirectiveFactory(OutputDirectiveFactory outputDirectiveFactory) {
if (outputDirectiveFactory == null) {
throw new IllegalArgumentException("outputDirectiveFactory can not be null");
}

View File

@ -35,7 +35,7 @@ import com.jfinal.template.stat.ast.Define;
public class Env {
protected EngineConfig engineConfig;
protected Map<String, Define> functionMap = new HashMap<String, Define>();
protected Map<String, Define> functionMap = new HashMap<String, Define>(16, 0.5F);
// 代替 Template 持有该属性便于在 #include 指令中调用 Env.addSource()
protected List<ISource> sourceList = null;

View File

@ -1,34 +0,0 @@
/**
* 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;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ast.Output;
/**
* OutputDirectiveFactory
*/
public class OutputDirectiveFactory implements IOutputDirectiveFactory {
public static final OutputDirectiveFactory me = new OutputDirectiveFactory();
public Output getOutputDirective(ExprList exprList, Location location) {
return new Output(exprList, location);
}
}

View File

@ -18,13 +18,14 @@ package com.jfinal.template.expr.ast;
import java.lang.reflect.Field;
import java.util.HashMap;
import com.jfinal.kit.SyncWriteMap;
/**
* FieldKit
*/
public class FieldKit {
private static final HashMap<Long, Object> fieldCache = new HashMap<Long, Object>();
private static final HashMap<Long, Object> fieldCache = new SyncWriteMap<Long, Object>(512, 0.5F);
public static Field getField(Long key, Class<?> targetClass, String fieldName) {
Object field = fieldCache.get(key);

View File

@ -24,6 +24,17 @@ import com.jfinal.template.stat.Scope;
/**
* Method : expr '.' ID '(' exprList? ')'
*
* 每次通过 MethodKit.getMethod(...) MethodInfo 而不是用属性持有其对象
* 是为了支持 target 对象的动态类型MethodInfo 中的 Method 被调用 15 次以后
* 会被 JDK 动态生成 GeneratedAccessorXXX 字节码性能不是问题
* 唯一的性能损耗是从 HashMap 中获取 MethodInfo 对象可以忽略不计
*
* 如果在未来通过结合 #dynamic(boolean) 指令来优化需要在 Ctrl 中引入一个
* boolean dynamic = false 变量而不能在 EnvScope 引入该变量
*
* 还需要引入一个 NullMethodInfo 以及 isNull() 方法此优化复杂度提高不少
* 暂时不做此优化
*/
public class Method extends Expr {

View File

@ -23,6 +23,7 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.jfinal.kit.ReflectKit;
import com.jfinal.kit.SyncWriteMap;
import com.jfinal.template.ext.extensionmethod.ByteExt;
import com.jfinal.template.ext.extensionmethod.DoubleExt;
import com.jfinal.template.ext.extensionmethod.FloatExt;
@ -37,10 +38,10 @@ import com.jfinal.template.ext.extensionmethod.StringExt;
public class MethodKit {
private static final Class<?>[] NULL_ARG_TYPES = new Class<?>[0];
private static final Set<String> forbiddenMethods = new HashSet<String>();
private static final Set<Class<?>> forbiddenClasses = new HashSet<Class<?>>();
private static final Map<Class<?>, Class<?>> primitiveMap = new HashMap<Class<?>, Class<?>>();
private static final HashMap<Long, Object> methodCache = new HashMap<Long, Object>();
private static final Set<String> forbiddenMethods = new HashSet<String>(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<Long, Object> methodCache = new SyncWriteMap<Long, Object>(2048, 0.25F);
// 初始化在模板中调用 method 时所在的被禁止使用类
static {
@ -307,7 +308,7 @@ public class MethodKit {
}
}
private static final Map<Class<?>, Class<?>> primitiveToBoxedMap = new HashMap<Class<?>, Class<?>>();
private static final Map<Class<?>, Class<?>> primitiveToBoxedMap = new HashMap<Class<?>, Class<?>>(64);
// 初始化 primitive type boxed type 的映射
static {

View File

@ -27,6 +27,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import com.jfinal.kit.HashKit;
import com.jfinal.kit.ReflectKit;
import com.jfinal.kit.SyncWriteMap;
/**
* SharedMethodKit
@ -44,7 +45,7 @@ public class SharedMethodKit {
}
private final List<SharedMethodInfo> sharedMethodList = new ArrayList<SharedMethodInfo>();
private final HashMap<Long, SharedMethodInfo> methodCache = new HashMap<Long, SharedMethodInfo>();
private final HashMap<Long, SharedMethodInfo> methodCache = new SyncWriteMap<Long, SharedMethodInfo>(512, 0.25F);
public SharedMethodInfo getSharedMethodInfo(String methodName, Object[] argValues) {
Class<?>[] argTypes = MethodKit.getArgTypes(argValues);

View File

@ -79,23 +79,25 @@ public class DateDirective extends Directive {
private void outputWithoutDatePattern(Env env, Scope scope, Writer writer) {
Object value = valueExpr.eval(scope);
if (value != null) {
if (value instanceof Date) {
write(writer, (Date)value, env.getEngineConfig().getDatePattern());
} else if (value != null) {
throw new TemplateException("The first parameter date of #date directive must be Date type", location);
}
}
private void outputWithDatePattern(Env env, Scope scope, Writer writer) {
Object value = valueExpr.eval(scope);
if (value == null) {
return ;
}
if (value instanceof Date) {
Object datePattern = this.datePatternExpr.eval(scope);
if ( !(datePattern instanceof String) ) {
if (datePattern instanceof String) {
write(writer, (Date)value, (String)datePattern);
} else {
throw new TemplateException("The sencond parameter datePattern of #date directive must be String", location);
}
write(writer, (Date)value, (String)datePattern);
} else if (value != null) {
throw new TemplateException("The first parameter date of #date directive must be Date type", location);
}
}
private void write(Writer writer, Date date, String datePattern) {

View File

@ -52,11 +52,12 @@ public class EscapeDirective extends Directive {
case '>':
ret.append("&gt;");
break;
case '\"':
case '"':
ret.append("&quot;");
break;
case '\'':
ret.append("&apos;"); // IE 不支持 &apos; 考虑 &#39;
// ret.append("&apos;"); // IE 不支持 &apos; 考虑 &#39;
ret.append("&#39;");
break;
case '&':
ret.append("&amp;");

View File

@ -16,8 +16,8 @@
package com.jfinal.template.ext.directive;
import java.util.HashMap;
import java.util.Map;
import com.jfinal.kit.SyncWriteMap;
import com.jfinal.template.Directive;
import com.jfinal.template.EngineConfig;
import com.jfinal.template.Env;
@ -60,7 +60,7 @@ import com.jfinal.template.stat.ast.StatList;
public class RenderDirective extends Directive {
private String parentFileName;
private Map<String, StatInfo> statInfoCache = new HashMap<String,StatInfo>();
private Map<String, StatInfo> statInfoCache = new SyncWriteMap<String,StatInfo>(16, 0.5F);
public void setExprList(ExprList exprList) {
int len = exprList.length();

View File

@ -16,6 +16,7 @@
package com.jfinal.template.ext.spring;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.HashMap;
@ -56,8 +57,20 @@ public class JFinalView extends AbstractTemplateView {
}
}
try {
OutputStream os = response.getOutputStream();
JFinalViewResolver.engine.getTemplate(getUrl()).render(model, os);
} catch (Exception e) { // 捕获 ByteWriter.close() 抛出的 RuntimeException
Throwable cause = e.getCause();
if (cause instanceof IOException) { // ClientAbortExceptionEofException 直接或间接继承自 IOException
String name = cause.getClass().getSimpleName();
if ("ClientAbortException".equals(name) || "EofException".equals(name)) {
return ;
}
}
throw e;
}
}
@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})

View File

@ -17,12 +17,11 @@
package com.jfinal.template.source;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import com.jfinal.template.EngineConfig;
/**
@ -72,17 +71,12 @@ public class ClassPathSource implements ISource {
}
protected void processIsInJarAndlastModified() {
try {
URLConnection conn = url.openConnection();
if ("jar".equals(url.getProtocol()) || conn instanceof JarURLConnection) {
if ("file".equalsIgnoreCase(url.getProtocol())) {
isInJar = false;
lastModified = new File(url.getFile()).lastModified();
} else {
isInJar = true;
lastModified = -1;
} else {
isInJar = false;
lastModified = conn.getLastModified();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@ -120,12 +114,7 @@ public class ClassPathSource implements ISource {
}
protected long getLastModified() {
try {
URLConnection conn = url.openConnection();
return conn.getLastModified();
} catch (IOException e) {
throw new RuntimeException(e);
}
return new File(url.getFile()).lastModified();
}
/**

View File

@ -14,14 +14,13 @@
* limitations under the License.
*/
package com.jfinal.template;
package com.jfinal.template.stat;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ast.Output;
/**
* IOutputDirectiveFactory
* OutputDirectiveFactory
* 用于定制自定义输出指令替换系统默认输出指令满足个性化需求
*
* 用法
@ -32,12 +31,15 @@ import com.jfinal.template.stat.ast.Output;
* }
*
* public void exec(Env env, Scope scope, Writer writer) {
* write(writer, exprList.eval(scope));
* Object value = exprList.eval(scope);
* if (value != null) {
* write(writer, value.toString());
* }
* }
* }
*
* 2定义 MyOutputDirectiveFactory
* public class MyOutputDirectiveFactory implements IOutputDirectiveFactory {
* public class MyOutputDirectiveFactory extends OutputDirectiveFactory {
* public Output getOutputDirective(ExprList exprList) {
* return new MyOutput(exprList);
* }
@ -46,11 +48,13 @@ import com.jfinal.template.stat.ast.Output;
* 3配置
* engine.setOutputDirectiveFactory(new MyOutputDirectiveFactory())
*/
public interface IOutputDirectiveFactory {
public class OutputDirectiveFactory {
public Output getOutputDirective(ExprList exprList, Location location);
public static final OutputDirectiveFactory me = new OutputDirectiveFactory();
public Output getOutputDirective(ExprList exprList, Location location) {
return new Output(exprList, location);
}
}

View File

@ -58,7 +58,7 @@ enum Symbol {
* 扩展指令在得到 # id ( 序列以后才要求得到正确的后续 Token 序列否则仅仅 return fail()
*/
@SuppressWarnings("serial")
private static final Map<String, Symbol> keywords = new HashMap<String, Symbol>() {{
private static final Map<String, Symbol> keywords = new HashMap<String, Symbol>(64) {{
put(Symbol.IF.getName(), IF);
put(Symbol.ELSEIF.getName(), ELSEIF);
put(Symbol.ELSE.getName(), ELSE);