上一篇介绍到要写mvc的所用的核心技术,这一篇我们就开始真正的开始写mvc,其实就是把昨天写过的代码进行一些组装就可以了。

我们用eclipse新建一个web项目。然后web.xml如下

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>kis</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 配置action拦截器 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>com.keepitsimple.core.DispatcherServlet</servlet-class>
<init-param>
<param-name>scan_package</param-name>
<param-value>com.mvc</param-value>
</init-param>
</servlet> <servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<!-- 配置action拦截器 -->
</web-app>

在web.xml里我们配置一个servlet拦截器。拦截所有的action请求。然后init-param里配置一下action所在路径,我们要进行自动扫描。这个功能就类似与spring的自动扫描装备功能。用的好像也是我们用到的写法。

然后就开始写这个servlet

在这个servlet里我们实现doGet,doPost方法,还有init方法。

init方法呢,主要作用就是装配扫描bean

代码片段如下:

@Override
public void init(final ServletConfig config) throws ServletException
{
// TODO Auto-generated method stub
super.init(config);
String scan_package = config.getInitParameter("scan_package");
DispatcherServletHelper.init(scan_package);
}

可以看到,这里这里通过config.getinitParameter("scan_packge")读取web.xml中的initparam。也就是扫描包的路径。

在各种跳转过程中,我们需要一个java bean来装一些我们需要的参数。

package com.keepitsimple.core;

public class ActionContext{
private String URL; //调用的url,也就是actionname
private String methodName; //调用的方法名称
private Class<?> cla; //调用方法的类名
private Object action;//类的实例
private String result;//返回结果
private Class<?>[] paramsType;// 调用方法的参数类型
private String[] actionParamsName;//调用的方法参数的名称 //getter and setter

然后我们定义一下自定义注解,让方法只扫描我们注解了的类。

package com.keepitsimple.core;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented()
public @interface ActionAnnotate
{
String ActionName() default "";
String Result() default "";
}

然后我们调用Helper的init方法。init方法主要就是把上面这个类填充。初始化装载所有的action。

package com.keepitsimple.core;

import java.io.File;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map; import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo; public final class DispatcherServletHelper { public static Map<String, ServletListenerBean> urls = null; // public DispatcherServletHelper()
// {
// getURLs();
// } public static void init(String scan_package)
{
if (null == scan_package || scan_package.length() <= 0)
System.out.println("正在扫描classpath....");
urls = new HashMap<String, ServletListenerBean>();
String result = DispatcherServletHelper.class.getResource("/").toString().replace("file:/", "").replace("%20", "");
try
{
assembleBean(result, result.replace("/", "\\"), scan_package);
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} } /**
* 扫描该路径下 所有的class文件 then 装配bean
*
* @param realPath
* @param root
* 真实路径 E:\\GitHome\\kis\\build\\classes\\
* @param scan_package
* 扫描的包
* @return
* @throws Exception
* @throws
*/
public static String assembleBean(String realPath, String root, String scan_package) throws Exception
{
File file = new File(realPath);
if (file.isDirectory())
{
for (File f : file.listFiles())
{
if (f.isDirectory())
{
assembleBean(f.getAbsolutePath(), root, scan_package);
} else
{
String classPath = f.getAbsolutePath().replace("\\", ".");
int len = classPath.length();
if (len > 6 && classPath.endsWith(".class"))
{
if(null!=scan_package&&scan_package.length()>0&&classPath.indexOf("classes."+scan_package)>=0){
classPath = classPath.substring(8+classPath.indexOf("classes."+scan_package));
}else{
continue;
}
String className = classPath.substring(0, classPath.length() - 6);
Class<?> clazz = Class.forName(className);
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods)
{
if (method.isAnnotationPresent(com.keepitsimple.core.ActionAnnotate.class))
{
com.keepitsimple.core.ActionAnnotate actionAnno = method.getAnnotation(com.keepitsimple.core.ActionAnnotate.class);
if (urls.containsKey(actionAnno.ActionName()))
{
throw new RuntimeException("重复路径" + clazz + ":" + actionAnno.ActionName());
} else
{
ActionContext bean = new ActionContext ();
bean.setCla(clazz);
bean.setMethodName(method.getName());
bean.setURL(actionAnno.ActionName());
bean.setResult(actionAnno.Result());
bean.setAction(clazz.newInstance());
bean.setParamsType(method.getParameterTypes());
bean.setActionParamsName(Utils.getParamNames(clazz, method.getName()));
urls.put(actionAnno.ActionName(), bean);
}
}
}
}
}
}
}
return "";
} }

上面这段代码就是整个的helper类。通过扫描文件然后装配bean。

现在这个mvc的初始化就完成了。这个里面有不少问题。希望大家能够发现,并且尝试改一下。(注1)

doGet方法我们就直接调用doPost来处理

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

doPost方法

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
request.setCharacterEncoding("utf-8");
String path = request.getServletPath();// 获取请求action /addUser.action
path = path.substring(1);// addUser.action
if (DispatcherServletHelper.urls.containsKey(path))
{
ActionContext bean = DispatcherServletHelper.urls.get(path);
Class<?> cla = bean.getCla();
Object actionObject = bean.getAction();
if (bean.getParamsType() != null)
{
BeanUtils beanUtils = new BeanUtils();
Object[] params = beanUtils.getMethodParam(request, bean.getParamsType(),bean.getActionParamsName());//这里就是封装jsp传过来的参数
System.out.println("action方法名:" + bean.getMethodName());
System.out.println("方法参数属性:" + bean.getParamsType());
System.out.println("action实体类:" + actionObject);
System.out.println("action方法参数:" + params);
try
{
cla.getMethod(bean.getMethodName(), bean.getParamsType()).invoke(actionObject, params);//反射调用action方法
} catch (IllegalArgumentException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
} if (bean.getResult() != null)
{
request.getRequestDispatcher(bean.getResult()).forward(request, response);
}
}
}

下面我们看看怎么来封装方法所需要的参数。

package com.keepitsimple.core;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry; import javax.servlet.http.HttpServletRequest; public class BeanUtils<T> { private static Map<Class<?>, Class<?>> classMap = new HashMap<Class<?>, Class<?>>();
static
{
classMap.put(int.class, Integer.class);
classMap.put(short.class, Short.class);
classMap.put(long.class, Long.class);
classMap.put(float.class, Float.class);
classMap.put(double.class, Double.class);
classMap.put(char.class, Character.class);
classMap.put(boolean.class, Boolean.class);
classMap.put(byte.class, Byte.class);
classMap.put(Integer.class, Integer.class);
classMap.put(Short.class, Short.class);
classMap.put(Long.class, Long.class);
classMap.put(Float.class, Float.class);
classMap.put(Double.class, Double.class);
classMap.put(Character.class, Character.class);
classMap.put(Boolean.class, Boolean.class);
classMap.put(Byte.class, Byte.class);
classMap.put(String.class, String.class);
} public Object[] getMethodParam(HttpServletRequest request, Class<?>[] paramsType, String[] actionParamsName)
{
Object[] params = new Object[actionParamsName.length];
for (int i = 0; i < actionParamsName.length; i++)
{
Map<String, String[]> requestMap = request.getParameterMap();
Iterator<Entry<String, String[]>> it = requestMap.entrySet().iterator();
try
{
Object object = paramsType[i].newInstance();
while (it.hasNext())
{
System.out.println(i);
if (isBasicType(paramsType[i]))
{
Entry<String, String[]> entry = it.next();
String value = "";
for (String s : entry.getValue())
{
value += s;
}
if (entry.getKey().equals(actionParamsName[i]))
{
params[i] = basicType(paramsType[i], value); }
} else
{
Field[] fields = paramsType[i].getDeclaredFields();
Entry<String, String[]> entry = it.next();
String paramName;
String value = "";
for (String s : entry.getValue())
{
value += s;
}
if (entry.getKey().indexOf(".") > 0 && entry.getKey().split("\\.")[0].equals(actionParamsName[i]))
{
for (Field field : fields)
{
if (field.getName().equals(entry.getKey().split("\\.")[1]))
{
paramName = entry.getKey().split("\\.")[1];
String methodName = "set" + toFirstLetterUpperCase(paramName);
paramsType[i].getMethod(methodName, field.getType()).invoke(object, value);
}
}
params[i] = object;
}
}
}
} catch (InstantiationException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return params;
} private boolean isBasicType(Class clazz)
{
if (clazz.equals(int.class) || clazz.equals(Integer.class))
{
return true;
} else if (clazz.equals(short.class) || clazz.equals(Short.class))
{
return true;
} else if (clazz.equals(long.class) || clazz.equals(Long.class))
{
return true;
} else if (clazz.equals(float.class) || clazz.equals(Float.class))
{
return true;
} else if (clazz.equals(double.class) || clazz.equals(Double.class))
{
return true;
} else if (clazz.equals(char.class) || clazz.equals(Character.class))
{
return true;
} else if (clazz.equals(boolean.class) || clazz.equals(Boolean.class))
{
return true;
} else if (clazz.equals(byte.class) || clazz.equals(Byte.class))
{
return true;
} else if (clazz.equals(String.class))
{
return true;
} else
{
return false;
}
}
/**
* 基础数据绑定
* @param class1
* @param value
* @return
*/
private Object basicType(Class<?> class1, String value)
{
if (isBasicType(class1))
{
Class<?> newClass = classMap.get(class1);
if (newClass.equals(Integer.class))
{
return Integer.parseInt(value);
} else if (newClass.equals(Short.class))
{
return Short.parseShort(value);
} else if (newClass.equals(Long.class))
{
return Long.parseLong(value);
} else if (newClass.equals(Float.class))
{
return Float.parseFloat(value);
} else if (newClass.equals(Double.class))
{
return Double.parseDouble(value);
} else if (newClass.equals(Character.class))
{
return value.toCharArray()[0];
} else if (newClass.equals(Boolean.class))
{
return Boolean.parseBoolean(value);
} else if (newClass.equals(Byte.class))
{
return Byte.parseByte(value);
} else
{
return value;
}
} else if (class1.equals(java.util.Date.class))
{
try
{
if (value.indexOf(":") == -1)
{
return new SimpleDateFormat("yyyy-MM-dd").parse(value);
} else
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value);
} catch (Exception e)
{
e.printStackTrace();
return null;
}
} else
{
return class1.cast(value);
}
}
/**
* 转换String的第一个字母
* @param s
* @return
*/
public String toFirstLetterUpperCase(String s)
{
if (s.length() == 1)
{
return s.toUpperCase();
} else if (s == null || s.length() == 0)
{
return "";
} else
{
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
} }

上面这段基本都是昨天的代码。利用反射来进行封装,这里面基础数据只做了简单的封装。还不是很完整,

关于getMethodParam()方法,你们可以借助于commons-beanutils.jar包来实现,用这个方法比较简单。

但是这里为了加强大家对反射的理解跟应用,我就采用了反射来做。

刚才我的注1那里,主要的问题是,这个ActionContext是否是线程安全,是否还能改装?还有一个问题是 如果 一个action要有多种跳转结果怎么办?

至此,我们就实现了一个最简单的mvc功能。现在把他添加到tomcat里。就可以运行自己的mvc了。

这mvc系列到这就结束了,剩下的就是一些细节的处理。

这个mvc我也在写 也在一直完善。

上面的代码,大家可以在我的git里check出来。并且实现了action多个跳转结果。希望大家自己想想怎么实现。自己去实现,如果不能实现在check out我的代码。

https://git.oschina.net/sum/kis.git

关于git工具。大家可以看我的上一篇博文,里面有介绍GIT工具。

这里面大家有不明白的可以给我留言,一定会积极回复。

谢谢大家

The End

大家一起写mvc(三)_结束的更多相关文章

  1. mvc 三个 之间 肮脏的交易

    就当个小零食一样写. MVC 是 Model-View-Controller 的缩写,Model代表的是应用的业务逻辑(通过 JavaBean,EJB 组件实现),View 是应用的表示层(由 JSP ...

  2. 手写MVC框架(一)-再出发

    背景 前段时间把之前写的DAO框架(手写DAO框架(一)-从“1”开始)整理了一下,重构了一版.整理过程中看以前写的代码,只是为了了解实现,只是为了实现,代码写的有点粗糙.既然已经整理了DAO框架,索 ...

  3. 手写MVC框架(二)-代码实现和使用示例

    --------上一篇:手写MVC框架(一)-再出发----- 背景 书接上文,之前整理了实现MVC框架需要写哪些东西.这周粗看了一下,感觉也没多少工作量,所以就计划一天时间来完成.周末的时间,哪会那 ...

  4. 七天学会ASP.NET MVC (三)——ASP.Net MVC 数据处理

    第三天我们将学习Asp.Net中数据处理功能,了解数据访问层,EF,以及EF中常用的代码实现方式,创建数据访问层和数据入口,处理Post数据,以及数据验证等功能. 系列文章 七天学会ASP.NET M ...

  5. 60行以内写mvc

    标题党.几天前看到一个30行写mvc的文章,东施效颦,也动手写了个60行的,功能上略微扩充一些,记录下来,后面有时间可以继续优化. mvc其实是一个观察者模式.view来监听model,所以当mode ...

  6. 大家一起写mvc(二)

    上一篇已经看了,我想大家都明白了mvc的原理,今天我们来说一下要写自己mvc框架必须要会的技术. mvc的目录是这样的 src目录是我们核心的mvc代码.这个代码明天讲,今天主要讲的代码都在test目 ...

  7. C#_02.13_基础三_.NET类基础

    C#_02.13_基础三_.NET类基础 一.类概述: 类是一个能存储数据和功能并执行代码的数据结构,包含数据成员和函数成员.(有什么和能够干什么) 运行中的程序是一组相互作用的对象的集合. 二.为类 ...

  8. 七天学会ASP.NET MVC (三)——ASP.Net MVC 数据处理 【转】

    http://www.cnblogs.com/powertoolsteam/p/MVC_three.html 第三天我们将学习Asp.Net中数据处理功能,了解数据访问层,EF,以及EF中常用的代码实 ...

  9. JavaWeb-RESTful(三)_使用SpringMVC开发RESTful_下

    JavaWeb-RESTful(一)_RESTful初认识 传送门 JavaWeb-RESTful(二)_使用SpringMVC开发RESTful_上 传送门 JavaWeb-RESTful(三)_使 ...

随机推荐

  1. js完美转换阿拉伯数字为数字大写(原创)

    啥都不说,直接上代码: //阿拉伯数字转换为简写汉字 function Arabia_To_SimplifiedChinese(Num) { for (i = Num.length - 1; i &g ...

  2. strace命令介绍(转)

    原文链接:http://www.cnblogs.com/ggjucheng/archive/2012/01/08/2316692.html 简介 strace常用来跟踪进程执行时的系统调用和所接收的信 ...

  3. Delphi inline编译器优化问题

    function Test():Integer; inline; var P:Pointer; begin FreeMem(P); Result := AtomicIncrement(__gr); / ...

  4. Win7中不能调试windows service

    多年前玩过一次windows service,觉得挺简单的. 这次工作要维护产品中的windows service,发现不是那么简单,vs附加调试器的窗体中无法找到windows service进程. ...

  5. com.apache.dc.query.Query所属包名apache-common-sid.jar

    com.apache.dc.query.Query所属包名apache-common-sid.jar 首先这个类是基于HQL的,好多方法里面要传String clzz, 刚开始我真不知道这个参数传什么 ...

  6. Response、Request、QueryString,repeater添加,修改,删除数据

    内置对象: Response对象:响应请求,Response对象用于动态响应客户端请示,控制发送给用户的信息,并将动态生成响应.Response.Write("<script>a ...

  7. marquee标签属性详解(跑马灯文字效果)

    请大家先看下面这段代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http: ...

  8. linux下如何查找需要的文件后并删除

    1.首先查找指定目录下的文件,默认为当前目录 使用命令:find . -name 'a.txt' 会得到当前目录下所有包括子孙目录下的所有后缀为txt的文件 2.查找后删除 使用命令:find . - ...

  9. log4j2自定义输出线程环境信息

    在配置日志的输出格式的时候,我们可以按照内置的规则输出日志,但是有时候需要及时输出我们自定义的信息,这时需要借助ThreadContext类. ThreadContext类类似于MDC和NDC,它是一 ...

  10. Service的两种用法及其生命周期

    先来一点基础知识: Service 是android的四大组件之一,与Activity同属于一个级别,它是运行在后台进行服务的组件(例如在后台播放的音乐,播放音乐的同时并不影响其他操作).Servic ...