写在前面
之前使用阿里的webx框架,整体设计思想很不错,但拿到别的公司直接使用总感觉不妥,因为webx比较庞大,功能繁琐、配置复杂,所以本人在业余时间里基于 spring-mvc 重写了一套 mini-webx,去其糟粕,取其精华。
框架的本质是为了提高开发效率,本框架实现得非常轻巧,各位同学可以根据实际的需求作适当的改进,以下附上使用教程与源码(依赖jar包请自行下载 ),欢迎拍砖。
1、概览
web 层参照 webx设计。(熟悉阿里webx的同学能轻松驾驭)
传统mvc框架,后台必须要写一个 controller或 action 类,然后由该类去渲染一个jsp、ftl、vm页面,此方式的弊端有以下几点:
1、 程序开发之前,前端开发无法介入
2、 在没有约束的情况下,页面的url制定规则比较乱,由请求的url去定位到后台action类比较费时。
3、 对于json请求的输出,通常都是手式组装格式,较繁琐。
设计出发点
1) 页面驱动
让前端开发、设计人员来驱动后台开发人员,来完成页面。
在后台开发人员介入前,前端人员就可以可以页面开发(不需要后台类的支持),保证开发进度上的并行。
2) 约定胜于配置
框架中减少了很多配置工作,只需要按照约定,即可轻松完成相应开发。
3)兼容 srping-mvc 原有特性,如拦截器配置、类型转换配置等
<!-- 拦截器配置 -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
</list>
</property>
</bean>
<!-- 类型转换配置 -->
<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.zpd.common.mvc.converter.String2DateConverter"init-method="init">
<property name="formats">
<list>
<value>yyyy-MM-dd HH:mm:ss</value>
<value>yyyy-MM-dd</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
Web层结构
screen 代表页面的主体
controll 代表页面的片段,通常为公共调用
rpc json格式请求
注意事项
1、所有web层处理对象均为单例,为保证线程安全,不要使用类成员变量
2、url必须全部为小写,对应的类名无限制
3、在本框架去掉了所有 @RequestMapping 注解
2、页面开发 (screen)
步骤:
1、 编写vm页面
页面存放路径为 WEB-INF/view/screen,对应访问路径只需将 vm改成htm 即可
例1:
vm 路径:/mode_name/sub_folder/page.vm
访问路径:http://loalhost:8080/mode_name/sub_folder/page.htm
例2:
vm 路径:/demo/samplelist.vm
访问路径:http://loalhost:8080/demo/samplelist.htm
2、 编写后台类
在对应的screen 包下编写页面处理类,vm 页面与 screen类路径映射规则:
类名:com.zpd.module.moduleName.web.screen.subpath.PageName
Uri: /modulename/ subpath /pagename.vm
示例:
类名:com.zpd.module.demo.web.screen. SampleList
Uri: /demo/samplelist.vm
类名:com.zpd.module.member.web.screen. MemberCenter
Uri: /member/membercenter.vm
类名:com.zpd.module.deal.web.screen.order. Index
Uri: /deal / order /index.vm
Screen处理类示例:
@Controller
public class SampleList {
@Autowired
private TestSampleManagermanager;
@PagingConfig(defaultParams = {"keyword" })
public void execute(SearchVo vo, HttpServletRequestrequest) {
List<TestSample> list =manager.selectByParamVo(vo);
request.setAttribute("list",list);
}
}
注:
1、 vm对应的screen类是可有可无的。如果该类存在,会先执行execute方法,再渲染页面。
2、 screen类头部必须加@controller,为防止 beanName重复,需指定beanName,如 @Controller(“beanName”)
3、 screen 类必须有 execute方法,参数不限
4、 screen类均为单例,为保证线程安全,不要用使用类成员变量
5、 根路径页面的访问对应的package是com.zpd.module.facade.web.screen,如/index.htm对应 com.zpd.module.facade.web.screen.Index。facade不是模块名,用于标识根目录。
3、json请求 (rpc)
json请求通常用于异步接口开发,浏览器ajax 请求等。
Json请求不需要渲染页面。
步骤:
1、 在 xxx.web.rpc包下新建 java类。
2、 完成所需方法,按业务需求return指定的数据结构。
3、 按照 url 规则进行访问
Rpcurl映射规则
由于通常一个类里能写多个请求,所以与 screen略有区别,rpc请求以方法为单位,后缀名为.json。
com.zpd.module.moduleName.web.rpc.XxxxRpc.methodA()
/modulename/xxxx/methoda.json
示例:
Rpc 类如下:
package com.zpd.module.demo.web.rpc;
@Controller
public class TestSampleRpc {
@Autowired
private TestSampleManagermanager;
public TestSample save_update(TestSamplerecord, Integer[]uploadImgIds) {
manager.saveOrUpdate(record,uploadImgIds);
returnrecord;
}
public TestSample detail(Integerid) {
TestSample record = manager.selectById(id);
returnrecord;
}
@PagingConfig
public List<TestSample> list(SearchVovo) {
List<TestSample> list =manager.selectByParamVo(vo);
returnlist;
}
public String delete(Integer[]ids) {
manager.deleteByIds(ids);
return"deletesuccess";
}
}
该类对应以下 json请求:
/demo/testsample/list.json
/demo/testsample/delete.json
/demo/testsample/save_update.json
/demo/testsample/detail.json
Rpc返回的数据格式
{
success: true/false,
data: Object, (rpc方法中返回什么类型,这里就是什么类型,对象、数组、集合…)
errorCode: “404/500”,
errorMsg:“ error message”
}
注:
1、 Rpc请求只需返回基础对象即可,不允许手动构造json 对象输出
2、 与screen一样,类头部需加@Controller(“beanName”),beanName 视情况可有可无。
3、 在方法中不要手动处理异常信息,如果出错会由框架层统一处理,输出 success:false格式的json
4、页面片段 (controll)
开发过程
Controll为页面片段, 的开发规则基本与 screen 一样:
vm存储路径为 /WEB-INF/views/controll
class 包路径为com.zpd.module.moduleName.web.controll
class类示例:
@Controller
public class Login_Msg implements ControllInterface {
public void execute(Map<String, Object> context) {
// 获取页面中传递的参数
context.get("xxxx");
// 存入需在要 controll页面显示的数据
context.put("date",new Date());
}
}
说明:
1、 对应的 class可有可无
2、 与scrren类不同的是,controll类需实现 ControllInterface接口
3、 Controll一般只用于查询数据,渲染页面,由于调用频率通常较高,请尽量使用AO层对象,调用缓存
4、 ControllInterface接口的方法中的 context对象是用于向页面输出数据的。
controll的调用
在任何vm页面中均可按照以下方式调用
$controll.include('/member/login_msg.vm')
传递参数采用的键值对的方式,调用方式如下:
$controll.include('/member/login_msg.vm','key1', $value1, 'key2', 'value2')
相当于在 controll类执行了以下方法:
context.put(key1, value1);
context.put(key2, value2);
注:为保障页面结构的清晰,尽量不要在controll中嵌套调用其它 controll
5、url 重写
需求描述:
去掉url后的参数,保持 url 的简洁,利于 seo
案例1:
原始路径:/goods/list.htm?type=1
目标映射路径:/goods/list/1.htm
案例2:
原始路径:/goods/list.htm?type=1&key=abc
目标映射路径:/goods/list/1_abc.htm
实现步骤:
1、 编写 screen 类,在 execute方法上加上注解 @RewritePath
2、 在参数里加上对应注解 @PathVariable,保持相应变量名与 @RewritePath中的一致
例:
Java类:
package com.zpd.module.demo.web.screen;
@Controller
public class Test {
@RewritePath("{typeId}_{key}")
public void execute(@PathVariable("typeId") Integer type,
@PathVariable("key") String key) {
}
}
匹配规则说明:
正常匹配:
/demo/test/101_apple.htm
typeId: 101, key:apple
非正常规则匹配:
/demo/test/103_add_pple.htm
typeId: 103, key:add_pple
类型转换异常:
/demo/test/103dd_add_pple.htm
typeId: null, key:add_pple
以上是本框架的基本使用说明。