本文对Jfinal的控制器源码做以下分析。
PS:控制器是所有请求跳转的基础,本文就Jfinal控制器的继承关系及初始化的方法做出解释说明。
啰嗦下:所有的请求和响应都是都是通过web容器封装,我们主要跟踪JFinal的Controller是如何被初始化、如何处理业务逻辑,后续需要研究容器的加载机制,进而晋升到高级甚至技术专家的程度。
源码分析总结:慢慢随着源码的深入,渐渐的对源码不会那那么的反感,刚开始读一定要静下心来,一步一脚印,别人用好几年甚至几十年写的代码,对于一个初中级开发来说,很难理解的其设计的核心,我们只有一边又一边的反复读取,才能理解作者设计的思想,无形之中提高自己的代码水平和代码架构;总之,天道酬勤、持之以恒。
- JFinal中Controller的继承关系
JFinal中自定义的控制器通过继承JFinal提供的Controller(抽象的)即可,该类中有四个重要成员变量需要我们关注public abstract class Controller {
//Web请求
private HttpServletRequest request;
//响应 private HttpServletResponse response;
//Url参数
private String urlPara;
//Url参数数组 private String[] urlParaArray;
}PS:后续我们重点跟踪request、response、urlPara是如何被初始化,我们猜想:这些变量都是Jfinal框架本身从web容器中获取。
留个疑问:自定义的Controller是 单例吗? - 自定义Controller的写法
如上所示,自定义的Controller通过继承抽象类Controller即可,实例代码如下
public class BlogController extends Controller {
static BlogService service = new BlogService();
public void index() {
setAttr("blogPage", service.paginate(getParaToInt(0, 1), 10));
render("blog.html");
}
}
将Controller绑定给容器
通过JFinalConfig实现的ConfigRoute方式将自定义的Controller绑定给容器,供请求时候调用,如下
/**
* 配置路由
*/
public void configRoute(Routes me) {
me.add("/", IndexController.class, "/index"); // 第三个参数为该Controller的视图存放路径
me.add("/blog", BlogController.class); // 第三个参数省略时默认与第一个参数值相同,在此即为 "/blog"
}
PS:为何不弄个注解,让JFinal自动扫描自定义的控制器呢?按照常规思想,实现注解,省略了配置代码多好
如果不弄注解:我将所有的Controller能够统一管理,能够快速找到我自定义的控制器有哪些,鄙人猜测,这应该是作者不整个Controller上的注解的原因吧,但我觉得这样的注解还是有必要的,毕竟每个人的使用方法不一样,框架本身应该要满足庞大客户群体的需求,该注解可自定添加,重新JFinal加载Controller的逻辑代码即可
- Web过滤器
通过重新Servlet.Filter的过滤方法,获取请求的Request和Response和相关参数,交由用户自定义的控制去实现相关的逻辑。
过滤器的代码如下public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
//从过滤器中获取请求
HttpServletRequest request = (HttpServletRequest)req;
//从过滤器中获取响应 HttpServletResponse response = (HttpServletResponse)res;
//设置请求的编码 request.setCharacterEncoding(encoding);
//获取请求的URI
String target = request.getRequestURI();
if (contextPathLength != 0) {
//从URI中去除根目录 target = target.substring(contextPathLength);
}
boolean[] isHandled = {false};
try {
//处理器这块需要重点关注,经过分析处理器的实现为:ActionHandler 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);
}
}接下里我们分析ActionHandler中handle方法的逻辑
public final void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
if (target.indexOf('.') != -1) {
return ;
}
isHandled[0] = true;
String[] urlPara = {null};
//基于URI和参数获取请求的action Action action = actionMapping.getAction(target, urlPara);
if (action == null) {
if (log.isWarnEnabled()) {
String qs = request.getQueryString();
//Action为null的情况下,记录查询字符串的日志 log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs));
}
renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render();
return ;
}
try {
//从Action中获取当前请求对应的Controller的对象,这个地方每次都是实例化一次,所以JFinal的Controller不是单例 Controller controller = action.getControllerClass().newInstance();
//controller调用初始化方法,赋值请求、响应和url参数 controller.init(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 {
//反射调用,仔细跟 new Invocation(action, controller).invoke();
}
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();
}
}以上代码通过反射代理的方式调用了Controller中的方法,最终renderManager实现跳转,Render是Jfinal对应web响应的一个封装,这种设计思想在实际开发中很有必要,简洁代码,使用方便,下文给出Render实现类
PS:Render这块是对响应进行了封装,具体更多的东西还需要自行分析和总机。
天道酬勤,努力每一天,也欢迎您的指正,共同进步,请联系:xieyang@e6yun.com/cnxieyang@163.com