刚接触Jfinal,后面继续整理;
Jfinal源码解析
http://blog.csdn.net/soul_code/article/category/6337939
JFinal初始化过程浅析
http://blog.csdn.net/huilangeliuxin/article/details/37961385
JFinal实现原理
http://blog.csdn.net/huilangeliuxin/article/details/37961385
JFinal实现原理
一切都从web.xml开始说起:
当服务器初始化的时候会初始化web.xml里面的相关配置信息;
下面我们来看一个重要的过滤器配置:JFinalFilter。下面是相关的配置信息。
jfinal
com.jfinal.core.JFinalFilter
true
configClass
net.dreamlu.common.WebConfig
这就意味着所有的请求都要经过JFinalFiter过滤了
从中我们可以看到在web.xml之中配置了一个名为JFinalFilter的过滤器。下面我们来看看JFinalFiter的源码。
/**
* JFinal framework filter
*/
public final class JFinalFilter implements Filter {
private Handler handler;
private String encoding;
private JFinalConfig jfinalConfig;
private Constants constants;
private static final JFinal jfinal = JFinal.me();
private static Log log;
private int contextPathLength;
// 系统在初始化Servlet的时候自动调用该方法;
public void init(FilterConfig filterConfig) throws ServletException {
// 创建JFianlConfig对象
createJFinalConfig(filterConfig.getInitParameter("configClass"));
// 所有的初始化操作都在这里进行了,以后会再次提到这里!
if (jfinal.init(jfinalConfig, filterConfig.getServletContext()) == false)
throw new RuntimeException("JFinal init error!");
handler = jfinal.getHandler();
constants = Config.getConstants();
encoding = constants.getEncoding();
// afterJFinalStart 方法
jfinalConfig.afterJFinalStart();
// 得到项目根路径
String contextPath = filterConfig.getServletContext().getContextPath();
contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0 : contextPath.length());
}
// request和response的作用不用过多介绍了。这个FilterChain的左右主要是用来连续调用下一个Filter时候使用的,
// 下面给出了FilterChain的作用介绍.
/**
* A FilterChain is an object provided by the servlet container to the developer
* giving a view into the invocation chain of a filtered request for a resource. Filters
* use the FilterChain to invoke the next filter in the chain, or if the calling filter
* is the last filter in the chain, to invoke the resource at the end of the chain.
*
* @see Filter
* @since Servlet 2.3
**/
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)
// 得到其ServletPath以及相关的参数
target = target.substring(contextPathLength);
boolean[] isHandled = {false};
try {
// isHandler用来判断该Target是否应该被相应的handler处理;
// 这是整个Filter的最核心方法
handler.handle(target, request, response, isHandled);
}
catch (Exception e) {
if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, e);
}
}
// 该Target没被处理,进入下一个handler;
if (isHandled[0] == false)
chain.doFilter(request, response);
}
public void destroy() {
// beforeJFinalStop方法
jfinalConfig.beforeJFinalStop();
// 停止插件
jfinal.stopPlugins();
}
// 创建JFianlConfig对象, 读取app配置文件
private void createJFinalConfig(String configClass) {
if (configClass == null)
throw new RuntimeException("Please set configClass parameter of JFinalFilter in web.xml");
Object temp = null;
try {
temp = Class.forName(configClass).newInstance();
} catch (Exception e) {
throw new RuntimeException("Can not create instance of class: " + configClass, e);
}
if (temp instanceof JFinalConfig)
jfinalConfig = (JFinalConfig)temp;
else
throw new RuntimeException("Can not create instance of class: " + configClass + ". Please check the config in web.xml");
}
static void initLog() {
log = Log.getLog(JFinalFilter.class);
}
}
让我们重点看看这个handler的由来。
首先由handler = jfinal.getHandler();知道这个handler是由jfinal对象得来的,现在让我们看看jfinal的部分源码:
/**
* JFinal
*/
public final class JFinal {
private Constants constants;
private ActionMapping actionMapping;
private Handler handler; // 定义了其他成员变量;
private ServletContext servletContext;
private String contextPath = "";
private static IServer server;
private static final JFinal me = new JFinal();
private JFinal() {
}
public static JFinal me() {
return me;
}
// 初始化JFinal时候调用的方法(在上面已经提到过这一点)
boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
this.servletContext = servletContext;
this.contextPath = servletContext.getContextPath();
initPathUtil();
// 启动插件,初始化日志工厂;
Config.configJFinal(jfinalConfig); // start plugin and init log factory in this method
constants = Config.getConstants();
initActionMapping();
initHandler();
initRender();
initOreillyCos();
initTokenManager();
return true;
}
private void initTokenManager() {
ITokenCache tokenCache = constants.getTokenCache();
if (tokenCache != null)
TokenManager.init(tokenCache);
}
private void initHandler() {
Handler actionHandler = new ActionHandler(actionMapping, constants);
handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
}
private void initOreillyCos() {
OreillyCos.init(constants.getBaseUploadPath(), constants.getMaxPostSize(), constants.getEncoding());
}
private void initPathUtil() {
String path = servletContext.getRealPath("/");
PathKit.setWebRootPath(path);
}
private void initRender() {
RenderFactory.me().init(constants, servletContext);
}
private void initActionMapping() {
actionMapping = new ActionMapping(Config.getRoutes(), Config.getInterceptors());
actionMapping.buildActionMapping();
Config.getRoutes().clear();
}
// 停止插件;从后往前遍历的
void stopPlugins() {
List plugins = Config.getPlugins().getPluginList();
if (plugins != null) {
for (int i=plugins.size()-1; i >= 0; i--) { // stop plugins
boolean success = false;
try {
success = plugins.get(i).stop();
}
catch (Exception e) {
success = false;
LogKit.error(e.getMessage(), e);
}
if (!success) {
System.err.println("Plugin stop error: " + plugins.get(i).getClass().getName());
}
}
}
}
Handler getHandler() {
return handler;
}
public Constants getConstants() {
return Config.getConstants();
}
public String getContextPath() {
return contextPath;
}
public ServletContext getServletContext() {
return this.servletContext;
}
public Action getAction(String url, String[] urlPara) {
return actionMapping.getAction(url, urlPara);
}
public List getAllActionKeys() {
return actionMapping.getAllActionKeys();
}
// start方法,会在main方法调用;
public static void start() {
server = ServerFactory.getServer();
server.start();
}
public static void start(String webAppDir, int port, String context, int scanIntervalSeconds) {
server = ServerFactory.getServer(webAppDir, port, context, scanIntervalSeconds);
server.start();
}
public static void stop() {
server.stop();
}
/**
* Run JFinal Server with Debug Configurations or Run Configurations in Eclipse JavaEE
* args example: WebRoot 80 / 5
*/
public static void main(String[] args) {
if (args == null || args.length == 0) {
server = ServerFactory.getServer();
server.start();
}
else {
String webAppDir = args[0];
int port = Integer.parseInt(args[1]);
String context = args[2];
int scanIntervalSeconds = Integer.parseInt(args[3]);
server = ServerFactory.getServer(webAppDir, port, context, scanIntervalSeconds);
server.start();
}
}
}
由这里我们知道handler是由HandlerFactory的getHandler方法得来的。
让我们再次看看HandlerFactory的部分源码以探个究竟:
/**
* HandlerFactory.
*/
public class HandlerFactory {
private HandlerFactory() {
}
/**
* Build handler chain
*
* 显然这里返回的是一个actionHandler为首handler chain。
*/
@SuppressWarnings("deprecation")
public static Handler getHandler(List handlerList, Handler actionHandler) {
Handler result = actionHandler;
for (int i=handlerList.size()-1; i>=0; i--) {
Handler temp = handlerList.get(i);
temp.next = result;
temp.nextHandler = result;
result = temp;
}
return result;
}
}
显然这里返回的是一个actionHandler为首handler chain。
让我们再来看看这个:
private void initHandler() {
Handler actionHandler = new ActionHandler(actionMapping, constants);
handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
}
此处传进去并不是简单的handler,而是他的子类ActionHandler,并且传进去了有两个参数,一个是ActionMapping类的变量,一个是constants。对于后者将就是一些常量的设置所以不进行过多介绍。让我们先看看ActionMapping之后再来看这个ActionHandler。
/**
* ActionMapping
*/
final class ActionMapping {
private static final String SLASH = "/";
private Routes routes;
// private Interceptors interceptors;
private final Map mapping = new HashMap();
ActionMapping(Routes routes, Interceptors interceptors) {
this.routes = routes;
// this.interceptors = interceptors;
}
private Set buildExcludedMethodName() {
Set excludedMethodName = new HashSet();
Method[] methods = Controller.class.getMethods();
for (Method m : methods) {
if (m.getParameterTypes().length == 0)
excludedMethodName.add(m.getName());
}
return excludedMethodName;
}
void buildActionMapping() {
mapping.clear();
Set excludedMethodName = buildExcludedMethodName();
InterceptorManager interMan = InterceptorManager.me();
for (Entry> 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);
}
private static final String buildMsg(String actionKey, Class extends Controller> controllerClass, Method method) {
StringBuilder sb = new StringBuilder("The action \"")
.append(controllerClass.getName()).append(".")
.append(method.getName()).append("()\" can not be mapped, ")
.append("actionKey \"").append(actionKey).append("\" is already in use.");
String msg = sb.toString();
System.err.println("\nException: " + msg);
return msg;
}
/**
* Support four types of url
* 1: http://abc.com/controllerKey ---> 00
* 2: http://abc.com/controllerKey/para ---> 01
* 3: http://abc.com/controllerKey/method ---> 10
* 4: http://abc.com/controllerKey/method/para ---> 11
* The controllerKey can also contains "/"
* Example: http://abc.com/uvw/xyz/method/para
*/
Action getAction(String url, String[] urlPara) {
Action action = mapping.get(url);
if (action != null) {
return action;
}
// --------
int i = url.lastIndexOf(SLASH);
if (i != -1) {
action = mapping.get(url.substring(0, i));
urlPara[0] = url.substring(i + 1);
}
return action;
}
List getAllActionKeys() {
List allActionKeys = new ArrayList(mapping.keySet());
Collections.sort(allActionKeys);
return allActionKeys;
}
}
在ActionMapping中定义了一个路由(routes)和一个Interceptors,这个routes类里面主要的核心是两个Map
,内容如下(截取了部分源码过来):
//每一个访问路径(controllerKey)都对应有一个相应的controller,并作为一对Entry放到map中
private final Map> map = new HashMap>();
//每一个访问路径(controllerKey)都对应一个在项目中的实际存放路径(WEB-INF/index.jsp等等),并作为一对Entry放到viewPathMap中
private final Map viewPathMap = new HashMap();
因此我们知道这个ActionHandler就是处理一些关于ActionMapping中对应的ControllerKey与Controller.class的事情。
所以现在既然这些都已经清楚了,我们可以看看ActionHandler的庐山真面目了。
在ActionHandler中我们可以看到这样一行注释:
/**
* handle
* 1: Action action = actionMapping.getAction(target)
* 2: new Invocation(...).invoke()
* 3: render(...)
*/
这就解释了handle方法需要做的事情了,首先是根据ActionMapping获得相应的Action,然后利用反射进行方法的调用,最后把结果映射到相应的页面上去。这就是核心的三个步骤了,
接下来让我们详细的读一下这个源码:
/**
* ActionHandler
*/
final class ActionHandler extends Handler {
private final boolean devMode;
private final ActionMapping actionMapping;
private static final RenderFactory renderFactory = RenderFactory.me();
private static final Log log = Log.getLog(ActionHandler.class);
public ActionHandler(ActionMapping actionMapping, Constants constants) {
this.actionMapping = actionMapping;
this.devMode = constants.getDevMode();
}
/**
* 这里进行了核心的handle方法描述:
* 这里的target为以下格式:http://localhost:8080/ContextPath/ControllerKey/MethodName/parameters
*
*/
/**
* handle
* 1: Action action = actionMapping.getAction(target)
* 2: new Invocation(...).invoke()
* 3: render(...)
* 首先是根据ActionMapping获得相应的Action,然后利用反射进行方法的调用,最后把结果映射到相应的页面上去;
*/
public final void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
if (target.indexOf('.') != -1) { // .love,就过滤了;
return ;
}
isHandled[0] = true;
String[] urlPara = {null};
Action action = actionMapping.getAction(target, urlPara); // 1.根据ActionMapping获得相应的Action;
if (action == null) {
if (log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs));
}
renderFactory.getErrorRender(404).setContext(request, response).render();
return ;
}
try {
Controller controller = action.getControllerClass().newInstance(); // Action所在的控制器;Controller
controller.init(request, response, urlPara[0]); // Controller初始化,配置request, response
if (devMode) {
if (ActionReporter.isReportAfterInvocation(request)) {
new Invocation(action, controller).invoke(); // 利用反射进行方法的调用;
ActionReporter.report(controller, action);
} else {
ActionReporter.report(controller, action);
new Invocation(action, controller).invoke(); // 利用反射进行方法的调用;
}
}
else {
new Invocation(action, controller).invoke(); // 2. 然后利用反射进行方法的调用;
}
Render render = controller.getRender(); // 3. 结果映射到相应的页面上;
if (render instanceof ActionRender) {
String actionUrl = ((ActionRender)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 = renderFactory.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();
if (errorCode == 404 && log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("404 Not Found: " + (qs == null ? target : target + "?" + qs));
}
else if (errorCode == 401 && log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("401 Unauthorized: " + (qs == null ? target : target + "?" + qs));
}
else if (errorCode == 403 && log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("403 Forbidden: " + (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 (Throwable t) {
if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, t);
}
renderFactory.getErrorRender(500).setContext(request, response, action.getViewPath()).render();
}
}
}
到这里,我们简单了看了一下JFinal的实现原理。