• 大家一起写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

  • 相关阅读:
    HandlerMethodArgumentResolver[1]-Controller入参封装
    SpringMVC拦截器
    处理器映射器HandlerMapping
    SpringBoot启动流程
    SpringBoot自动配置原理
    DeferredImportSelector的处理
    Spring的SPI机制【spring.factories】
    Spring 5.3.x源码构建
    分析jvm的cpu,内存,回收问题
    F.A.Q常见问题处理
  • 原文地址:https://www.cnblogs.com/-10086/p/4063763.html
Copyright © 2020-2023  润新知