JFinal个人学习笔记之源码分析2

3 篇文章 0 订阅

本篇是接着上篇讲

上篇我们讲到了`actionMapping.buildActionMapping();`源码:
void buildActionMapping() {
        mapping.clear();
        Set<String> excludedMethodName = buildExcludedMethodName();
        InterceptorManager interMan = InterceptorManager.me();

        for (Entry<String, Class<? extends Controller>> entry : routes.getEntrySet()) {
            Class<? extends Controller> 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);
    }
我们看到`Set<String> excludedMethodName = buildExcludedMethodName();` 这里buildExcludedMethodName方法:
    private Set<String> buildExcludedMethodName() {
        Set<String> excludedMethodName = new HashSet<String>();
        Method[] methods = Controller.class.getMethods();
        for (Method m : methods) {
            if (m.getParameterTypes().length == 0)
                excludedMethodName.add(m.getName());
        }
        return excludedMethodName;
    }
从代码中我们可以看出先创建了一个HashSet<String>,猜也能猜出是为了除去重复。
接着`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<String, Class<? extends Controller>> entry : routes.getEntrySet()) {
            Class<? extends Controller> 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<? extends Controller> 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<? extends Interceptor>[] interceptorClasses) {
        if (interceptorClasses == null || interceptorClasses.length == 0) {
            return NULL_INTERS;
        }

        Interceptor[] result = new Interceptor[interceptorClasses.length];
        try {
            for (int i=0; i<result.length; i++) {
                result[i] = singletonMap.get(interceptorClasses[i]);
                if (result[i] == null) {
                    result[i] = (Interceptor)interceptorClasses[i].newInstance();
                    singletonMap.put(interceptorClasses[i], result[i]);
                }
            }
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
从上面代码中我们可以看出,如何我们没有使用自定义拦截器,它会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()方法执行完成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山鬼谣me

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值