陆陆续续用闲暇时间写的自用 MVC 框架,本来想利用十一期间整理整理代码放出来,结果一直拖延症终于缓慢地完工了(然后这帖又写了两天……)
先放地址:https://github.com/cloudeecn/mocha-mvc 欢迎大家clone下来吐嘈,如果觉得博主写得好的话,欢迎Star/Fork
运行环境要求:
运行Example
clone工程,用eclipse/IDEA/whatever你喜欢的IDE导入maven工程,用支持Servlet3.0的容器启动里面的example子工程就OK啦
在web.xml中有使用Spring
和Guice
作为DI的两套配置,默认打开的是用Guice
作DI的,也可以打开Spring
来试一下。
Spring
的配置在example工程中src/main/resources/works/cirno/mocha/example/spring/applicationContext.xml
Guice
的配置在
works.cirno.mocha.example.guice
包下
运行起来后,可以尝试一下 /example/parameter 和 /example/+(随便你起个名字) 两个地址来感受一下Bean绑定上传文件和RESTful地址的支持
快速上手创建自己的工程
git clone
Mocha工程,maven clean install
安装到本地库中
创建一个web maven工程,假设命名为mocha-example,加上如下依赖:
<dependency>
<groupId>works.cirno.mocha</groupId>
<artifactId>mvc-core</artifactId>
<version>0.1-SNAPSHOT</version>
</dependency>
- 创建MVCConfigurator的子类
package example;
import ...
public class HelloConfigurator extends MVCConfigurator {
public void configure(){
serve("/hello/${name}").with(HelloController.class, "hello");
}
}
- 创建Controller类
package example;
import ...
public class HelloController {
void hello(PrintWriter out, String name){
out.print("Hello " + name);
}
}
- web.xml中加入如下配置:
<filter>
<filter-name>dispatcher</filter-name>
<filter-class>works.cirno.mocha.DispatcherFilter</filter-class>
<init-param>
<param-name>configurator</param-name>
<param-value>example.HelloController</param-value>
</init-param>
</filter>
完成. 用Servlet容器运行这个工程,访问
http://127.0.0.1:8080/mocha-example/hello/user
输出为
更复杂的配置请参考工程中的Example工程和下面的章节:
(GitHub上的详细文档还没写好,请耐心等待orz)
略微详细的配置介绍
用过Guice
的同学应该对这种方式很熟悉,创建一个works.cirno.mocha.MVCConfigurator
的子类,并在public void configure()
方法中进行配置。
配置路由项
serve({路径}[, "GET/POST/PUT..."])
会产生一个路由项,利用方法链的方式可以进一步对匹配这个路径的访问进行配置
配置响应
serve({路径}[, "GET/POST/PUT..."]).with({controller}, {method});
中的with方法给路由项配置响应的Controller
和其中的方法method
Controller返回
Controller方法可以支持返回一下几个类型
Integer:直接用response.sendError返回返回码
String:作为view的名字寻找对应的jsp进行渲染
View对象:根据view的名字寻找对应的jsp进行渲染,view对象中有attribute(key, value)方法可以给request的attribute提供值
null或者方法返回类型为void:不做后续处理
配置view返回
可以通过forward方法执行响应view名对应的jsp文件(forward方法返回一个支持to方法的接口,其中的to方法执行JSP的位置)
可以配置多个
serve({路径}[, "GET/POST/PUT..."]).with({controller}, {method})
.forward("view名").to("JSP位置")
.forward("view名").to("JSP位置")
...
;
支持Restful风格的api
serve("/parameter/\\+${userId}").with(ParameterController.class, "user").forward("success").to("/WEB-INF/jsp/parameter-rest.jsp");
serve("/parameter/\\+${userId}", "POST").with(ParameterController.class, "userPost");
serve("/parameter/\\+${userId}.json").with(ParameterController.class, "userJson");
会匹配/parameter/+后面的任何内容,并作为userId参数和候选
目前有两种写法:
JDK7终于支持了的namedGroup:写法如下:(?<name>pattern)
,会匹配pattern正则并且作为name参数的候选
另外支持一种比较基本的${name}
写法,会被翻译成(?<name>.*?)
,最小匹配接下来的内容作为name参数的候选
参数获取
Controller方法中的参数目前会根据以下的规则获取:
按类型匹配
- 类型为HttpServletRequest的参数会给予request对象
- 类型为HttpServletResponse的参数会给予response对象
- 类型为ServletOutputStream或OutputStream的参数会给予response.getOutputStream()的结果
- 类型为PrintWriter或Writer的参数会给予response.getWriter()的结果
如果是multipart请求,如果参数类型是InputStream或者Reader,根据参数名匹配上传文件的内容
根据参数的名称,按照url中的变量 - multipart中解析到的表单域 - request.getParameter
的顺序获取字符串,并尝试通过系统中的PropertyEditor进行类型转换,如果转换成功将转换结果给予参数
将这个类型用无参构造函数实例化,如果成功作为JavaBean遍历其中的属性,根据规则1-4获取参数名.属性名
的参数值进行填充,如果能获取到至少一个,将这个实例给予参数
以上都不匹配返回参数的默认值(基本类型给false/0,对象类型给null)
TODO
- 写文档(README.md居然只写了一半的内容!)
- 写文档(注释简直是没有)
- 写文档(Wiki也没有!)
- 修改路径匹配的规则,现在是用正则匹配的,对带正则元字符的的路径不友好
- 优化获取参数的性能……从request中填充一个5个属性的Bean居然要16毫秒简直不能忍
- 重构一下View的结构,现在的View机制是在Renderer之下的,结构很诡异
- 用一个比较优雅的方式封装参数获取和转换的配置(目前是写死的规则……)
- 继续把之前的连载填上
随后我会慢慢在GitHub的README.md和Wiki中补充上各种文档,请大家期待;)