Browse Source

jfinal 4.0

James 6 years ago
parent
commit
96590e85aa

+ 28 - 0
src/main/java/com/jfinal/proxy/Callback.java

@@ -0,0 +1,28 @@
+/**
+ * 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;
+
+/**
+ * Callback
+ */
+@FunctionalInterface
+public interface Callback {
+	public Object call(Object[] args);
+}
+
+
+

+ 38 - 0
src/main/java/com/jfinal/proxy/Proxy.java

@@ -0,0 +1,38 @@
+/**
+ * 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;
+
+/**
+ * Proxy
+ */
+public class Proxy {
+	
+	static ProxyFactory proxyFactory = new ProxyFactory();
+	
+	/**
+	 * 获取代理对象
+	 * @param target 被代理的类
+	 * @return 代理对象
+	 */
+	public static <T> T get(Class<T> target) {
+		return proxyFactory.get(target);
+	}
+}
+
+
+
+

+ 102 - 0
src/main/java/com/jfinal/proxy/ProxyClass.java

@@ -0,0 +1,102 @@
+/**
+ * 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.ArrayList;
+import java.util.List;
+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;
+	}
+}
+
+
+
+
+

+ 70 - 0
src/main/java/com/jfinal/proxy/ProxyClassLoader.java

@@ -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);
+	}
+}
+
+
+
+

+ 155 - 0
src/main/java/com/jfinal/proxy/ProxyCompiler.java

@@ -0,0 +1,155 @@
+/**
+ * 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.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+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"*/);
+	
+	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, options, null, Arrays.asList(javaFileObject)).call();
+			if (! result) {
+				collector.getDiagnostics().forEach(item -> log.error(item.toString()));
+			}
+			
+			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);
+		}
+	}
+	
+	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<>();
+		
+		protected 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;
+		}
+	}
+}
+
+
+
+
+
+
+
+

+ 122 - 0
src/main/java/com/jfinal/proxy/ProxyFactory.java

@@ -0,0 +1,122 @@
+/**
+ * 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.lang.reflect.Modifier;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * ProxyFactory
+ */
+@SuppressWarnings("unchecked")
+public class ProxyFactory {
+	
+	protected ConcurrentHashMap<Class<?>, Class<?>> cache = new ConcurrentHashMap<>();
+	
+	protected ProxyGenerator proxyGenerator = new ProxyGenerator();
+	protected ProxyCompiler proxyCompiler = new ProxyCompiler();
+	protected ProxyClassLoader proxyClassLoader = new ProxyClassLoader();
+	
+	public <T> T get(Class<T> target) {
+		try {
+			Class<T> ret = (Class<T>)cache.get(target);
+			if (ret != null) {
+				return (T)ret.newInstance();
+			} else {
+				return getProxyClass(target).newInstance();
+			}
+		} catch (ReflectiveOperationException e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+	protected <T> Class<T> getProxyClass(Class<T> target) throws ReflectiveOperationException {
+		// 在此不对 static 类做检测,支持对 static 类的代理
+		int mod = target.getModifiers();
+		if ( ! Modifier.isPublic(mod) ) {
+			throw new IllegalArgumentException("Only public class can be proxied");
+		}
+		if (Modifier.isFinal(mod)) {
+			throw new IllegalArgumentException("final class can not be proxied");
+		}
+		if (Modifier.isAbstract(mod)) {
+			throw new IllegalArgumentException("abstract class or interface can not be proxied");
+		}
+		
+		synchronized (target) {
+			Class<T> ret = (Class<T>)cache.get(target);
+			if (ret != null) {
+				return ret;
+			}
+			
+			ProxyClass proxyClass = proxyGenerator.generate(target);
+			if (proxyClass.needProxy()) {
+				proxyCompiler.compile(proxyClass);
+				ret = (Class<T>)proxyClassLoader.loadProxyClass(proxyClass);
+				proxyClass.setClazz(ret);
+				
+				cacheMethodProxy(proxyClass);	// 放在 loadClass 动作之后
+				
+				cache.put(target, ret);
+				return ret;
+			} else {
+				cache.put(target, target);		// 无需代理的情况映射原参数 target
+				return target;
+			}
+		}
+	}
+	
+	/**
+	 * 在生成类被 loadClass 成功以后缓存 MethodProxy,否则 MethodProxyCache
+	 * 将存进去不健康的 ProxyMethod
+	 */
+	protected void cacheMethodProxy(ProxyClass proxyClass) {
+		for (ProxyMethod m : proxyClass.getProxyMethodList()) {
+			m.setProxyClass(proxyClass.getClazz());
+			ProxyMethodCache.put(m);
+		}
+	}
+	
+	public void setProxyGenerator(ProxyGenerator proxyGenerator) {
+		Objects.requireNonNull(proxyGenerator, "proxyGenerator can not be null");
+		this.proxyGenerator = proxyGenerator;
+	}
+	
+	public ProxyGenerator getProxyGenerator() {
+		return proxyGenerator;
+	}
+	
+	public void setProxyCompiler(ProxyCompiler proxyCompiler) {
+		Objects.requireNonNull(proxyCompiler, "proxyCompiler can not be null");
+		this.proxyCompiler = proxyCompiler;
+	}
+	
+	public ProxyCompiler getProxyCompiler() {
+		return proxyCompiler;
+	}
+	
+	public void setProxyClassLoader(ProxyClassLoader proxyClassLoader) {
+		Objects.requireNonNull(proxyClassLoader, "proxyClassLoader can not be null");
+		this.proxyClassLoader = proxyClassLoader;
+	}
+	
+	public ProxyClassLoader getProxyClassLoader() {
+		return proxyClassLoader;
+	}
+}
+

+ 308 - 0
src/main/java/com/jfinal/proxy/ProxyGenerator.java

@@ -0,0 +1,308 @@
+/**
+ * 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.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+import com.jfinal.aop.Before;
+import com.jfinal.aop.Clear;
+import com.jfinal.aop.InterceptorManager;
+import com.jfinal.kit.Kv;
+import com.jfinal.log.Log;
+import com.jfinal.template.Engine;
+import com.jfinal.template.Template;
+
+/**
+ * ProxyGenerator 用于生成代理类的源代码
+ * 
+ * 注意:业务层全局拦截器要在 ProxyGenerator 工作之前配置好,否则无法参与生成
+ * 
+ * 追求性能极致:
+ * 1:禁止使用 JDK 的 Method.invoke(...) 调用被代理方法
+ * 2:method 存在有效的拦截器才生成代理方法 ProxyMethod
+ * 3:目标类 target 存在 ProxyMethod 才生成代理类 ProxyClass
+ * 
+ * 避免生成代理类的方法:
+ * 1:类文件内部删掉 @Before 声明的拦截器
+ * 2:添加一个 class 层的 @Clear 注解
+ * 因此,proxy 模块设计可以覆盖掉 @Enhance 注解功能
+ */
+public class ProxyGenerator {
+	
+	protected static final Log log = Log.getLog(ProxyGenerator.class);
+	
+	protected Engine engine = new Engine("forProxy").setToClassPathSourceFactory();
+	protected Template template = engine.getTemplate("com/jfinal/proxy/proxy_class_template.jf");
+	
+	protected boolean printGeneratedClassToConsole = false;
+	
+	public ProxyClass generate(Class<?> target) {
+		ProxyClass proxyClass = new ProxyClass(target);
+		
+		Kv clazz = Kv.create();
+		clazz.set("pkg", proxyClass.getPkg());
+		clazz.set("name", proxyClass.getName());
+		clazz.set("targetName", getTargetName(target));
+		
+		List<Class<?>> methodUpperInters = getMethodUpperInterceptors(proxyClass);
+		
+		List<Kv> methodList = new ArrayList<>();
+		clazz.set("methodList", methodList);
+		Method[] methodArray = target.getMethods();
+		for (Method m : methodArray) {
+			if (isSkipMethod(m)) {
+				continue ;
+			}
+			
+			// 没有拦截器的 method 不生成 ProxyMethod
+			if ( ! hasInterceptor(methodUpperInters, proxyClass, m) ) {
+				continue ;
+			}
+			
+			Kv method = Kv.create();
+			method.set("returnType", getReturnType(m));
+			method.set("name", m.getName());
+			
+			Parameter[] paras = m.getParameters();
+			List<String> paraTypes = Arrays.asList(paras).stream().map(
+				x -> {
+					// 参考 JDK Parameter
+					StringBuilder sb = new StringBuilder();
+			        Type type = x.getParameterizedType();
+			        String typename = type.getTypeName();
+			        
+			        if(x.isVarArgs()) {
+			            sb.append(typename.replaceFirst("\\[\\]$", "..."));
+			        } else {
+			            sb.append(typename);
+			        }
+			        
+			        return sb.toString();
+				}
+			)
+			.collect(Collectors.toList());
+			method.set("paraTypes", paraTypes);
+			
+			// 缓存 ProxyMethod 的 key 值
+			Long proxyMethodKey = ProxyMethodCache.generateKey();
+			method.set("proxyMethodKey", proxyMethodKey);
+			
+			// 方法仅有一个可变参数时传递一个 boolean onlyVarArgs 为 true
+			if (paras.length == 1 && paras[0].isVarArgs()) {
+				method.set("onlyVarArgs", true);
+			}
+			
+			if (m.getReturnType() != void.class) {
+				method.set("frontReturn", "return ");
+			} else {
+				method.set("backReturn", "return null;");
+			}
+			
+			methodList.add(method);
+			
+			ProxyMethod proxyMethod = new ProxyMethod();
+			proxyClass.addProxyMethod(proxyMethod);
+			proxyMethod.setKey(proxyMethodKey);
+			proxyMethod.setTargetClass(target);
+			proxyMethod.setMethod(m);
+		}
+		
+		if (proxyClass.needProxy()) {
+			String sourceCode = template.renderToString(clazz);
+			proxyClass.setSourceCode(sourceCode);
+			
+			if (printGeneratedClassToConsole) {
+				String msg = "Generate proxy class \"" + proxyClass.getPkg() + "." + proxyClass.getName() + "\":";
+				System.out.print(msg);
+				System.out.println(sourceCode);
+			}
+			if (log.isInfoEnabled()) {
+				String msg = "\nGenerate proxy class \"" + proxyClass.getPkg() + "." + proxyClass.getName() + "\":";
+				log.info(msg + sourceCode);
+			}
+		}
+		
+		return proxyClass;
+	}
+	
+	/**
+	 * 支持对 static 类的代理
+	 */
+	protected String getTargetName(Class<?> target) {
+		if (Modifier.isStatic(target.getModifiers())) {
+			// 无法兼容主类类名中包含字符 '$',例如:com.xxx.My$Target&Inner
+			// return target.getName().replace('$', '.');
+			
+			// 静态类的 getName() 值为 com.xxx.Target&Inner 需要将字符 '$' 替换成 '.'
+			String ret = target.getName();
+			int index = ret.lastIndexOf('$');
+			return ret.substring(0, index) + "." + ret.substring(index + 1);
+		} else {
+			return target.getSimpleName();
+		}
+	}
+	
+	/**
+	 * 方法返回值为 int[] 时 method.getReturnType().getName() 返回值为: [I
+	 * 需要识别并转化
+	 */
+	protected String getReturnType(Method method) {
+		// return method.getReturnType().getName();
+		// return method.getAnnotatedReturnType().getType().getTypeName();
+		return method.getGenericReturnType().getTypeName();
+	}
+	
+	/**
+	 * 跳过不能代理的方法
+	 * 1:非 public
+	 * 2:final、static、abstract
+	 * 3:方法名为:toString、hashCode、equals
+	 */
+	protected boolean isSkipMethod(Method method) {
+		int mod = method.getModifiers();
+		if ( ! Modifier.isPublic(mod) ) {
+			return true;
+		}
+		
+		if (Modifier.isFinal(mod) || Modifier.isStatic(mod) || Modifier.isAbstract(mod)) {
+			return true;
+		}
+		
+		String n = method.getName();
+		if (n.equals("toString") || n.equals("hashCode") || n.equals("equals")) {
+			return true;
+		}
+		
+		return false;
+	}
+	
+	/**
+	 * 获取 method 上层的拦截器,也即获取 global、class 这两层拦截器
+	 * 注意:global 层拦截器已结合 class 层 @Clear 注解处理过
+	 */
+	protected List<Class<?>> getMethodUpperInterceptors(ProxyClass proxyClass) {
+		List<Class<?>> ret;
+		
+		// 结合 class 级 @Clear,得到 global 级拦截器
+		Clear clearOnClass = proxyClass.getTarget().getAnnotation(Clear.class);
+		if (clearOnClass != null) {
+			Class<?>[] clearIntersOnClass = clearOnClass.value();
+			if (clearIntersOnClass.length != 0) {	// class 级 @clear 且带参
+				ret = InterceptorManager.me().getGlobalServiceInterceptorClasses();
+				removeInterceptor(ret, clearIntersOnClass);
+			} else {
+				ret = new ArrayList<>(3);
+			}
+		} else {
+			ret = InterceptorManager.me().getGlobalServiceInterceptorClasses();
+		}
+		
+		// 追加 class 级拦截器
+		Before beforeOnClass = proxyClass.getTarget().getAnnotation(Before.class);
+		if (beforeOnClass != null) {
+			Class<?>[] classInters = beforeOnClass.value();
+			for (Class<?> c : classInters) {
+				ret.add(c);
+			}
+		}
+		
+		return ret;
+	}
+	
+	protected void removeInterceptor(List<Class<?>> target, Class<?>[] clearInters) {
+		if (target.isEmpty() || clearInters.length == 0) {
+			return ;
+		}
+		
+		for (Iterator<Class<?>> it = target.iterator(); it.hasNext();) {
+			Class<?> interClass = it.next();
+			for (Class<?> c : clearInters) {
+				if (c == interClass) {
+					it.remove();
+					break ;
+				}
+			}
+		}
+	}
+	
+	/**
+	 * 当前 method 是否存在有效拦截器
+	 * 1:如果存在 method 级拦截器,则 return true
+	 * 2:否则结合 method 级的 @Clear 考察 global、class 两层拦截器的留存
+	 *    global、class 两层拦截器已作为参数 methodUpperInters 被传入
+	 *    methodUpperInters 中的拦截器已结合 class 级 @Clear 处理过
+	 */
+	protected boolean hasInterceptor(List<Class<?>> methodUpperInters, ProxyClass proxyClass, Method method) {
+		// 如果 method 存在拦截器,可断定 hasInterceptor 为真,因为 @Clear 不能清除 method 级拦截器
+		Before beforeOnMethod = method.getAnnotation(Before.class);
+		if (beforeOnMethod != null && beforeOnMethod.value().length != 0) {
+			return true;
+		}
+		
+		// method 级拦截器不存在的情况,只需考虑察 global、class 级拦截器结合 method 级 @Clear 的留存情况
+		List<Class<?>> ret;
+		Clear clearOnMethod = method.getAnnotation(Clear.class);
+		if (clearOnMethod != null) {
+			Class<?>[] clearIntersOnMethod = clearOnMethod.value();
+			if (clearIntersOnMethod.length != 0) {
+				// 复制一份 methodUpperInters,以免 removeInterceptorClass 操作影响下次迭代
+				ret = copyInterceptors(methodUpperInters);
+				removeInterceptor(ret, clearIntersOnMethod);
+			} else {
+				ret = null;
+			}
+		} else {
+			ret = methodUpperInters;
+		}
+		
+		return ret != null && ret.size() > 0;
+	}
+	
+	protected List<Class<?>> copyInterceptors(List<Class<?>> methodUpperInters) {
+		List<Class<?>> ret = new ArrayList<>(methodUpperInters.size());
+		for (Class<?> c : methodUpperInters) {
+			ret.add(c);
+		}
+		return ret;
+	}
+	
+	/**
+	 * 配置打印生成类到控制台
+	 */
+	public void setPrintGeneratedClassToConsole(boolean printGeneratedClassToConsole) {
+		this.printGeneratedClassToConsole = printGeneratedClassToConsole;
+	}
+	
+	public void setProxyClassTemplate(String proxyClassTemplate) {
+		template = engine.getTemplate(proxyClassTemplate);
+	}
+}
+
+
+
+
+
+
+
+

+ 48 - 0
src/main/java/com/jfinal/proxy/ProxyManager.java

@@ -0,0 +1,48 @@
+/**
+ * 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.Objects;
+
+/**
+ * ProxyManager
+ */
+public class ProxyManager {
+	
+	private static final ProxyManager me = new ProxyManager();
+	
+	private ProxyManager() {}
+	
+	public static ProxyManager me() {
+		return me;
+	}
+	
+	public ProxyManager setProxyFactory(ProxyFactory proxyFactory) {
+		Objects.requireNonNull(proxyFactory, "proxyFactory can not be null");
+		Proxy.proxyFactory = proxyFactory;
+		return this;
+	}
+	
+	public ProxyFactory getProxyFactory() {
+		return Proxy.proxyFactory;
+	}
+}
+
+
+
+
+

+ 90 - 0
src/main/java/com/jfinal/proxy/ProxyMethod.java

@@ -0,0 +1,90 @@
+/**
+ * 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.lang.reflect.Method;
+import com.jfinal.aop.Interceptor;
+import com.jfinal.aop.InterceptorManager;
+
+/**
+ * ProxyMethod
+ * 
+ * 在 ProxyFactory 生成、编译、加载代理类彻底完成之后,
+ * 再将 ProxyMethod 放入缓存,避免中途出现异常时缓存
+ * 不完整的 ProxyMethod 对象
+ */
+public class ProxyMethod {
+	
+	static final InterceptorManager interMan = InterceptorManager.me();
+	
+	private Long key;
+	
+	private Class<?> targetClass;
+	private Class<?> proxyClass;
+	private Method method;
+	private Interceptor[] interceptors = null;
+	
+	public void setKey(long key) {
+		this.key = key;
+	}
+	
+	public Long getKey() {
+		return key;
+	}
+	
+	public void setTargetClass(Class<?> targetClass) {
+		this.targetClass = targetClass;
+	}
+	
+	public Class<?> getTargetClass() {
+		return targetClass;
+	}
+	
+	/**
+	 * 代理类在 ProxyFactory 中才被 loadClass,所以本方法在 ProxyFactory 中被调用
+	 */
+	public void setProxyClass(Class<?> proxyClass) {
+		this.proxyClass = proxyClass;
+	}
+	
+	public Class<?> getProxyClass() {
+		return proxyClass;
+	}
+	
+	public void setMethod(Method method) {
+		this.method = method;
+	}
+	
+	public Method getMethod() {
+		return method;
+	}
+	
+	/**
+	 * 分离类的生成与对象的创建,避免 ProxyGenerator 与 AopFactory 形成死循环
+	 * 
+	 * 本方法仅在 Invocation 构造方法中调用
+	 */
+	public Interceptor[] getInterceptors() {
+		if (interceptors == null) {
+			Interceptor[] ret = interMan.buildServiceMethodInterceptor(InterceptorManager.NULL_INTERS, targetClass, method);
+			interceptors = ret;
+		}
+		return interceptors;
+	}
+}
+
+

+ 58 - 0
src/main/java/com/jfinal/proxy/ProxyMethodCache.java

@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.jfinal.proxy;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+import com.jfinal.kit.SyncWriteMap;
+
+/**
+ * ProxyMethodCache
+ */
+public class ProxyMethodCache {
+	
+	private static final AtomicLong atomicLong = new AtomicLong();
+	private static final Map<Long, ProxyMethod> cache = new SyncWriteMap<>(2048, 0.25F);
+	
+	public static Long generateKey() {
+		return atomicLong.incrementAndGet();
+	}
+	
+	public static void put(ProxyMethod proxyMethod) {
+		Objects.requireNonNull(proxyMethod, "proxyMethod can not be null");
+		Objects.requireNonNull(proxyMethod.getKey(), "the key of proxyMethod can not be null");
+		if (cache.containsKey(proxyMethod.getKey())) {
+			throw new RuntimeException("the key of proxyMethod already exists");
+		}
+		
+		cache.putIfAbsent(proxyMethod.getKey(), proxyMethod);
+	}
+	
+	public static ProxyMethod get(Long key) {
+		return cache.get(key);
+	}
+}
+
+
+
+
+
+
+
+
+

+ 84 - 0
src/main/java/com/jfinal/proxy/proxy_class_template.jf

@@ -0,0 +1,84 @@
+#--
+生成的源代码格式如下:
+
+package com.xxx;
+import com.jfinal.aop.Invocation;
+
+public class Target$$EnhancerByJFinal extends Target {
+	public String test(String p0, int p1) {
+		Invocation inv = new Invocation(this, 123L,
+			args -> {
+				return super.test(
+							(String)args[0],
+							(int)args[1]
+						);
+			},
+			p0, p1);
+		
+		inv.invoke();
+		
+		return inv.getReturnValue();
+	}
+}
+--#
+
+package #(pkg);
+import com.jfinal.aop.Invocation;
+public class #(name) extends #(targetName) {
+#for(x : methodList)
+	
+	public #(x.returnType) #(x.name)(#for(y : x.paraTypes)#(y) p#(for.index)#(for.last ? "" : ", ")#end) {
+		#if(x.onlyVarArgs)
+		#@newInvocationForOnlyVarArgs()
+		#else
+		#@newInvocationForCommon()
+		#end
+		
+		inv.invoke();
+		#if (x.returnType != "void")
+		
+		return inv.getReturnValue();
+		#end
+	}
+#end
+}
+
+#--
+   一般参数情况
+--#
+#define newInvocationForCommon()
+		Invocation inv = new Invocation(this, #(x.proxyMethodKey)L,
+			args -> {
+				#(x.frontReturn) #(name).super.#(x.name)(
+						#for(y : x.paraTypes)
+						(#(y.replace("...", "[]")))args[#(for.index)]#(for.last ? "" : ",")
+						#end
+					);
+				#(x.backReturn)
+			}
+			#for(y : x.paraTypes), p#(for.index)#end);
+#end
+#--
+   只有一个参数,且该参数是可变参数
+--#
+#define newInvocationForOnlyVarArgs()
+		Object[] p0Array = new Object[p0.length];
+		for (int i=0; i<p0.length; i++) {
+			p0Array[i] = p0[i];
+		}
+		
+		Invocation inv = new Invocation(this, #(x.proxyMethodKey)L,
+			args -> {
+				#setLocal(t = x.paraTypes[0].replace("...", ""))
+				#(t)[] array = new #(t)[args.length];
+				for (int i=0; i<array.length; i++) {
+					array[i] = (#(t))args[i];
+				}
+				
+				#(x.frontReturn) #(name).super.#(x.name)(
+						array
+					);
+				#(x.backReturn)
+			}
+			, p0Array);
+#end