JFinal Controller层 源码解读(待完善)
所有Controller层框架,都离不开HttpServletRequest,HttpServletResponse两大对象。 而框架做的事情无非是 :1. 将Controller中的方法与请求URL形成一对一的映射。2. 提供方便的路由拦截功能。3. 封装了request对象的方法,也就是springMVC中说的参数绑定。4. 封装了response对象的方法,即渲染视图或返回json串。
1. web.xml配置
jfinal核心过滤器拦截所有请求,在init-class 中配置自定义的配置文件对象 后续需要获取该对象。
<!-- 配置jfinal核心拦截器 -->
<filter>
<filter-name>jfinal</filter-name>
<filter-class>com.jfinal.core.JFinalFilter</filter-class>
<init-param>
<param-name>configClass</param-name>
<param-value>com.iipmes.config.IIPConfig</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>jfinal</filter-name>
<!--拦截所有请求-->
<url-pattern>/*</url-pattern>
</filter-mapping>
2.JfinalFilter过滤器
过滤器中两个核心方法:
- ① init()初始化时方法, web容器启动时执行,主要用于加载配置 ,Controller层需要将配置的Routes 加载到ActionMapping中。
- ② doFilter 根据url映射到指定的Controller的方法 并执行(其实是映射一个action对象 ,它里面封装了Controller对象 以及映射的方法Method,拦截器对象等)。
jfinal是配置一个核心拦截器JFinalFilter来处理所有请求,不同于springMvc通过配置一个前端控制器DispatcherServlet来处理所有请求。
//过滤器中的初始化方法 web容器启动时执行
public void init(FilterConfig filterConfig) throws ServletException {
//获取核心配置对象IIPConfig全限定名
//createJFinalConfig()方法中反射该对象
createJFinalConfig(filterConfig.getInitParameter("configClass"));
// 配置初始化 核心很重要 后续4会讲
jfinal.init(jfinalConfig, filterConfig.getServletContext());
// 获取项目名路径 后面的不重要
String contextPath = filterConfig.getServletContext().getContextPath();
// 获取项目名长度 这两步都是为了获取到的url去除项目名的影响
// jfinal的请求不带项目名
contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0 : contextPath.length());
//常量配置 主要是 是否是开发模式dev 配置
constants = Config.getConstants();
//编码方式
encoding = constants.getEncoding();
// jfinal项目启动后的一个方法 可在自定义Config中重写
jfinalConfig.afterJFinalStart();
//***获取handler对象 怎么获取的?在jfinal.init 有set进去 先不管 详细见5
//handler中的handler方法用于处理请求 可看3
handler = jfinal.getHandler(); // 开始接受请求
}
// 每次请求经过的方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
request.setCharacterEncoding(encoding);
String target = request.getRequestURI();
if (contextPathLength != 0) {
//请求的url(去除了项目名)
target = target.substring(contextPathLength);
}
boolean[] isHandled = {false};
try {
//*核心处理请求的方法
// target:请求的url(去除了项目名)
// request,response:请求响应必须的对象
// isHandled:拦截器是否通过
handler.handle(target, request, response, isHandled);
}
catch (Exception e) {
if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, e);
}
}
if (isHandled[0] == false) {
chain.doFilter(request, response);
}
}
3.ActionHandler 核心方法handle
Action action = actionMapping.getAction(target, urlPara); 这一句可以发现它是根据我们请求的url 获取到一个Action对象 其中包括了主要的 ①对应Controller的Class对象 ②对应方法Method对象 ③所有拦截器对象 。 而actionMapping对象是在过滤器的init中初始化了,后续会讲。可以直接看4
/**
* handle
* 1: Action action = actionMapping.getAction(target)
* 2: new Invocation(...).invoke()
* 3: render(...)
*/
public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
if (target.indexOf('.') != -1) {
return ;
}
isHandled[0] = true;
String[] urlPara = {null};
//获取action对象 其中包括了主要的 ①对应Controller的Class对象 ②对应方法Method对象 ③所有拦截器对象 。
Action action = actionMapping.getAction(target, urlPara);
if (action == null) {
if (log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs));
}
renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render();
return ;
}
Controller controller = null;
try {
//此处反射出对应的Controller对象 这也是为什么我们自定义的TestController要继承jfinal的Controller类 反射出来的对象是Controller接收的 没继承没有多态性质 无法接收
// Controller controller = action.getControllerClass().newInstance();
controller = controllerFactory.getController(action.getControllerClass());
//将action, request, response, urlPara[0] 挂载到Controller的成员变量上 详细请看3.1
controller.init(action, request, response, urlPara[0]);
//开发模式中打印日志
if (devMode) {
if (ActionReporter.isReportAfterInvocation(request)) {
new Invocation(action, controller).invoke();
ActionReporter.report(target, controller, action);
} else {
ActionReporter.report(target, controller, action);
new Invocation(action, controller).invoke();
}
}
else {
//核心 这个对象有没有一点熟悉? 没错就是我们的
//拦截器Interceptor中要实现的拦截方法
//intercept(Invocation inv) 中的形参
//这一句 将action对象和controller对象传入
//invoke方法中执行了拦截器
//invoke方法 详情请看 3.2
new Invocation(action, controller).invoke();
}
//后续即为render方法 渲染视图
Render render = controller.getRender();
if (render instanceof ForwardActionRender) {
String actionUrl = ((ForwardActionRender)render).getActionUrl();
if (target.equals(actionUrl)) {
throw new RuntimeException("The forward action url is the same as before.");
} else {
handle(actionUrl, request, response, isHandled);
}
return ;
}
if (render == null) {
render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName());
}
render.setContext(request, response, action.getViewPath()).render();
}
catch (RenderException e) {
if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, e);
}
}
catch (ActionException e) {
int errorCode = e.getErrorCode();
String msg = null;
if (errorCode == 404) {
msg = "404 Not Found: ";
} else if (errorCode == 401) {
msg = "401 Unauthorized: ";
} else if (errorCode == 403) {
msg = "403 Forbidden: ";
}
if (msg != null) {
if (log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn(msg + (qs == null ? target : target + "?" + qs));
}
} else {
if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, e);
}
}
e.getErrorRender().setContext(request, response, action.getViewPath()).render();
}
catch (Exception e) {
if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, e);
}
renderManager.getRenderFactory().getErrorRender(500).setContext(request, response, action.getViewPath()).render();
} finally {
if (controller != null) {
controller.clear();
}
}
}
3.1 Controller的 init方法
把我们所必需的request,response对象挂载到Controller上
void init(Action action, HttpServletRequest request, HttpServletResponse response, String urlPara) {
this.action = action;
this.request = request;
this.response = response;
this.urlPara = urlPara;
urlParaArray = null;
render = null;
}
3.2 Invocation 中的invoke方法
拦截器核心部分 递归执行所有拦截器的intercept方法 有一个拦截器未执行invoke方法 即action上挂载的Controller方法不会被执行。
public void invoke() {
//inters 为Controller上所有拦截器对象数组,在构造方法上初始化的
//递归执行每一个intercept方法
if (index < inters.length) {
inters[index++].intercept(this);
}
//如果有一个拦截器中的invoke方法未执行 则index++ != inters.length
//即代表不放行 后续的Controller中的方法不能执行。
//拦截器执行顺序也是全局的》类上的》方法上的
else if (index++ == inters.length) { // index++ ensure invoke action only one time
try {
// Invoke the action
if (action != null) {
//调用执行指定Controller上的方法
returnValue = action.getMethod().invoke(target, args);
}
// Invoke the method
else {
// if (!Modifier.isAbstract(method.getModifiers()))
// returnValue = methodProxy.invokeSuper(target, args);
if (useInjectTarget)
returnValue = methodProxy.invoke(target, args);
else
returnValue = methodProxy.invokeSuper(target, args);
}
}
catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
throw t instanceof RuntimeException ? (RuntimeException)t : new RuntimeException(e);
}
catch (RuntimeException e) {
throw e;
}
catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
4 如何生成actionMapping
首先在JFinalFilter过滤器的init方法中调用了jfinal.init()方法,如下。该方法中的initActionMapping()。
void init(JFinalConfig jfinalConfig, ServletContext servletContext) {
//上下文对象
this.servletContext = servletContext;
//获取项目名
this.contextPath = servletContext.getContextPath();
initPathKit();
//此处 提供口子 给用户配置处理器
//底部调用Conifg的configRoute方法
//详细见4.1
Config.configJFinal(jfinalConfig); // start plugin, init log factory and init engine in this method
constants = Config.getConstants();
//初始化url映射Action对象
//详细见4.4
initActionMapping();
initHandler();
initRender();
initOreillyCos();
initTokenManager();
}
4.1 configJFinal()
/*
* Config order: constant, plugin, route, engine, interceptor, handler
*/
static void configJFinal(JFinalConfig jfinalConfig) {
//配置常量
jfinalConfig.configConstant(constants); initLogFactory(); initEngine();
configPluginWithOrder(1, jfinalConfig);
//记不记得我们会重写这个方法?
//将 url controllerClass viewpath (这三个是我们配置的)
//添加到Route对象上 再添加到Routes对象上的 List<Route> routeItemList集合里
//详细见4.2
jfinalConfig.configRoute(routes);
configPluginWithOrder(2, jfinalConfig);
//配置模板引擎
jfinalConfig.configEngine(engine);
configPluginWithOrder(3, jfinalConfig);
//全局拦截器 interceptors 挂载到了Config 上
jfinalConfig.configInterceptor(interceptors);
configPluginWithOrder(4, jfinalConfig);
//配置处理器
jfinalConfig.configHandler(handlers);
configPluginWithOrder(5, jfinalConfig);
}
4.2 Route对象
public static class Route {
//url
private String controllerKey;
//对应的controllerClass
private Class<? extends Controller> controllerClass;
//返回的视图路径 (配置了就可以省略的那个)
private String viewPath;
}
4.3 Routes对象
public abstract class Routes {
private static List<Routes> routesList = new ArrayList<Routes>();
private static Set<String> controllerKeySet = new HashSet<String>();
private String baseViewPath = null;
private List<Route> routeItemList = new ArrayList<Route>();
private List<Interceptor> injectInters = new ArrayList<Interceptor>();
/**
* Add route
* @param controllerKey A key can find controller
* @param controllerClass Controller Class
* @param viewPath View path for this Controller
*/
public Routes add(String controllerKey, Class<? extends Controller> controllerClass, String viewPath) {
routeItemList.add(new Route(controllerKey, controllerClass, viewPath));
return this;
}
}
4.4 initActionMapping()
private void initActionMapping() {
//Config.getRoutes() 返回值是Routes对象 这个对象有我们配置的Route集合 //见4.3
//me.add("/", IndexController.class);
actionMapping = new ActionMapping(Config.getRoutes());
//见4.5 url(Route)映射action对象
actionMapping.buildActionMapping();
Config.getRoutes().clear();
}
4.5 buildActionMapping()
url映射核心方法
protected void buildActionMapping() {
mapping.clear();
//获取到Controller类上 所有形式参数为空的方法名set集合
//所以我们Controller上的方法一定不能有参数
//这里的方法是Controller类上的 不是我们自己要映射Url的方法 需要排除
//详细见4.6
Set<String> excludedMethodName = buildExcludedMethodName();
InterceptorManager interMan = InterceptorManager.me();
//此处 为什么不是直接遍历Route集合 而是Routes集合
//根据getRoutesList方法返回的Routes集合其实是 //Routes上挂载的一个List<Routes> 加 本身Routes
//是给我们提供一种直接添加Routes的方式
for (Routes routes : getRoutesList方法()) {
//所以这里有两层循环
for (Route route : routes.getRouteItemList()) {
Class<? extends Controller> controllerClass = route.getControllerClass();
//扫描拿到类上的拦截器 (加了@Before()注解的)
Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass);
//判断controllerClass是否是直接继承了Controller
//是就不需要获取父类Controller的方法了
//不是则全部要获取
//这里就是如果是直接继承了Controller的能提高效率
boolean sonOfController = (controllerClass.getSuperclass() == Controller.class);
Method[] methods = (sonOfController ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());
for (Method method : methods) {
String methodName = method.getName();
//Controller 中的方法以及形参不为空的方法被排除
if (excludedMethodName.contains(methodName) || method.getParameterTypes().length != 0)
continue ;
//这里非公有方法会被排除
if (sonOfController && !Modifier.isPublic(method.getModifiers()))
continue ;
//这里是将方法上的,routes上的,类上的,全局的拦截器 合并到一起
Interceptor[] actionInters = interMan.buildControllerActionInterceptor(routes.getInterceptors(), controllerInters, controllerClass, method);
String controllerKey = route.getControllerKey();
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 映射了 Controller类
//再加上方法名 就是默认 路径
//***即 url == controllerKey +'/'+methodName 对应到一个action
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);
}
}
4.6 buildExcludedMethodName
protected 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;
}
5 Handler对象
initHandler 可以看到将 actionMapping 挂载到了actionHandler上
private void initHandler() {
ActionHandler actionHandler = Config.getHandlers().getActionHandler();
if (actionHandler == null) {
actionHandler = new ActionHandler();
}
actionHandler.init(actionMapping, constants);
handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
}