自己编写一个基于Velocity的MVC框架

公司留了作业(还有一个月毕业),让预习Velocity,在家呆着没意思,反正闲着也是闲着,看了VelocityViewServlet源码,感觉还可以,取其精华去其糟粕,自己写了一个基于Velocity的MVC框架,废话不多说了,直接进入正题。

VelocityActionServlet是整个MVC框架的核心类,拦截所有的Action请求,分发给不同的Action进行处理。
init()方法初始化了系统需要的资源和VelocityEngine。
doProcess()是这个类的核心方法,处理用户请求,获取context数据,获取模板,合成html

package com.zzq.velocity.core;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.collections.ExtendedProperties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
import org.apache.velocity.io.VelocityWriter;

public class VelocityActionServlet extends HttpServlet {

private static Log log = LogFactory.getLog(VelocityActionServlet.class);

/**
* request放入VelocityContext中的key
*/
public static final String REQUEST = "request";

/**
* response放入VelocityContext中的key
*/
public static final String RESPONSE = "response";

public static final String CONTENT_TYPE = "default.contentType";

public static final String OUTPUT_ENCODING = "output.encoding";

/**
* 默认的输出编码
*/
public static final String DEFAULT_OUTPUT_ENCODING = "UTF-8";

/**
* velocity.properties在web.xml文件默认的key
*/
protected static final String INIT_PROPS_KEY =
"org.apache.velocity.properties";

/**
* velocity.properties在默认的路径
*/
protected static final String DEFAULT_PROPERTIES_PATH =
"/WEB-INF/velocity.properties";

/**
* 默认的ContextType
*/
public static final String DEFAULT_CONTENT_TYPE = "text/html";

/**
* 对输出流维护的池
*/
private ObjectPool<VelocityWriter> pool = new ObjectPool<VelocityWriter>(40);

private VelocityEngine velocity = null;

/**
* 例如user.do?method=add -> 会调用UserAction的add方法 相当于DispatchAction功能
*/
private static final String DEFAULT_PARAMETER_METHOD = "method";

private static final String INIT_PARAMETER_KEY = "parameter";

private String parameterMethod;

/**
* 最终默认的ContentType
*/
private String defaultContentType;

private static final String INIT_TEMPLATEPATH_KEY = "templatePath";

private static final String FILE_RESOURCE_LOADER_PATH = "file.resource.loader.path";

public void init(ServletConfig config) throws ServletException {

super.init(config);

/**
* 初始化Velocity引擎
*/
initVelocity(config);

defaultContentType = (String)getVelocityProperty(CONTENT_TYPE, DEFAULT_CONTENT_TYPE);
String encoding = (String)getVelocityProperty(OUTPUT_ENCODING, DEFAULT_OUTPUT_ENCODING);
parameterMethod = findInitParameter(config, INIT_PARAMETER_KEY, DEFAULT_PARAMETER_METHOD);

int index = defaultContentType.lastIndexOf(";");

if(index < 0) {
defaultContentType += "; charset=" + encoding;
}

}

/**
* 初始化Velocity引擎
* @param config
* @throws ServletException
*/
protected void initVelocity(ServletConfig config) throws ServletException {

try {

velocity = new VelocityEngine();

ExtendedProperties p = loadConfiguration(config);

String templatePath = findInitParameter(config, INIT_TEMPLATEPATH_KEY);

templatePath = config.getServletContext().getRealPath("/") + templatePath;

velocity.setProperty(FILE_RESOURCE_LOADER_PATH, templatePath);

velocity.setExtendedProperties(p);

velocity.init();
} catch (Exception e) {
log.error("初始化velocity引擎时出错!");
throw new ServletException(e);
}
}

protected String getVelocityProperty(String key, String defaultValue) {

String value = (String)velocity.getProperty(key);

if(null == value || "".equals(value.trim())) {
value = defaultValue;
}

return value;
}

/**
* 加载velocity.properties配置文件
*/
protected ExtendedProperties loadConfiguration(ServletConfig config) {

String propsFile = findInitParameter(config, INIT_PROPS_KEY);

if(null == propsFile) {
propsFile = DEFAULT_PROPERTIES_PATH;
}

ExtendedProperties properties = new ExtendedProperties();
InputStream inputStream = getServletContext().getResourceAsStream(propsFile);

try {
inputStream = getServletContext().getResourceAsStream(propsFile);

if(null != inputStream) {
properties.load(inputStream);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
throw new RuntimeException("关闭" + propsFile + "文件时出现异常!");
}
}
}

return properties;
}

protected String findInitParameter(ServletConfig config, String key) {

String value = config.getInitParameter(key);

if(null == value || "".equals(value.trim())) {
value = config.getServletContext().getInitParameter(key);
}

return value;
}

protected String findInitParameter(ServletConfig config, String key, String defaultValue) {

String value = config.getInitParameter(key);

if(null == value || "".equals(value.trim())) {
value = config.getServletContext().getInitParameter(key);
}

if(null == value || "".equals(value.trim())) {
value = defaultValue;
}

return value;
}

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

this.doProcess(request, response);
}

protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

this.doProcess(request, response);
}

/**
* 请求处理的核心方法
* @param request
* @param response
*/
protected void doProcess(HttpServletRequest request, HttpServletResponse response) {

Context context = null;

try {
initServletActionContext(request, response);

initActionContext(request);

setContentType(request, response);

Action action = getAction(request);

String templateName = doExecute(action, request, response);

//在Action中调用response.sendRedirect();
if(null == templateName) {
return ;
}

context = createContext(action, request, response);

Template template = handleRequest(templateName, request, response, context);

mergeTemplate(template, context, response);
} catch(Exception e) {
try {
e.printStackTrace(response.getWriter());
response.getWriter().flush();
} catch (IOException e1) {
throw new RuntimeException(e);
}
} finally {
destoryServletActionContext();
destoryActionContext(request);
}
}

/**
* 给ServletActionContext赋值
* @param request
* @param response
*/
protected void initServletActionContext(HttpServletRequest request,
HttpServletResponse response) {
ServletActionContext.setRequest(request);
ServletActionContext.setResponse(response);
}

/**
* 给ActionContext赋值
* @param request
*/
protected void initActionContext(HttpServletRequest request) {
//处理request
Map<String, Object> values = new HashMap<String, Object>();

Enumeration enumeration = request.getAttributeNames();
while(enumeration.hasMoreElements()) {
String key = (String)enumeration.nextElement();
values.put(key, request.getAttribute(key));
}

ActionContext.setRequest(values);

//处理session
values = new HashMap<String, Object>();
HttpSession session = request.getSession();
if(null != session) {
session = request.getSession(true);
}

enumeration = session.getAttributeNames();

while(enumeration.hasMoreElements()) {
String key = (String)enumeration.nextElement();
values.put(key, session.getAttribute(key));
}

ActionContext.setSession(values);

//处理ServletContext
values = new HashMap<String, Object>();
ServletContext servletContext = session.getServletContext();
enumeration = servletContext.getAttributeNames();

while(enumeration.hasMoreElements()) {
String key = (String)enumeration.nextElement();
values.put(key, session.getAttribute(key));
}

ActionContext.setServletContext(values);
}

/**
* 销毁当前线程的ServletActionContext
*/
protected void destoryServletActionContext() {
ServletActionContext.destory();
}

/**
* 销毁当前线程的ActionContext
* @param request
*/
protected void destoryActionContext(HttpServletRequest request) {

Map<String, Object> values = ActionContext.getContext().getRequest();
for(Iterator<String> iter = values.keySet().iterator(); iter.hasNext(); ) {
String key = iter.next();
request.setAttribute(key, values.get(key));
}

values = ActionContext.getContext().getSession();
HttpSession session = request.getSession();
for(Iterator<String> iter = values.keySet().iterator(); iter.hasNext(); ) {
String key = iter.next();
session.setAttribute(key, values.get(key));
}

values = ActionContext.getContext().getServletContext();
ServletContext servletContext = session.getServletContext();
for(Iterator<String> iter = values.keySet().iterator(); iter.hasNext(); ) {
String key = iter.next();
servletContext.setAttribute(key, values.get(key));
}

ActionContext.destory();

}

protected String doExecute(Action action, HttpServletRequest request,
HttpServletResponse response) throws Exception {

String methodName = request.getParameter(parameterMethod);

if(null == methodName || "".equals(methodName.trim())) {
methodName = "execute";
}

Method method = action.getClass().getMethod(methodName);

String templateName = (String)method.invoke(action);

return templateName;
}

protected Context createContext(Action action, HttpServletRequest request,
HttpServletResponse response){

VelocityContext context = new VelocityContext();

context.put(REQUEST, request);
context.put(RESPONSE, response);

Map<String, Object> params = action.getContext();
for(Iterator<String> iter = params.keySet().iterator(); iter.hasNext(); ) {
String key = iter.next();
Object value = params.get(key);
context.put(key, value);
}

return context;
}

private Action getAction(HttpServletRequest request) {
String requestURI = request.getRequestURI();

String actionName = requestURI.substring(requestURI.indexOf('/', 1), requestURI.lastIndexOf('.'));

Action action = (Action)BeanFactory.getBean(actionName);

if(null == action) {
throw new RuntimeException("没有找到路径为:" + actionName + "对应的Action");
}

return action;
}

protected void setContentType(HttpServletRequest request,
HttpServletResponse response) {
response.setContentType(defaultContentType);
}

protected Template handleRequest(String templateName, HttpServletRequest request,
HttpServletResponse response, Context ctx) {

//do something......

Template template = null;

try {
template = velocity.getTemplate(templateName);
} catch (Exception e) {
throw new RuntimeException(e);
}

return template;
}

protected void mergeTemplate(Template template,
Context context,HttpServletResponse response) throws UnsupportedEncodingException, IOException {

VelocityWriter vw = null;
Writer writer = getResponseWriter(response);

try {
vw = pool.get();
if(null == vw) {
vw = new VelocityWriter(writer, 4*1024, true);
} else {
vw.recycle(writer);
}
template.merge(context, writer);
} finally {
if(null != vw) {
vw.flush();
vw.recycle(null);
pool.put(vw);
}
}
}

protected Writer getResponseWriter(HttpServletResponse response)
throws UnsupportedEncodingException, IOException {
Writer writer = null;

try {
writer = response.getWriter();
} catch (IllegalStateException e) {
String encoding = response.getCharacterEncoding();
if (encoding == null) {
encoding = DEFAULT_OUTPUT_ENCODING;
}
writer = new OutputStreamWriter(response.getOutputStream(), encoding);
}

return writer;
}
}


这是一个简单的Bean工程实现,加载类路径下的beans.properties文件(这里完全可以用xml,这都是次要的问题,不是核心问题,等以后有时间了可以继续改进)
beans.properties文件,
路径=Action类,如:
/user=com.velocity.test.action.UserAction

package com.zzq.velocity.core;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

public class BeanFactory {

private static Map<String, String> map = new HashMap<String, String>();

static {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("beans.properties");

try {
Properties prop = new Properties();
prop.load(is);
Iterator iter = prop.keySet().iterator();

while(iter.hasNext()) {
String key = (String)iter.next();
String className = prop.getProperty(key);
map.put(key, className);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("初始化BeanFactory失败", e);
} finally {
try {
if(null != is) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}

public static Object getBean(String id) {

//prototype

String className = map.get(id);
if(null == className) {
return null;
}

Object action = null;
try {
action = Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("创建Action类:" + className + "失败", e);
}

return action;
}
}


ObjectPool对象池的实现,主要是对Writer进行管理。

package com.zzq.velocity.core;

import java.util.ArrayList;
import java.util.List;

/**
* 对象池
* @author zzq
*
*/
public class ObjectPool<T> {

/**
* 默认池大小
*/
public static final int DEFAULT_POOL_SIZE = 10;

private int max;

private int current=-1;

private List<T> pool = null;

public ObjectPool() {

this(DEFAULT_POOL_SIZE);
}

public ObjectPool(int size) {
max = size;
pool = new ArrayList<T>(size);
}

public T get() {
T obj = null;

synchronized (this) {
if(current > -1) {
obj = pool.get(current);
current --;
}
}
return obj;
}

public void put(T obj) {
synchronized (this) {
current ++;
if(current < max) {
pool.add(obj);
return ;
}
current --;
}
}

public int getMax() {
return max;
}

public List<T> getPool() {
return pool;
}

}


VelocityWriter类对Writer进行修饰,加入了Buffer。

package com.zzq.velocity.core;

import java.io.IOException;
import java.io.Writer;

public class VelocityWriter extends Writer {

private int bufferSize;
private boolean autoFlush;

private Writer writer;

private char cb[];
private int next;

private static final int DEFAULT_CHARBUFFER_SIZE = 8 * 1024;

public VelocityWriter(Writer writer) {
this(writer, DEFAULT_CHARBUFFER_SIZE, true);
}

public VelocityWriter(Writer writer, int size, boolean isFlush) {

if(size < 0) {
throw new IllegalArgumentException("Buffer size < 0");
}

this.bufferSize = size;
this.autoFlush = isFlush;
this.writer = writer;

cb = size > 0 ? new char[size] : null;
next = 0;
}

public int getBufferSize() { return bufferSize; }

public boolean isAutoFlush() { return autoFlush; }

private final void flushBuffer() throws IOException {

if(bufferSize == 0) {
return ;
}
if(next == 0) {
return ;
}
writer.write(cb, 0, next);
this.next = 0;
}

public final void clear() {
next = 0;
}

private final void bufferOverflow() throws IOException {

throw new IOException("Buffer over flow!");
}

public final void write(int c) throws IOException {

if(bufferSize == 0) {
writer.write(c);
return ;
}

if(next >= bufferSize) {
if(autoFlush) {
flushBuffer();
} else {
bufferOverflow();
}
}

cb[next++] = (char)c;
}

public final void write(char buf[]) throws IOException {
write(buf, 0, buf.length);
}

public final void write(String s, int off, int len) throws IOException {
write(s.toCharArray(), off, len);
}

public final void write(String s) throws IOException {
write(s, 0, s.length());
}

@Override
public void write(char[] cbuf, int off, int len) throws IOException {

if(bufferSize == 0) {
writer.write(cbuf, off, len);
return ;
}

if (len == 0)
{
return;
}

if (len >= bufferSize)
{
if (autoFlush)
flushBuffer();
else
bufferOverflow();
writer.write(cbuf, off, len);
return;
}

if(len + off > cbuf.length) {
throw new IllegalArgumentException("len + off > cbuf.length");
}

while(len > 0) {
int b = len > bufferSize - next ? bufferSize - next : len;
System.arraycopy(cb, next, cbuf, off, b);
next += b;
if(next >= bufferSize) {
if(autoFlush) {
flushBuffer();
} else {
bufferOverflow();
}
}

off += b;
len -= b;
}
}

public final void recycle(Writer writer) {
this.writer = writer;
clear();
}

@Override
public void close() throws IOException {
if(writer != null) {
flush();
writer.close();
}
}

@Override
public void flush() throws IOException {
flushBuffer();
writer.flush();
}
}


ServletActionContext这个类,挺简单的,一看就能明白,和Struts2的ServletActionContext功能一样。

package com.zzq.velocity.core;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class ServletActionContext {

private static ThreadLocal<HttpServletRequest> reqContext = new ThreadLocal<HttpServletRequest>();

private static ThreadLocal<HttpServletResponse> respContext = new ThreadLocal<HttpServletResponse>();

public static void setRequest(HttpServletRequest request) {
reqContext.set(request);
}

public static void setResponse(HttpServletResponse response) {
respContext.set(response);
}

public static HttpServletRequest getRequset() {
return reqContext.get();
}

public static HttpServletResponse getResponse() {
return respContext.get();
}

public static HttpSession getSession() {
return reqContext.get().getSession();
}

public static ServletContext getServletContext() {
return reqContext.get().getSession().getServletContext();
}

public static void destory() {
reqContext.remove();
respContext.remove();
}
}

这个更不用说了,玩过Struts2的人一看就知道。

package com.zzq.velocity.core;

import java.util.Map;

/**
* 将Servlet API 进行解耦
* @author zzq
*
*/
public class ActionContext {

private static ActionContext instance = new ActionContext();

private static ThreadLocal<Map<String, Object>> request = new ThreadLocal<Map<String, Object>>();

private static ThreadLocal<Map<String, Object>> session = new ThreadLocal<Map<String, Object>>();

private static ThreadLocal<Map<String, Object>> servletContext = new ThreadLocal<Map<String, Object>>();

private ActionContext() {}

public static ActionContext getContext() {
return instance;
}

public static void setRequest(Map<String, Object> value) {
request.set(value);
}

public static void setSession(Map<String, Object> value) {
session.set(value);
}

public static void setServletContext(Map<String, Object> value) {
servletContext.set(value);
}

public Map<String, Object> getSession() {
return session.get();
}

public Map<String, Object> getServletContext() {
return servletContext.get();
}

public Map<String, Object> getRequest() {
return request.get();
}

public static void destory() {
request.remove();
session.remove();
servletContext.remove();
}
}

一个接口,所有的Action类都必须要实现这个接口。

package com.zzq.velocity.core;

import java.util.Map;

public interface Action {

public String execute() throws Exception;

/**
* 获取Velocity需要的Context数据
* @return Context
*/
public Map<String, Object> getContext();
}


最后就是这个web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<servlet>
<servlet-name>VelocityActionServlet</servlet-name>
<servlet-class>com.zzq.velocity.core.VelocityActionServlet</servlet-class>
<init-param>
<param-name>properties</param-name>
<param-value>/WEB-INF/velocity.properties</param-value>
</init-param>
<init-param>
<param-name>templatePath</param-name>
<param-value>/vm</param-value>
</init-param>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>VelocityActionServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
</web-app>


核心的东西就是这些,下面玩一下这个框架。
1、定义Action类

package com.velocity.test.action;

import java.util.HashMap;
import java.util.Map;

import com.zzq.velocity.core.Action;
import com.zzq.velocity.core.ServletActionContext;

public class UserAction implements Action {

private Map<String, Object> map = new HashMap<String, Object>();

public String execute() throws Exception {

return "/login.vm";
}

public String login() {

String username = ServletActionContext.getRequset().getParameter("username");
String password = ServletActionContext.getRequset().getParameter("password");

User user = new User();
user.setUsername(username);
user.setPassword(password);

System.out.println(username);
System.out.println(password);

map.put("user", user);

ServletActionContext.getSession().setAttribute("user", user);

return "/login_success.vm";
}

public String register() {

String username = ServletActionContext.getRequset().getParameter("username");
String password = ServletActionContext.getRequset().getParameter("password");

System.out.println(username);
System.out.println(password);

return "/register_success.vm";
}

public String registerInput() {

return "/register.vm";
}

public Map<String, Object> getContext() {
return map;
}
}


2、在beans.properties加入
/user=com.velocity.test.action.UserAction

3、定义模板,放到WebRoot/vm/下(这个路径可以在web.xml中配)
[quote]<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>用户登录</h2>
<form action="user.html" method="post">
<input type="hidden" name="method" value="login">
用户名:<input name="username"><br>
密码:<input name="password" type="password"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
[/quote]
[quote]<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
登录成功!$user.username<br>
session:$request.session.getAttribute('user').username
</body>
</html>[/quote]
4、访问http://localhost:8080/Veloctiy/user.html 一看就知道

总之:这个就是现在闲着没意思编着玩的,缺少很多功能(如上传,参数类型装换,还有对整个框架的扩展如插件化等等),跟成型的MVC Framework没法发,但是如果有人力、时间、金钱,相信我们国人也会编出相当完美优雅的框架。
还有一个月就毕业了,回望过去,还是很向往自己的大学生活,面对现实只能勇敢的去面对。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值