Browse Source

jfinal 4.2

James 6 years ago
parent
commit
e8718eefae

+ 8 - 2
pom.xml

@@ -175,8 +175,14 @@
 			<artifactId>javase</artifactId>
 			<version>3.2.1</version>
 			<scope>provided</scope>
-		 </dependency>
-		 
+		</dependency>
+		
+		<dependency>
+			<groupId>cglib</groupId>
+			<artifactId>cglib-nodep</artifactId>
+			<version>3.2.5</version>
+			<scope>provided</scope>
+		</dependency>
 		<dependency>
 			<groupId>org.springframework</groupId>
 			<artifactId>spring-webmvc</artifactId>

+ 14 - 0
src/main/java/com/jfinal/aop/Invocation.java

@@ -58,6 +58,20 @@ public class Invocation {
 		this(target, proxyMethodKey, callback, NULL_ARGS);
 	}
 	
+	/**
+	 * 用于扩展 ProxyFactory
+	 */
+	public Invocation(Object target, Method method, Interceptor[] inters, Callback callback, Object[] args) {
+		this.action = null;
+		this.target = target;
+		
+		this.method = method;
+		this.inters = inters;
+		
+		this.callback = callback;
+		this.args = args;
+	}
+	
 	// InvocationWrapper need this constructor
 	protected Invocation() {
 		this.action = null;

+ 80 - 0
src/main/java/com/jfinal/ext/proxy/CglibCallback.java

@@ -0,0 +1,80 @@
+/**
+ * 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.ext.proxy;
+
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Set;
+import com.jfinal.aop.Interceptor;
+import com.jfinal.aop.InterceptorManager;
+import com.jfinal.aop.Invocation;
+import com.jfinal.ext.proxy.CglibProxyFactory.IntersCache;
+import com.jfinal.ext.proxy.CglibProxyFactory.MethodKey;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+
+/**
+ * CglibCallback.
+ */
+class CglibCallback implements MethodInterceptor {
+	
+	private static final Set<String> excludedMethodName = buildExcludedMethodName();
+	private static final InterceptorManager interMan = InterceptorManager.me();
+	
+	public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
+		if (excludedMethodName.contains(method.getName())) {
+			return methodProxy.invokeSuper(target, args);
+		}
+		
+		Class<?> targetClass = target.getClass();
+		if (targetClass.getName().indexOf("$$EnhancerBy") != -1) {
+			targetClass = targetClass.getSuperclass();
+		}
+		
+		
+		MethodKey key = IntersCache.getMethodKey(targetClass, method);
+		Interceptor[] inters = IntersCache.get(key);
+		if (inters == null) {
+			inters = interMan.buildServiceMethodInterceptor(targetClass, method);
+			IntersCache.put(key, inters);
+		}
+		
+		Invocation invocation = new Invocation(target, method, inters, 
+			x -> {
+				return methodProxy.invokeSuper(target, x);
+			}
+		, args);
+		
+		
+		invocation.invoke();
+		return invocation.getReturnValue();
+	}
+	
+	private static final Set<String> buildExcludedMethodName() {
+		Set<String> excludedMethodName = new HashSet<String>(64, 0.25F);
+		Method[] methods = Object.class.getDeclaredMethods();
+		for (Method m : methods) {
+			excludedMethodName.add(m.getName());
+		}
+		// getClass() registerNatives() can not be enhanced
+		// excludedMethodName.remove("getClass");	
+		// excludedMethodName.remove("registerNatives");
+		return excludedMethodName;
+	}
+}
+
+

+ 101 - 0
src/main/java/com/jfinal/ext/proxy/CglibProxyFactory.java

@@ -0,0 +1,101 @@
+/**
+ * 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.ext.proxy;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Objects;
+import com.jfinal.aop.Interceptor;
+import com.jfinal.kit.HashKit;
+import com.jfinal.kit.SyncWriteMap;
+import com.jfinal.proxy.ProxyFactory;
+
+/**
+ * CglibProxyFactory 用于扩展 cglib 的代理模式,默认不使用
+ * 
+ * <pre>
+ * 配置方法:
+ * 	ProxyManager.me().setProxyFactory(new CglibProxyFactory());
+ * </pre>
+ */
+public class CglibProxyFactory extends ProxyFactory {
+	
+	@SuppressWarnings("unchecked")
+	public <T> T get(Class<T> target) {
+		return (T)net.sf.cglib.proxy.Enhancer.create(target, new CglibCallback());
+	}
+	
+	static class IntersCache {
+		private static final Map<MethodKey, Interceptor[]> cache = new SyncWriteMap<>(2048, 0.25F);
+		
+		public static void put(MethodKey methodKey, Interceptor[] inters) {
+			Objects.requireNonNull(methodKey, "methodKey can not be null");
+			Objects.requireNonNull(inters, "inters can not be null");
+			
+			cache.putIfAbsent(methodKey, inters);
+		}
+		
+		public static Interceptor[] get(MethodKey methodKey) {
+			return cache.get(methodKey);
+		}
+		
+		public static MethodKey getMethodKey(Class<?> target, Method method) {
+			long paraHash = HashKit.FNV_OFFSET_BASIS_64;
+			Class<?>[] paraTypes = method.getParameterTypes();
+			for (Class<?> pt : paraTypes) {
+				paraHash ^= pt.getName().hashCode();
+				paraHash *= HashKit.FNV_PRIME_64;
+			}
+			
+			return new MethodKey(target.getName().hashCode(), method.getName().hashCode(), paraHash);
+		}
+	}
+	
+	static class MethodKey {
+		final int classHash;
+		final int methodHash;
+		final long paraHash;
+		
+		MethodKey(int classHash, int methodHash, long paraHash) {
+			this.classHash = classHash;
+			this.methodHash = methodHash;
+			this.paraHash = paraHash;
+		}
+		
+		public int hashCode() {
+			return classHash ^ methodHash ^ ((int)paraHash);
+		}
+		
+		/**
+		 * 通过比较三部分 hash 值,避免超大规模场景下可能的 key 值碰撞
+		 * 
+		 * 不必判断 if (methodKey instanceof MethodKey),因为所有 key 类型必须要相同
+		 * 不必判断 if (this == methodKey),因为每次用于取值的 methodKey 都是新建的
+		 */
+		public boolean equals(Object methodKey) {
+			MethodKey mk = (MethodKey)methodKey;
+			return classHash == mk.classHash && methodHash == mk.methodHash && paraHash == mk.paraHash;
+		}
+		
+		public String toString() {
+			return "classHash = " + classHash + "\nmethodHash = " + methodHash + "\nparaHash = " + paraHash;
+		}
+	}
+}
+
+
+