今天1.0版本已经有一个雏形了,以后会加入更多的东西。
先介绍下我的思路吧。
第一步、是velocity引擎,我使用一个取路径的类来寻找已经设计好的模版文档,将其中的模版标签替换,将其作为响应返回给浏览器。
代码:
/**CurrentappHelper.java*/
public static File getTemplatedir() {
return new File(guessAppdir(), "view");
}
/**VelocityEnginee.java**/
public class VelocityEnginee{
public VelocityEnginee() {
File templatedir = CurrentappHelper.getTemplatedir();
System.out.println(templatedir);
RuntimeSingleton.setProperty(
RuntimeConstants.FILE_RESOURCE_LOADER_PATH, templatedir
.getAbsolutePath());
try {
RuntimeSingleton.init();
} catch (Exception e) {
logger.error("Error", e);
}
}
VelocityContext context = new VelocityContext();
/**
* 根据当前请求生成 模板数据内容,如果不生成会显示原数据
*
* @param req
* @return
*/
public void putContext(String infoname,Object info) {
context.put(infoname, info);
}
/**
* 根据视图名生成响应后的页面
* @param viewname
* @param context
* @param writer
* @throws ParseErrorException
* @throws ResourceNotFoundException
* @throws Exception
*/
public void show(String viewname,PrintWriter writer) throws Exception {
Template tp = RuntimeSingleton.getTemplate(viewname+".html", "gbk");
tp.merge(context, writer);
}
}
第二步、修改web.xml文件将其所有的sevlet路径都指向我自己定义的类。
<web-app>
<servlet>
<servlet-name>vatana</servlet-name>
<servlet-class>com.inca.vatana.VatanaServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>vatana</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
第三步。建立自己的类VatanaSevlet类。类中调用VatanaController的call()作为控制器指定模型并取得一个字符串作为视图索引。
调用VelocityEnginee的show()方法呈现视图给浏览器。
public class VatanaServlet extends HttpServlet {
Category logger = Category.getInstance(VatanaServlet.class);
/**
* doGet=doPost 合并url的get和post请求
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=gbk");
try {
//调用模型
String viewname = VatanaController.getInstance().call(req);
//呈现试图
VelocityEnginee.getInstance().show(viewname, resp.getWriter());
} catch (Exception e) {
//监控异常
e.printStackTrace();
try {
//返回错误页面
ErrorModel s = new ErrorModel(req, e);
s.excute();
VelocityEnginee.getInstance().show("error",resp.getWriter());
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
第四步、控制器VatanaController。
/**
* 控制器
* @author vatana
*
*/
public class VatanaController {
Category logger = Category.getInstance(VatanaController.class);
private static VatanaController ins;
private VatanaController(){
}
public static VatanaController getInstance(){
if(ins==null){
ins = new VatanaController();
}
return ins;
}
public String call(HttpServletRequest req) throws Exception {
//取得请求的信息
String actionClass = getActionClass(req);
String actionURL = getActionUrl(req);
String method = getMethod(req);
// 指向类
Class<?> cls = Class.forName("com.vatana.model."
+ actionURL + "." + actionClass );
// 创建类参数
Class<?> partypes[] = new Class[1];
partypes[0] = HttpServletRequest.class;
// 根据构造函数创建对象
Constructor ct = cls.getConstructor(partypes);
Object arglist[] = new Object[1];
arglist[0] = req;
Object demo = ct.newInstance(arglist);
// 调用函数
Method meth = cls.getMethod(method);
// 取得返回值 将要跳转到哪个试图
String viewname = (String) meth.invoke(demo);
return actionURL+"/"+viewname;
}
//取得方法名
private String getMethod(HttpServletRequest req) {
String method="excute";
return method;
}
//取得动作名
private String getActionClass(HttpServletRequest req) {
String actionClass = "HelloWorld";
return actionClass;
}
//取得动作路径
private String getActionUrl(HttpServletRequest req) {
String actionURL = "helloworld";
return actionURL;
}
}
可以看到该类并没有完成。其中URL分析部分还需完善。
其主要作用是分析url,变换成相应的动作指令,使用java的反射机制进行调用。这个思路来源于rails,在这里我想使用约定而不是xml配置文件。把一些东西限制定死了,可以集中人的注意力在处理逻辑上。
假如一个url是:http://localhost/vatana/action/method?param1=1¶m2=2
那么作为控制器会自动寻找类Action的method方法,如果不写方法,默认为excute,username和password参数及其值,可以在params中找到。默认action是必须写的,如果不写,按照主页处理,将会定位到index页面,在这里我用的velocity模版的后缀是html.这样可以直接在浏览器中预览,方便设计。
第五步、一个模版类需要继承BaseModel类。然后创建一个自己的方法(或者重写excute方法)即可。
public abstract class ModelBase {
protected HttpSession session;//取得session
protected HashMap<String, String> params;//取得参数
protected HttpServletRequest req;//当前的请求对象
public ModelBase(HttpServletRequest req) {
this.req = req;
this.session = req.getSession();
this.params = getParams(req);
}
/*默认的执行函数excute*/
abstract public String excute();
/**注册模版内容**/
protected void register(String infoname, Object info) {
VelocityEnginee.getInstance().putContext(infoname, info);
}
private HashMap<String, String> getParams(HttpServletRequest req) {
HashMap<String, String> params = new HashMap<String, String>();
Enumeration<String> enname = req.getParameterNames();
while (enname.hasMoreElements()) {
String key = enname.nextElement();
String value = req.getParameter(key);
params.put(key, value);
}
return params;
}
这样在继承类里就只需要重写excute类即可。在此,我有一点犹豫,excute方法是否应定义成抽象函数,
一个具体的模型类
public class HelloWorld extends ModelBase{
public HelloWorld(HttpServletRequest req) {
super(req);
}
/*重写excute方法*/
@Override
public String excute() {
//一个操作员对象
Operater user = new Operater();
user.setUserid("1");
user.setUsername("vatana");
//注册对象到模版
register("user", user);
return "index";
}
}
注意上个类中的注册对象到模版,velocity是支持对自定义对象进行操作的。上面这个模型对应的视图模版可以这样写
<html>
<head>
<meta http-equiv=Content-Type content="text/html;charset=gbk">
<title>${user.username}</title>
</head>
<body>
你好 ${user.username},你的ID是 ${user.userid}!
</body>
</html>
最终呈现的结果会是
你好 vatana,你的ID是1!
框架的大致流程就是这样子,使用vatana开发应用,只需要创建一个模版页,一个model类,就足够了。
当然这个框架还有很多需要扩充的地方,我认为url方面还有更多东西来做,url的创建,链接的管理,我打算用一个类管理超链接和form标签,这个类将会通过velocity模版展现在页面上,不需要开发人员每次都要遍历所有的模版去找要修改的内容。
这是第一版的代码,我在这里把model的包路径给定死了com.vatana.model,这是我自己用的一个路径,以后我打算做一个让此路径可以定制。但是每个项目只能做一次定制,我在这里把给模型类同名的package,这样不会导致所有的类型都集中在一个package下显的混乱。
为了方便开发,我打算写一段自动生成文件的程序,只需要一点点的配置,就可以得到我想要的一切--程序,视图模版。不需修改就能使用。
同时在这里做一下展望吧,在将来框架会更完善,集成jQuery,hibernate等更多的元素让每一个java程序员能够更敏捷开发web程序,