本文共 9663 字,大约阅读时间需要 32 分钟。
上篇我们讲到了`actionMapping.buildActionMapping();`源码:
void buildActionMapping() { mapping.clear(); SetexcludedMethodName = buildExcludedMethodName(); InterceptorManager interMan = InterceptorManager.me(); for (Entry > entry : routes.getEntrySet()) { Class controllerClass = entry.getValue(); Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass); boolean sonOfController = (controllerClass.getSuperclass() == Controller.class); Method[] methods = (sonOfController ? controllerClass.getDeclaredMethods() : controllerClass.getMethods()); for (Method method : methods) { String methodName = method.getName(); if (excludedMethodName.contains(methodName) || method.getParameterTypes().length != 0) continue ; if (sonOfController && !Modifier.isPublic(method.getModifiers())) continue ; Interceptor[] actionInters = interMan.buildControllerActionInterceptor(controllerInters, controllerClass, method); String controllerKey = entry.getKey(); 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, routes.getViewPath(controllerKey)); if (mapping.put(actionKey, action) != null) throw new RuntimeException(buildMsg(actionKey, controllerClass, method)); } } // support url = controllerKey + urlParas with "/" of controllerKey Action action = mapping.get("/"); if (action != null) mapping.put("", action); }
我们看到`SetexcludedMethodName = buildExcludedMethodName();` 这里buildExcludedMethodName方法:
private SetbuildExcludedMethodName() { Set excludedMethodName = new HashSet (); Method[] methods = Controller.class.getMethods(); for (Method m : methods) { if (m.getParameterTypes().length == 0) excludedMethodName.add(m.getName()); } return excludedMethodName; }
从代码中我们可以看出先创建了一个HashSet,猜也能猜出是为了除去重复。接着`Controller.class.getMethods();`方法是利用了反射。①Controller.class获得了Controller类的Class对象。之后又调用了getMethods()方法,获得了Controller类中的所有方法,也就说它的返回值是个数组,里面存的全是Controller类中的方法。接着它又执行一个循环,该循环里if判断`m.getParameterTypes().length == 0`,其中`m.getParameterTypes()`是获取到遍历的当前方法的参数数组。可以看出,这个循环最终保存是Controller方法中无参的方法名。我们再回到ActionMapping中的buildActionMapping方法中。`InterceptorManager interMan = InterceptorManager.me();`该方法获得了一个`InterceptorManager`对象。InterceptorManager我认为应该是个拦截器管理者;管理 控制层、业务层全局拦截器。我们再回到ActionMapping中的buildActionMapping方法中。看到接下来是个for循环。这里是对routes进行遍历。这里的routes对象是我们在JFinal类中执行`Config.configJFinal(jfinalConfig);`语句时,已经初始化了的。它是Config类的一个属性。所以打断点时,你会发现他是`com.jfinal.core.Config$1@1a245833`。而它里面遍历的键值对就是:`/blog=class com.demo.blog.BlogController, /=class com.demo.index.IndexController`接着开始分析该循环,
for (Entry> entry : routes.getEntrySet()) { Class controllerClass = entry.getValue(); Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass); boolean sonOfController = (controllerClass.getSuperclass() == Controller.class); Method[] methods = (sonOfController ? controllerClass.getDeclaredMethods() : controllerClass.getMethods()); for (Method method : methods) { String methodName = method.getName(); if (excludedMethodName.contains(methodName) || method.getParameterTypes().length != 0) continue ; if (sonOfController && !Modifier.isPublic(method.getModifiers())) continue ; Interceptor[] actionInters = interMan.buildControllerActionInterceptor(controllerInters, controllerClass, method); String controllerKey = entry.getKey(); 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, routes.getViewPath(controllerKey)); if (mapping.put(actionKey, action) != null) throw new RuntimeException(buildMsg(actionKey, controllerClass, method)); } }
首先它执行是`interMan.createControllerInterceptor(controllerClass)`方法。它是把遍历到的Controller的Class,作为参数传入进去。该方法源码:
// 此处不缓存控制层 Class 级拦截器,已经在 com.jfinal.core.Action 对象中缓存 public Interceptor[] createControllerInterceptor(Class controllerClass) { return createInterceptor(controllerClass.getAnnotation(Before.class)); }
可以看到它通过Class调用用了getAnnotation()方法。该方法会返回Controller上被Before注解包裹的拦截器。例如:
@Before(BlogInterceptor.class)public class BlogController extends Controller { }
那么getAnnotation方法就会返回Before注解。例如`@com.jfinal.aop.Before(value=[class com.demo.blog.BlogInterceptor])`接着嵌套调用了`createInterceptor(beforeAnnotation.value());`方法。其中`beforeAnnotation.value()`得到的就是使用Before注解包裹的全类名数组。比如:`[class com.demo.blog.BlogInterceptor]`.接着我们看看createInterceptor(beforeAnnotation.value())的源码。
public Interceptor[] createInterceptor(Class [] interceptorClasses) { if (interceptorClasses == null || interceptorClasses.length == 0) { return NULL_INTERS; } Interceptor[] result = new Interceptor[interceptorClasses.length]; try { for (int i=0; i
从上面代码中我们可以看出,如何我们没有使用自定义拦截器,它会return一个`NULL_INTERS`,而`NULL_INTERS`是一个Interceptor[]。
public static final Interceptor[] NULL_INTERS = new Interceptor[0];
这里需要注意的是,Interceptor是个接口,这里`new Interceptor[0]`不是new一个接口,而是new了这个接口的实现类的数组。说白了它就是new了一个数组,类型是Interceptor,只不过Interceptor是个接口罢了。我们回到createInterceptor()方法。接下来是`Interceptor[] result = new Interceptor[interceptorClasses.length];`这里根据Before注解得到的Interceptor类数组大小类创建一个`Interceptor`数组。接着又是个循环,目的是为了通过循环创建相应的实例类(用反射)。并且保存到`InterceptorManager`类中`singletonMap`属性中去。该属性是ConcurrentHashMap类型,HashMap是非线程安全的。官方的说singletonMap是单例拦截器。最后singletonMap保存的是key拦截器的Class,value是拦截器的实例。最后return(返回)存储了自定义拦截器的实例的数组。我们再回到`buildActionMapping()`的方法中。看到`boolean sonOfController = (controllerClass.getSuperclass() == Controller.class);`这里判断遍历到的Controller的父类是不是Controller类型。也就是说该Controller有没有继承Controller类。接着下面这句`Method[] methods = (sonOfController ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());`意思是:如果是继承了Controller类,则调用`getDeclaredMethods()`方法。否则调用`getMethods()`的方法。getMethods()返回的是包括此`Class`对象所表示的类或接口的公共 member方法。也就是说该数组类返回包含的从Object 类继承的所有(公共)member 方法。getDeclaredMethods方法:返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。接着下面又是个循环,遍历之前得到的methods`interMan.buildControllerActionInterceptor`我们再看到`interMan.buildControllerActionInterceptor(controllerInters, controllerClass, method);`方法。该方法也是嵌套了很多层。最后一层的`doBuild(5个参数)` 方法中的`createInterceptor(method.getAnnotation(Before.class));`这个方法之前也讲解过。只不过这次调用getAnnotation()方法的是method。说白了,就是之前是获取controller层的拦截器,而这次是为了获得方法层(method)的拦截器。最后这个doBuild方法返回的是:list数组,里面存储的都是相应拦截器的实例。我们再回到ActionMapping类中的buildActionMapping方法。接下来`entry.getKey()`获得controllerKey。接下来`method.getAnnotation(ActionKey.class)`这里应该是获得jfinal自定义的注解ActionKey,接下来的if判断就是对路径进行组装,就是把methodName组装进去。例如:/blog/add、/blog/update等等。最后它会根据上面的参数new一个action出来。
Action action = new Action(controllerKey, actionKey, controllerClass, method, methodName, actionInters, routes.getViewPath(controllerKey));
if (mapping.put(actionKey, action) != null) throw new RuntimeException(buildMsg(actionKey, controllerClass, method));
这里的mapping.put(actionKey, action)是有返回值的,如果保存的key在mapping中已经存在了,那么它会保存新值,并且返回原来的旧值。如果不存在就为null。至此,buildActionMapping()方法执行完毕。到这里我们也就知道这里封装了controller的信息,controller类中每个方法且方法层的拦截器和路由都封装在一个action(new 出来的)。而每个new出来的action,最后有保存在mapping中。key是路由,value是相对于的action在执行完循环后最后还有一段代码:
Action action = mapping.get("/"); if (action != null) mapping.put("", action);
官方的解释是:`// support url = controllerKey + urlParas with "/" of controllerKey`为了支持这种格式的路由。之后我们就回到了JFinal类中的initActionMapping()方法中的`Config.getRoutes().clear();` 方法。这个方法清除掉了Routes中的map与viewPathMap。这些信息都已经保存到了相应的action中。至此 initActionMapping()方法执行完成。
转载地址:http://qzfh.baihongyu.com/