ソースを参照

!37 fixed bugs
Merge pull request !37 from fuhai/master

fuhai 6 年 前
コミット
61b2fb20aa

+ 316 - 277
src/main/java/com/jfinal/aop/AopFactory.java

@@ -1,277 +1,316 @@
-package com.jfinal.aop;
-
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * AopFactory 是工具类 Aop 功能的具体实现,详细用法见 Aop
- */
-public class AopFactory {
-	
-	// 单例缓存
-	protected ConcurrentHashMap<Class<?>, Object> singletonCache = new ConcurrentHashMap<Class<?>, Object>();
-	
-	// 父类到子类、接口到实现类之间的映射关系
-	protected HashMap<Class<?>, Class<?>> mapping = null;
-	
-	protected static int MAX_INJECT_DEPTH = 7;			// 最大注入深度
-	
-	protected boolean singleton = true;					// 默认单例
-	protected boolean enhance = true;					// 默认增强
-	protected int injectDepth = 3;						// 默认注入深度
-	
-	public <T> T get(Class<T> targetClass) {
-		try {
-			return doGet(targetClass, injectDepth);
-		} catch (ReflectiveOperationException e) {
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public <T> T get(Class<T> targetClass, int injectDepth) {
-		try {
-			return doGet(targetClass, injectDepth);
-		} catch (ReflectiveOperationException e) {
-			throw new RuntimeException(e);
-		}
-	}
-	
-	@SuppressWarnings("unchecked")
-	protected <T> T doGet(Class<T> targetClass, int injectDepth) throws ReflectiveOperationException {
-		// Aop.get(obj.getClass()) 可以用 Aop.inject(obj),所以注掉下一行代码
-		// targetClass = (Class<T>)getUsefulClass(targetClass);
-		
-		targetClass = (Class<T>)getMappingClass(targetClass);
-		
-		Singleton si = targetClass.getAnnotation(Singleton.class);
-		boolean singleton = (si != null ? si.value() : this.singleton);
-		
-		Object ret;
-		if ( ! singleton ) {
-			ret = createObject(targetClass);
-			doInject(targetClass, ret, injectDepth);
-			return (T)ret;
-		}
-		
-		ret = singletonCache.get(targetClass);
-		if (ret == null) {
-			synchronized (this) {
-				ret = singletonCache.get(targetClass);
-				if (ret == null) {
-					ret = createObject(targetClass);
-					doInject(targetClass, ret, injectDepth);
-					singletonCache.put(targetClass, ret);
-				}
-			}
-		}
-		
-		return (T)ret;
-	}
-	
-	public <T> T inject(T targetObject) {
-		try {
-			doInject(targetObject.getClass(), targetObject, injectDepth);
-			return targetObject;
-		} catch (ReflectiveOperationException e) {
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public <T> T inject(T targetObject, int injectDepth) {
-		try {
-			doInject(targetObject.getClass(), targetObject, injectDepth);
-			return targetObject;
-		} catch (ReflectiveOperationException e) {
-			throw new RuntimeException(e);
-		}
-	}
-	
-	// 方法原型的参数测试过可以是:Class<? super T> targetClass, T targetObject
-	public <T> T inject(Class<T> targetClass, T targetObject) {
-		try {
-			doInject(targetClass, targetObject, injectDepth);
-			return targetObject;
-		} catch (ReflectiveOperationException e) {
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public <T> T inject(Class<T> targetClass, T targetObject, int injectDepth) {
-		try {
-			doInject(targetClass, targetObject, injectDepth);
-			return targetObject;
-		} catch (ReflectiveOperationException e) {
-			throw new RuntimeException(e);
-		}
-	}
-	
-	protected void doInject(Class<?> targetClass, Object targetObject, int injectDepth) throws ReflectiveOperationException {
-		if ((injectDepth--) <= 0) {
-			return ;
-		}
-		
-		targetClass = getUsefulClass(targetClass);
-		Field[] fields = targetClass.getDeclaredFields();
-		if (fields.length == 0) {
-			return ;
-		}
-		
-		for (Field field : fields) {
-			Inject inject = field.getAnnotation(Inject.class);
-			if (inject == null) {
-				continue ;
-			}
-			
-			Class<?> fieldInjectedClass = inject.value();
-			if (fieldInjectedClass == Void.class) {
-				fieldInjectedClass = field.getType();
-			}
-			
-			Object fieldInjectedObject = doGet(fieldInjectedClass, injectDepth);
-			field.setAccessible(true);
-			field.set(targetObject, fieldInjectedObject);
-		}
-	}
-	
-	/**
-	 * 由于上层已经处理过 singleton,所以 Enhancer.enhance() 方法中不必关心 singleton
-	 */
-	@SuppressWarnings("deprecation")
-	protected Object createObject(Class<?> targetClass) throws ReflectiveOperationException {
-		Enhance en = targetClass.getAnnotation(Enhance.class);
-		boolean enhance = (en != null ? en.value() : this.enhance);
-		
-		return enhance ? com.jfinal.aop.Enhancer.enhance(targetClass) : targetClass.newInstance();
-	}
-	
-	/**
-	 * 被 cglib、guice 增强过的类需要通过本方法获取到被增强之前的类型
-	 * 否则调用其 targetClass.getDeclaredFields() 方法时
-	 * 获取到的是一堆 cglib guice 生成类中的 Field 对象
-	 * 而被增强前的原类型中的 Field 反而获取不到
-	 */
-	protected Class<?> getUsefulClass(Class<?> clazz) {
-		// com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158
-		// return (Class<? extends Model>)((modelClass.getName().indexOf("EnhancerByCGLIB") == -1 ? modelClass : modelClass.getSuperclass()));
-		return (Class<?>)(clazz.getName().indexOf("$$EnhancerBy") == -1 ? clazz : clazz.getSuperclass());
-	}
-	
-	/**
-	 * 设置被注入的对象是否被增强,可使用 @Enhace(boolean) 覆盖此默认值
-	 * 
-	 * 由于下一版本的 jfinal 3.6 将根据目标类中是否配置了拦截器来决定是否增强,
-	 * 所以该 setEnhance 方法仅仅是一个过渡功能,不建议使用
-	 */
-	@Deprecated
-	public AopFactory setEnhance(boolean enhance) {
-		this.enhance = enhance;
-		return this;
-	}
-	
-	/**
-	 * 设置被注入的对象是否为单例,可使用 @Singleton(boolean) 覆盖此默认值 
-	 */
-	public AopFactory setSingleton(boolean singleton) {
-		this.singleton = singleton;
-		return this;
-	}
-	
-	public boolean isSingleton() {
-		return singleton;
-	}
-	
-	/**
-	 * 设置注入深度,避免被注入类在具有循环依赖时造成无限循环
-	 */
-	public AopFactory setInjectDepth(int injectDepth) {
-		if (injectDepth <= 0) {
-			throw new IllegalArgumentException("注入层数必须大于 0");
-		}
-		if (injectDepth > MAX_INJECT_DEPTH) {
-			throw new IllegalArgumentException("为保障性能,注入层数不能超过 " + MAX_INJECT_DEPTH);
-		}
-		
-		this.injectDepth = injectDepth;
-		return this;
-	}
-	
-	public int getInjectDepth() {
-		return injectDepth;
-	}
-	
-	public AopFactory addSingletonObject(Object singletonObject) {
-		if (singletonObject == null) {
-			throw new IllegalArgumentException("singletonObject can not be null");
-		}
-		if (singletonObject instanceof Class) {
-			throw new IllegalArgumentException("singletonObject can not be Class type");
-		}
-		
-		Class<?> type = getUsefulClass(singletonObject.getClass());
-		if (singletonCache.putIfAbsent(type, singletonObject) != null) {
-			throw new RuntimeException("Singleton object already exists for type : " + type.getName());
-		}
-		
-		return this;
-	}
-	
-	public synchronized <T> AopFactory addMapping(Class<T> from, Class<? extends T> to) {
-		if (from == null || to == null) {
-			throw new IllegalArgumentException("The parameter from and to can not be null");
-		}
-		
-		if (mapping == null) {
-			mapping = new HashMap<Class<?>, Class<?>>(128, 0.25F);
-		} else if (mapping.containsKey(from)) {
-			throw new RuntimeException("Class already mapped : " + from.getName());
-		}
-		
-		mapping.put(from, to);
-		return this;
-	}
-	
-	public <T> AopFactory addMapping(Class<T> from, String to) {
-		try {
-			@SuppressWarnings("unchecked")
-			Class<T> toClass = (Class<T>)Class.forName(to.trim());
-			if (from.isAssignableFrom(toClass)) {
-				return addMapping(from, toClass);
-			} else {
-				throw new IllegalArgumentException("The parameter \"to\" must be the subclass or implementation of the parameter \"from\"");
-			}
-		} catch (ReflectiveOperationException e) {
-			throw new RuntimeException(e);
-		}
-	}
-	
-	/**
-	 * 获取父类到子类的映射值,或者接口到实现类的映射值
-	 * @param from 父类或者接口 
-	 * @return 如果映射存在则返回映射值,否则返回参数 from 的值
-	 */
-	public Class<?> getMappingClass(Class<?> from) {
-		if (mapping != null) {
-			Class<?> ret = mapping.get(from);
-			return ret != null ? ret : from;
-		} else {
-			return from;
-		}
-	}
-}
-
-
-/* 未来考虑不再支持对象的 Aop,只支持 Class 的 Aop
-public <T> T get(T targetObject) {
-	try {
-		inject(injectDepth, targetObject.getClass(), targetObject);
-		return targetObject;
-	}
-	catch (Exception e) {
-		throw new RuntimeException(e);
-	}
-}*/
-
-
-
-
+package com.jfinal.aop;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * AopFactory 是工具类 Aop 功能的具体实现,详细用法见 Aop
+ */
+public class AopFactory {
+	
+	// 单例缓存
+	protected ConcurrentHashMap<Class<?>, Object> singletonCache = new ConcurrentHashMap<Class<?>, Object>();
+
+	//实例化的上下文
+	protected ThreadLocal<HashMap<Class<?>, Object>> context = ThreadLocal.withInitial(() -> new HashMap<>());
+	
+	// 父类到子类、接口到实现类之间的映射关系
+	protected HashMap<Class<?>, Class<?>> mapping = null;
+	
+//	protected static int MAX_INJECT_DEPTH = 7;			// 最大注入深度
+	
+	protected boolean singleton = true;					// 默认单例
+	protected boolean enhance = true;					// 默认增强
+//	protected int injectDepth = 3;						// 默认注入深度
+	
+	public <T> T get(Class<T> targetClass) {
+		try {
+//			return doGet(targetClass, injectDepth);
+			return doGet(targetClass);
+		} catch (ReflectiveOperationException e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+//	public <T> T get(Class<T> targetClass, int injectDepth) {
+//		try {
+//			return doGet(targetClass, injectDepth);
+//		} catch (ReflectiveOperationException e) {
+//			throw new RuntimeException(e);
+//		}
+//	}
+	
+	@SuppressWarnings("unchecked")
+	protected <T> T doGet(Class<T> targetClass) throws ReflectiveOperationException {
+		// Aop.get(obj.getClass()) 可以用 Aop.inject(obj),所以注掉下一行代码
+		// targetClass = (Class<T>)getUsefulClass(targetClass);
+		
+		targetClass = (Class<T>)getMappingClass(targetClass);
+		
+		Singleton si = targetClass.getAnnotation(Singleton.class);
+		boolean singleton = (si != null ? si.value() : this.singleton);
+		
+		Object ret;
+		if ( ! singleton ) {
+			ret = createObject(targetClass);
+			doInject(targetClass, ret);
+			return (T)ret;
+		}
+		
+//		ret = singletonCache.get(targetClass);
+//		if (ret == null) {
+//			synchronized (this) {
+//				ret = singletonCache.get(targetClass);
+//				if (ret == null) {
+//					ret = createObject(targetClass);
+//					doInject(targetClass, ret);
+//					singletonCache.put(targetClass, ret);
+//				}
+//			}
+//		}
+//
+//		return (T)ret;
+
+		ret = singletonCache.get(targetClass);
+		if (ret != null) {
+			return (T) ret;
+		}
+
+		//只有在循环依赖的时候,这个context才会有值
+		ret = context.get().get(targetClass);
+		if (ret != null) {
+			return (T) ret;
+		}
+
+		synchronized (this) {
+			ret = singletonCache.get(targetClass);
+			if (ret == null) {
+//              ret = createObject(targetClass);
+//              doInject(targetClass, ret, injectDepth);
+//              singletonCache.put(targetClass, ret);
+
+				ret = createObject(targetClass);
+
+				//保存到本次初始化的上下文
+				context.get().put(targetClass, ret);
+
+				//循环注入
+				doInject(targetClass, ret);
+
+				//保存到缓存、并清除上下文数据
+				singletonCache.put(targetClass, ret);
+				context.get().clear();
+				context.remove();
+			}
+		}
+
+		return (T) ret;
+	}
+	
+	public <T> T inject(T targetObject) {
+		try {
+			doInject(targetObject.getClass(), targetObject);
+			return targetObject;
+		} catch (ReflectiveOperationException e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+//	public <T> T inject(T targetObject, int injectDepth) {
+//		try {
+//			doInject(targetObject.getClass(), targetObject, injectDepth);
+//			return targetObject;
+//		} catch (ReflectiveOperationException e) {
+//			throw new RuntimeException(e);
+//		}
+//	}
+	
+	// 方法原型的参数测试过可以是:Class<? super T> targetClass, T targetObject
+	public <T> T inject(Class<T> targetClass, T targetObject) {
+		try {
+			doInject(targetClass, targetObject);
+			return targetObject;
+		} catch (ReflectiveOperationException e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+//	public <T> T inject(Class<T> targetClass, T targetObject, int injectDepth) {
+//		try {
+//			doInject(targetClass, targetObject, injectDepth);
+//			return targetObject;
+//		} catch (ReflectiveOperationException e) {
+//			throw new RuntimeException(e);
+//		}
+//	}
+	
+	protected void doInject(Class<?> targetClass, Object targetObject) throws ReflectiveOperationException {
+//		if ((injectDepth--) <= 0) {
+//			return ;
+//		}
+		
+		targetClass = getUsefulClass(targetClass);
+		Field[] fields = targetClass.getDeclaredFields();
+		if (fields.length == 0) {
+			return ;
+		}
+		
+		for (Field field : fields) {
+			Inject inject = field.getAnnotation(Inject.class);
+			if (inject == null) {
+				continue ;
+			}
+			
+			Class<?> fieldInjectedClass = inject.value();
+			if (fieldInjectedClass == Void.class) {
+				fieldInjectedClass = field.getType();
+			}
+			
+			Object fieldInjectedObject = doGet(fieldInjectedClass);
+			field.setAccessible(true);
+			field.set(targetObject, fieldInjectedObject);
+		}
+	}
+	
+	/**
+	 * 由于上层已经处理过 singleton,所以 Enhancer.enhance() 方法中不必关心 singleton
+	 */
+	@SuppressWarnings("deprecation")
+	protected Object createObject(Class<?> targetClass) throws ReflectiveOperationException {
+		Enhance en = targetClass.getAnnotation(Enhance.class);
+		boolean enhance = (en != null ? en.value() : this.enhance);
+		
+		return enhance ? com.jfinal.aop.Enhancer.enhance(targetClass) : targetClass.newInstance();
+	}
+	
+	/**
+	 * 被 cglib、guice 增强过的类需要通过本方法获取到被增强之前的类型
+	 * 否则调用其 targetClass.getDeclaredFields() 方法时
+	 * 获取到的是一堆 cglib guice 生成类中的 Field 对象
+	 * 而被增强前的原类型中的 Field 反而获取不到
+	 */
+	protected Class<?> getUsefulClass(Class<?> clazz) {
+		// com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158
+		// return (Class<? extends Model>)((modelClass.getName().indexOf("EnhancerByCGLIB") == -1 ? modelClass : modelClass.getSuperclass()));
+		return (Class<?>)(clazz.getName().indexOf("$$EnhancerBy") == -1 ? clazz : clazz.getSuperclass());
+	}
+	
+	/**
+	 * 设置被注入的对象是否被增强,可使用 @Enhace(boolean) 覆盖此默认值
+	 * 
+	 * 由于下一版本的 jfinal 3.6 将根据目标类中是否配置了拦截器来决定是否增强,
+	 * 所以该 setEnhance 方法仅仅是一个过渡功能,不建议使用
+	 */
+	@Deprecated
+	public AopFactory setEnhance(boolean enhance) {
+		this.enhance = enhance;
+		return this;
+	}
+	
+	/**
+	 * 设置被注入的对象是否为单例,可使用 @Singleton(boolean) 覆盖此默认值 
+	 */
+	public AopFactory setSingleton(boolean singleton) {
+		this.singleton = singleton;
+		return this;
+	}
+	
+	public boolean isSingleton() {
+		return singleton;
+	}
+	
+	/**
+	 * 设置注入深度,避免被注入类在具有循环依赖时造成无限循环
+	 */
+//	public AopFactory setInjectDepth(int injectDepth) {
+//		if (injectDepth <= 0) {
+//			throw new IllegalArgumentException("注入层数必须大于 0");
+//		}
+//		if (injectDepth > MAX_INJECT_DEPTH) {
+//			throw new IllegalArgumentException("为保障性能,注入层数不能超过 " + MAX_INJECT_DEPTH);
+//		}
+//
+//		this.injectDepth = injectDepth;
+//		return this;
+//	}
+	
+//	public int getInjectDepth() {
+//		return injectDepth;
+//	}
+	
+	public AopFactory addSingletonObject(Object singletonObject) {
+		if (singletonObject == null) {
+			throw new IllegalArgumentException("singletonObject can not be null");
+		}
+		if (singletonObject instanceof Class) {
+			throw new IllegalArgumentException("singletonObject can not be Class type");
+		}
+		
+		Class<?> type = getUsefulClass(singletonObject.getClass());
+		if (singletonCache.putIfAbsent(type, singletonObject) != null) {
+			throw new RuntimeException("Singleton object already exists for type : " + type.getName());
+		}
+		
+		return this;
+	}
+	
+	public synchronized <T> AopFactory addMapping(Class<T> from, Class<? extends T> to) {
+		if (from == null || to == null) {
+			throw new IllegalArgumentException("The parameter from and to can not be null");
+		}
+		
+		if (mapping == null) {
+			mapping = new HashMap<Class<?>, Class<?>>(128, 0.25F);
+		} else if (mapping.containsKey(from)) {
+			throw new RuntimeException("Class already mapped : " + from.getName());
+		}
+		
+		mapping.put(from, to);
+		return this;
+	}
+	
+	public <T> AopFactory addMapping(Class<T> from, String to) {
+		try {
+			@SuppressWarnings("unchecked")
+			Class<T> toClass = (Class<T>)Class.forName(to.trim());
+			if (from.isAssignableFrom(toClass)) {
+				return addMapping(from, toClass);
+			} else {
+				throw new IllegalArgumentException("The parameter \"to\" must be the subclass or implementation of the parameter \"from\"");
+			}
+		} catch (ReflectiveOperationException e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+	/**
+	 * 获取父类到子类的映射值,或者接口到实现类的映射值
+	 * @param from 父类或者接口 
+	 * @return 如果映射存在则返回映射值,否则返回参数 from 的值
+	 */
+	public Class<?> getMappingClass(Class<?> from) {
+		if (mapping != null) {
+			Class<?> ret = mapping.get(from);
+			return ret != null ? ret : from;
+		} else {
+			return from;
+		}
+	}
+}
+
+
+/* 未来考虑不再支持对象的 Aop,只支持 Class 的 Aop
+public <T> T get(T targetObject) {
+	try {
+		inject(injectDepth, targetObject.getClass(), targetObject);
+		return targetObject;
+	}
+	catch (Exception e) {
+		throw new RuntimeException(e);
+	}
+}*/
+
+
+
+

+ 163 - 166
src/main/java/com/jfinal/core/ActionMapping.java

@@ -1,166 +1,163 @@
-/**
- * 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.core;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import com.jfinal.aop.Interceptor;
-import com.jfinal.aop.InterceptorManager;
-import com.jfinal.config.Routes;
-import com.jfinal.config.Routes.Route;
-
-/**
- * ActionMapping
- */
-public class ActionMapping {
-	
-	protected static final String SLASH = "/";
-	
-	protected Routes routes;
-	protected Map<String, Action> mapping = new HashMap<String, Action>(2048, 0.5F);
-	
-	public ActionMapping(Routes routes) {
-		this.routes = routes;
-	}
-	
-	protected List<Routes> getRoutesList() {
-		List<Routes> routesList = Routes.getRoutesList();
-		List<Routes> ret = new ArrayList<Routes>(routesList.size() + 1);
-		ret.add(routes);
-		ret.addAll(routesList);
-		return ret;
-	}
-	
-	protected void buildActionMapping() {
-		mapping.clear();
-		Class<?> dc;
-		InterceptorManager interMan = InterceptorManager.me();
-		for (Routes routes : getRoutesList()) {
-		for (Route route : routes.getRouteItemList()) {
-			Class<? extends Controller> controllerClass = route.getControllerClass();
-			Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass);
-			
-			boolean declaredMethods = routes.getMappingSuperClass()
-					? controllerClass.getSuperclass() == Controller.class
-					: true;
-			
-			Method[] methods = (declaredMethods ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());
-			for (Method method : methods) {
-				if (declaredMethods) {
-					if (!Modifier.isPublic(method.getModifiers()))
-						continue ;
-				} else {
-					dc = method.getDeclaringClass();
-					if (dc == Controller.class || dc == Object.class)
-						continue ;
-				}
-				
-				if (method.getAnnotation(NotAction.class) != null) {
-					continue ;
-				}
-				
-				Interceptor[] actionInters = interMan.buildControllerActionInterceptor(routes.getInterceptors(), controllerInters, controllerClass, method);
-				String controllerKey = route.getControllerKey();
-				
-				String methodName = method.getName();
-				ActionKey ak = method.getAnnotation(ActionKey.class);
-				String actionKey;
-				if (ak != null) {
-					actionKey = ak.value().trim();
-					if ("".equals(actionKey))
-						throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank.");
-					
-					if (!actionKey.startsWith(SLASH))
-						actionKey = SLASH + actionKey;
-				}
-				else if (methodName.equals("index")) {
-					actionKey = controllerKey;
-				}
-				else {
-					actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
-				}
-				
-				Action action = new Action(controllerKey, actionKey, controllerClass, method, methodName, actionInters, route.getFinalViewPath(routes.getBaseViewPath()));
-				if (mapping.put(actionKey, action) != null) {
-					throw new RuntimeException(buildMsg(actionKey, controllerClass, method));
-				}
-			}
-		}
-		}
-		routes.clear();
-		
-		// support url = controllerKey + urlParas with "/" of controllerKey
-		Action action = mapping.get("/");
-		if (action != null) {
-			mapping.put("", action);
-		}
-	}
-	
-	protected String buildMsg(String actionKey, Class<? extends Controller> controllerClass, Method method) {
-		StringBuilder sb = new StringBuilder("The action \"")
-			.append(controllerClass.getName()).append(".")
-			.append(method.getName()).append("()\" can not be mapped, ")
-			.append("actionKey \"").append(actionKey).append("\" is already in use.");
-		
-		String msg = sb.toString();
-		System.err.println("\nException: " + msg);
-		return msg;
-	}
-	
-	/**
-	 * Support four types of url
-	 * 1: http://abc.com/controllerKey                 ---> 00
-	 * 2: http://abc.com/controllerKey/para            ---> 01
-	 * 3: http://abc.com/controllerKey/method          ---> 10
-	 * 4: http://abc.com/controllerKey/method/para     ---> 11
-	 * The controllerKey can also contains "/"
-	 * Example: http://abc.com/uvw/xyz/method/para
-	 */
-	public Action getAction(String url, String[] urlPara) {
-		Action action = mapping.get(url);
-		if (action != null) {
-			return action;
-		}
-		
-		// --------
-		int i = url.lastIndexOf('/');
-		if (i != -1) {
-			action = mapping.get(url.substring(0, i));
-			urlPara[0] = url.substring(i + 1);
-		}
-		
-		return action;
-	}
-	
-	public List<String> getAllActionKeys() {
-		List<String> allActionKeys = new ArrayList<String>(mapping.keySet());
-		Collections.sort(allActionKeys);
-		return allActionKeys;
-	}
-}
-
-
-
-
-
-
-
+/**
+ * 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.core;
+
+import com.jfinal.aop.Interceptor;
+import com.jfinal.aop.InterceptorManager;
+import com.jfinal.config.Routes;
+import com.jfinal.config.Routes.Route;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+/**
+ * ActionMapping
+ */
+public class ActionMapping {
+	
+	protected static final String SLASH = "/";
+	
+	protected Routes routes;
+	protected Map<String, Action> mapping = new HashMap<String, Action>(2048, 0.5F);
+	
+	public ActionMapping(Routes routes) {
+		this.routes = routes;
+	}
+	
+	protected List<Routes> getRoutesList() {
+		List<Routes> routesList = Routes.getRoutesList();
+		List<Routes> ret = new ArrayList<Routes>(routesList.size() + 1);
+		ret.add(routes);
+		ret.addAll(routesList);
+		return ret;
+	}
+	
+	protected void buildActionMapping() {
+		mapping.clear();
+		Class<?> dc;
+		InterceptorManager interMan = InterceptorManager.me();
+		for (Routes routes : getRoutesList()) {
+		for (Route route : routes.getRouteItemList()) {
+			Class<? extends Controller> controllerClass = route.getControllerClass();
+			Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass);
+			
+			boolean declaredMethods = routes.getMappingSuperClass()
+					? controllerClass.getSuperclass() == Controller.class
+					: true;
+			
+			Method[] methods = (declaredMethods ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());
+			for (Method method : methods) {
+				if (declaredMethods) {
+					if (!Modifier.isPublic(method.getModifiers()))
+						continue ;
+				} else {
+					dc = method.getDeclaringClass();
+					if (dc == Controller.class || dc == Object.class)
+						continue ;
+				}
+				
+				if (method.getAnnotation(NotAction.class) != null) {
+					continue ;
+				}
+				
+				Interceptor[] actionInters = interMan.buildControllerActionInterceptor(routes.getInterceptors(), controllerInters, controllerClass, method);
+				String controllerKey = route.getControllerKey();
+				
+				String methodName = method.getName();
+				ActionKey ak = method.getAnnotation(ActionKey.class);
+				String actionKey;
+				if (ak != null) {
+					actionKey = ak.value().trim();
+					if ("".equals(actionKey))
+						throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank.");
+					
+					if (!actionKey.startsWith(SLASH))
+						actionKey = SLASH + actionKey;
+				}
+				else if (methodName.equals("index")) {
+					actionKey = controllerKey;
+				}
+				else {
+					actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
+				}
+				
+				Action action = new Action(controllerKey, actionKey, controllerClass, method, methodName, actionInters, route.getFinalViewPath(routes.getBaseViewPath()));
+				if (mapping.put(actionKey, action) != null) {
+					throw new RuntimeException(buildMsg(actionKey, controllerClass, method));
+				}
+			}
+		}
+		}
+		routes.clear();
+		
+		// support url = controllerKey + urlParas with "/" of controllerKey
+		Action action = mapping.get("/");
+		if (action != null) {
+			mapping.put("", action);
+		}
+	}
+	
+	protected String buildMsg(String actionKey, Class<? extends Controller> controllerClass, Method method) {
+		StringBuilder sb = new StringBuilder("The action \"")
+			.append(controllerClass.getName()).append(".")
+			.append(method.getName()).append("()\" can not be mapped, ")
+			.append("actionKey \"").append(actionKey).append("\" is already in use.");
+		
+		String msg = sb.toString();
+		System.err.println("\nException: " + msg);
+		return msg;
+	}
+	
+	/**
+	 * Support four types of url
+	 * 1: http://abc.com/controllerKey                 ---> 00
+	 * 2: http://abc.com/controllerKey/para            ---> 01
+	 * 3: http://abc.com/controllerKey/method          ---> 10
+	 * 4: http://abc.com/controllerKey/method/para     ---> 11
+	 * The controllerKey can also contains "/"
+	 * Example: http://abc.com/uvw/xyz/method/para
+	 */
+	public Action getAction(String url, String[] urlPara) {
+		Action action = mapping.get(url);
+		if (action != null) {
+			return action;
+		}
+		
+		// --------
+		int i = url.lastIndexOf('/');
+		if (i != -1) {
+			action = mapping.get(url.substring(0, i));
+			if (action != null) urlPara[0] = url.substring(i + 1);
+		}
+		
+		return action;
+	}
+	
+	public List<String> getAllActionKeys() {
+		List<String> allActionKeys = new ArrayList<String>(mapping.keySet());
+		Collections.sort(allActionKeys);
+		return allActionKeys;
+	}
+}
+
+
+
+
+
+
+