JFinal Controller层 源码解读

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
image

/**
 * 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);
}
6. Render 对象
7. 对request对象 方法的封装
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值