发现struts2要配的东西还不少,但对于一般的小项目 ,其实没有必要用到那么多功能。特别struts2和现在的好多云平台(BAE,GAE)都不是很好兼容。所以自己写的了一个MVC的东东代替STRTUS2,其实也是自己用习惯了。一般小项目主要用到还是STRUTS ACTION相关的功能。
先说MVC的一个流程吧:
1, 初始化Action相关的配置。(只要做一次就好了)
2, 截获相应的请求并根据配置初始化Action
3, 把对应的Request里的内容填充到Action。
4. 执行Action。
5, 根据Action的返回的结果填充Request并跳转。
1, 初始化Action相关的配置。(只要做一次就好了)
Struts2 conversion很好用,所以这个一定要仿,其实就是扫到相应包里的Class,再把他们做成Action.因为后面很多工作都要用到反射,所以把第一次反射后拿到的方法缓存起来。动手吧:
初始化配,因为还WEB应用,所以在web.xml里面用listener加载 相应配置:
<listener> <listener-class> com.demo.config.LifecycleInit </listener-class> </listener>
在这个类里扫描ACTION的包并加到配置类里去:
Set<Class<?>> classes = Utils.getClasses(Constants.ACTION_PACKAGE);
for (Class c : classes) {
MyConfig.getMyConfig().addConfigByClass(c);
}
getClasses方法不多说了,可以看源代码,就是扫描到包里所有的类。
MyConfig是一个单例,因为配置类只要一份就OK了。addConfigByClass里会做更细的配置。
if (StringUtils.endsWith(clazz.getName(), Constants.ACTION_SUFFIX)) {
ActionConfig actionConfig = ActionConfig.buildByClass(clazz);
if (actionConfigs.get(actionConfig.getUrlName()) != null)
throw new RuntimeException("duplicate action class found" + clazz.getName());
actionConfigs.put(actionConfig.getUrlName().toLowerCase(), actionConfig);
}
ActionConfig.buildByClassu将创建具体的Action配置类。
Method[] methods = clazz.getDeclaredMethods();
//缓存方法
for (Method method : methods) {
String methodName = method.getName();
if (StringUtils.startsWith(methodName, "get")) {//缓存数据方法
if (!StringUtils.endsWith(methodName,"Dao") ) {
String proNameTmp = StringUtils.removeStart(methodName,"get" );
String proName = proNameTmp.substring(0,1).toLowerCase()+proNameTmp.substring(1);
this.getMethod.put(proName, method);
}
} else { //缓存Action方法
this.methodMap.put(method.getName().toLowerCase(), method);
}
}
到这里配置就完成了。
2, 截获相应的请求并根据配置初始化Action
还是学Struts用filter来做url截获吧,自己写个ActionFilter
<filter> <filter-name>actionFilter</filter-name> <filter-class>com.demo.utils.ActionFilter</filter-class> </filter> <filter-mapping> <filter-name>actionFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
接下来就是这个filter里会根据URL和之前的配置类组装Action.对了Spring还是很好用的,所以里面还是用到了Spring
//根据名子得到Action
MyConfig myConfig = MyConfig.getMyConfig();
ActionConfig actionConfig = myConfig.getActionConfigByName(actionName);
//从SPRING里拿到Action bean
ServletContext servletContext = request.getSession().getServletContext();
BasicAction actionClazz = (BasicAction) servletfindBean(servletContext, actionName.toLowerCase() + Constants.ACTION_SUFFIX);
//初始化Action 的上下文
actionClazz.setActionConfig(actionConfig);
actionClazz.setCurMethodName(methodName);
actionClazz.setRequest(request);
actionClazz.setResponse(response);
//调用Action dispatch方法
actionClazz.dispatchUrl();
3, 把对应的Request里的内容填充到Action。
这里自己还是想偷下懒,用到ognl类里的表达式,这样可以很快的request里的数据自动装载到Action里面去
//云平台一般都要把SecurityManager设置成NULL
OgnlRuntime.setSecurityManager(null);
//因为属性是NULL的时候我们要自动构建对应的类
OgnlRuntime.setNullHandler(Object.class, new BasicAction.OnglNullHandler());
Map<String, Object> parMap = request.getParameterMap();
for (String name : parMap.keySet()) {
Ognl.setValue(name, this, parMap.get(name));
}
4. 执行Action。
这个简单了:之前就找在filter里找到了Action方法,这里动态调用一下就OK了。
Method curMethod = actionConfig.getMethodByName(curMethodName);
String result = (String) curMethod.invoke(this);
5, 根据Action的返回的结果填充Request并跳转。
最后就要把相应的属性放回到request里面去。还记得之前缓存过属性对应的方法,这里正好用得上:
for (String key : actionConfig.getGetMethod().keySet()) {
Object value = actionConfig.getGetMethod().get(key).invoke(this);
if (value != null) {
request.setAttribute(key, actionConfig.getGetMethod().get(key).invoke(this));
}
}
还差一步就是跳到对应的地方,里面可自己再定义更多的跳法,里面主要有仿TILES,redirect,和直接到JSP。
if (StringUtils.indexOf(result, Constants.ACTION_RESULT_SPLIT) > -1) {
String[] results = StringUtils.split(result, Constants.ACTION_RESULT_SPLIT);
if (results.length == 1) { //跳到默认模板
request.setAttribute("contentPageJsp", results[0]);
request.getRequestDispatcher(StringUtils.removeEnd(Constants.ACTION_RESULT_DEFAULT, Constants.ACTION_RESULT_SPLIT)).forward(request, response);
} else {
if (StringUtils.removeEnd(Constants.ACTION_RESULT_REDIRECT, Constants.ACTION_RESULT_SPLIT).equals(results[0])) { //redirect
response.sendRedirect(results[1]);
} else { //跳到指定模板
request.setAttribute("contentPageJsp", results[1]);
request.getRequestDispatcher(results[0]).forward(request, response);
}
}
} else { //跳到JSP
request.getRequestDispatcher(result).forward(request, response);
}
再说一下我的JSP里如果是TILES的怎么写吧。其实很简单写INCLUDE就好了,如有必要全写成变量也OK,
<div id="together">
<div id="ggw_header" class="ggw_tile"><jsp:include page="/jsp/layout/header.jsp" /> </div>
<div id="ggw_userheader" class="ggw_tile" style="z-index: 5;">
<jsp:include page="/jsp/layout/userheader.jsp" />
</div>
<div id="ggw_content" class="ggw_tile" style="z-index: 0;"><jsp:include page="${contentPageJsp}"/></div>
<div id="ggw_footer_transition" class="ggw_tile"></div>
</div>
<div id="ggw_footer"><jsp:include page="/jsp/layout/footer.jsp"/></div>
这里也可年到我用的JSTL EL去显示相量,用自己的MVC感觉也还OK,很多东西自己好控制了,不用为了struts2的BUG再烦了,有问题自己修。
用这个写了例子用baidu app engine:http://yijushequ.duapp.com/