enjoy 4.3 release ^_^

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

2
.gitignore vendored
View File

@ -52,3 +52,5 @@ a_little_config_pro.txt
dev_plan.txt

View File

@ -192,5 +192,3 @@ third-party archives.

View File

@ -8,7 +8,7 @@ Enjoy 是基于 Java 语言的极轻量极魔板引擎。极轻量级仅 227 KB
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>enjoy</artifactId>
<version>4.2</version>
<version>4.3</version>
</dependency>
```

View File

@ -128,3 +128,5 @@
</plugins>
</build>
</project>

View File

@ -0,0 +1,100 @@
/**
* 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.proxy;
import java.util.Map;
/**
* ProxyClass
*/
public class ProxyClass {
// 被代理的目标
private Class<?> target;
/**
* 以下是代理类信息
*/
private String pkg; // 包名
private String name; // 类名
private String sourceCode; // 源代码
private Map<String, byte[]> byteCode; // 字节码
private Class<?> clazz; // 字节码被 loadClass 后的 Class
// private List<ProxyMethod> proxyMethodList = new ArrayList<>();
public ProxyClass(Class<?> target) {
this.target = target;
this.pkg = target.getPackage().getName();
this.name = target.getSimpleName() + "$$EnhancerByJFinal";
}
/**
* 是否需要代理
*/
// public boolean needProxy() {
// return proxyMethodList.size() > 0;
// }
public Class<?> getTarget() {
return target;
}
public String getPkg() {
return pkg;
}
public String getName() {
return name;
}
public String getSourceCode() {
return sourceCode;
}
public void setSourceCode(String sourceCode) {
this.sourceCode = sourceCode;
}
public Map<String, byte[]> getByteCode() {
return byteCode;
}
public void setByteCode(Map<String, byte[]> byteCode) {
this.byteCode = byteCode;
}
public Class<?> getClazz() {
return clazz;
}
public void setClazz(Class<?> clazz) {
this.clazz = clazz;
}
// public void addProxyMethod(ProxyMethod proxyMethod) {
// proxyMethodList.add(proxyMethod);
// }
// public List<ProxyMethod> getProxyMethodList() {
// return proxyMethodList;
// }
}

View File

@ -0,0 +1,70 @@
/**
* 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.proxy;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
/**
* ProxyClassLoader
*/
public class ProxyClassLoader extends ClassLoader {
protected Map<String, byte[]> byteCodeMap = new ConcurrentHashMap<>();
static {
registerAsParallelCapable();
}
public ProxyClassLoader() {
super(getParentClassLoader());
}
protected static ClassLoader getParentClassLoader() {
ClassLoader ret = Thread.currentThread().getContextClassLoader();
return ret != null ? ret : ProxyClassLoader.class.getClassLoader();
}
public Class<?> loadProxyClass(ProxyClass proxyClass) {
for (Entry<String, byte[]> e : proxyClass.getByteCode().entrySet()) {
byteCodeMap.putIfAbsent(e.getKey(), e.getValue());
}
try {
return loadClass(proxyClass.getPkg() + "." + proxyClass.getName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] bytes = byteCodeMap.get(name);
if (bytes != null) {
Class<?> ret = defineClass(name, bytes, 0, bytes.length);
byteCodeMap.remove(name);
return ret;
}
return super.findClass(name);
}
}

View File

@ -0,0 +1,238 @@
/**
* 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.proxy;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import javax.tools.DiagnosticCollector;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
// import com.jfinal.log.Log;
/**
* ProxyCompiler
*
* https://www.programcreek.com/java-api-examples/?api=javax.tools.JavaCompiler
*/
public class ProxyCompiler {
// protected static final Log log = Log.getLog(ProxyCompiler.class);
// protected List<String> options = Arrays.asList("-target", "1.8" /*, "-parameters"*/);
protected volatile List<String> options = null;
protected List<String> getOptions() {
if (options != null) {
return options;
}
synchronized (this) {
if (options != null) {
return options;
}
List<String> ret = new ArrayList<>();
ret.add("-target");
ret.add("1.8");
String cp = getClassPath();
if (cp != null && cp.trim().length() != 0) {
ret.add("-classpath");
ret.add(cp);
}
options = ret;
return options;
}
}
/**
* 兼容 tomcat 丢失 class path否则无法编译
*/
protected String getClassPath() {
URLClassLoader classLoader = getURLClassLoader();
if (classLoader == null) {
return null;
}
int index = 0;
boolean isWindows = isWindows();
StringBuilder ret = new StringBuilder();
for (URL url : classLoader.getURLs()) {
if (index++ > 0) {
ret.append(File.pathSeparator);
}
String path = url.getFile();
// 如果是 windows 系统去除前缀字符 '/'
if (isWindows && path.startsWith("/")) {
path = path.substring(1);
}
// 去除后缀字符 '/'
if (path.length() > 1 && (path.endsWith("/") || path.endsWith(File.separator))) {
path = path.substring(0, path.length() - 1);
}
ret.append(path);
}
return ret.toString();
}
protected boolean isWindows() {
String osName = System.getProperty("os.name", "unknown");
return osName.toLowerCase().indexOf("windows") != -1;
}
protected URLClassLoader getURLClassLoader() {
ClassLoader ret = Thread.currentThread().getContextClassLoader();
if (ret == null) {
ret = ProxyCompiler.class.getClassLoader();
}
return (ret instanceof URLClassLoader) ? (URLClassLoader)ret : null;
}
public void compile(ProxyClass proxyClass) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null) {
throw new RuntimeException("Can not get javax.tools.JavaCompiler, check whether \"tools.jar\" is in the environment variable CLASSPATH");
}
DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
try (MyJavaFileManager javaFileManager = new MyJavaFileManager(compiler.getStandardFileManager(collector, null, null))) {
MyJavaFileObject javaFileObject = new MyJavaFileObject(proxyClass.getName(), proxyClass.getSourceCode());
Boolean result = compiler.getTask(null, javaFileManager, collector, getOptions(), null, Arrays.asList(javaFileObject)).call();
outputCompileError(result, collector);
Map<String, byte[]> ret = new HashMap<>();
for (Entry<String, MyJavaFileObject> e : javaFileManager.fileObjects.entrySet()) {
ret.put(e.getKey(), e.getValue().getByteCode());
}
proxyClass.setByteCode(ret);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
protected void outputCompileError(Boolean result, DiagnosticCollector<JavaFileObject> collector) {
if (! result) {
// collector.getDiagnostics().forEach(item -> log.error(item.toString()));
collector.getDiagnostics().forEach(item -> System.out.println(item.toString()));
}
}
public ProxyCompiler setCompileOptions(List<String> options) {
Objects.requireNonNull(options, "options can not be null");
this.options = options;
return this;
}
public ProxyCompiler addCompileOption(String option) {
Objects.requireNonNull(option, "option can not be null");
options.add(option);
return this;
}
public static class MyJavaFileObject extends SimpleJavaFileObject {
private String source;
private ByteArrayOutputStream outPutStream;
public MyJavaFileObject(String name, String source) {
super(URI.create("String:///" + name + Kind.SOURCE.extension), Kind.SOURCE);
this.source = source;
}
public MyJavaFileObject(String name, Kind kind) {
super(URI.create("String:///" + name + kind.extension), kind);
source = null;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
if (source == null) {
throw new IllegalStateException("source field can not be null");
}
return source;
}
@Override
public OutputStream openOutputStream() throws IOException {
outPutStream = new ByteArrayOutputStream();
return outPutStream;
}
public byte[] getByteCode() {
return outPutStream.toByteArray();
}
}
public static class MyJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
public Map<String, MyJavaFileObject> fileObjects = new HashMap<>();
public MyJavaFileManager(JavaFileManager fileManager) {
super(fileManager);
}
@Override
public JavaFileObject getJavaFileForOutput(Location location, String qualifiedClassName, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
MyJavaFileObject javaFileObject = new MyJavaFileObject(qualifiedClassName, kind);
fileObjects.put(qualifiedClassName, javaFileObject);
return javaFileObject;
}
// 是否在编译时依赖另一个类的情况下用到本方法 ?
@Override
public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {
JavaFileObject javaFileObject = fileObjects.get(className);
if (javaFileObject == null) {
javaFileObject = super.getJavaFileForInput(location, className, kind);
}
return javaFileObject;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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