首页 > 代码库 > 大家一起写mvc(三)_结束

大家一起写mvc(三)_结束

上一篇介绍到要写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(三)_结束