你们一块儿写mvc(三)_结束

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

咱们用eclipse新建一个web项目。而后web.xml以下java

<?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的自动扫描装备功能。用的好像也是咱们用到的写法。git

而后就开始写这个servletweb

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

init方法呢,主要做用就是装配扫描beantomcat

代码片断以下:安全

@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。也就是扫描包的路径。mvc

在各类跳转过程当中,咱们须要一个java bean来装一些咱们须要的参数。app

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

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

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

相关文章
相关标签/搜索