zoukankan      html  css  js  c++  java
  • springmvc入门及模拟springmvc

    springmvc入门

    1.添加依赖

    <dependencies>
        <!--springmvc-->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>5.1.9.RELEASE</version>
        </dependency>
      </dependencies>

    2.springmvc.xml配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.0.xsd ">
    
        <!-- 下面是配置扫描包的位置,包名为com.ework.upms.server,也就是说,我们的试图解析器应该放在com.ework.upms.server包下. -->
        <context:component-scan base-package="com.lusai"/>
    
        <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!-- 前缀,我们的视图文件应该放到/WEB-INF/views/目录下,这里我们需要在WEB-INF下面创建view文件夹 -->
            <property name="prefix" value="/WEB-INF/views/" />
            <!-- 设置后缀为.jsp -->
            <property name="suffix" value=".jsp" />
        </bean>
    
    </beans>

    3.web.xml

    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
    
      <!-- Spring的配置-->
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:springmvc.xml</param-value>
      </context-param>
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    
      <!-- springMVC的核心控制器 -->
      <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath*:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>/</url-pattern>
      </servlet-mapping>
    
    </web-app>

    4.控制层

    package com.lusai;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class IndexController {
    
        @RequestMapping("/hello")
        @ResponseBody
        public String hello() {
            return "hello";
        }
    }

    5.测试http://localhost:8080/springmvcDemo/hello

    模拟springmvc

    手写springMvc框架思路:

    1、配置web.xml,加载自定义的DispatcherServlet。

    2、初始化阶段,在DispatcherServlet类中,实现下面几个步骤:

    • 加载配置类。
    • 扫描当前项目下的所有文件。
    • 拿到扫描到的类,通过反射机制,实例化。并且放到ioc容器中。
    • 初始化path与方法的映射。
    • 获取请求传入的参数并处理参数通过初始化好的handlerMapping中拿出url对应的方法名,反射调用。

    3、运行阶段,每一次请求将会调用doGet或doPost方法,它会根据url请求去HandlerMapping中匹配到对应的Method,然后利用反射机制调用Controller中的url对应的方法,并得到结果返回。

    开始模拟

    1.修改maven依赖,注释springmvc依赖,添加servlet和dom4j依赖

    <!--springmvc-->
        <!--<dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>5.1.9.RELEASE</version>
        </dependency>-->
        <!--dom4j-->
        <dependency>
          <groupId>dom4j</groupId>
          <artifactId>dom4j</artifactId>
          <version>1.6.1</version>
        </dependency>
        <!--servlet-->
        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
          <scope>provided</scope>
        </dependency>

    2.修改web.xml

    <servlet>
          <servlet-name>DispatcherServlet</servlet-name>
          <servlet-class>com.zzw.cn.springmvc.dispathcer.DispatcherServlet</servlet-class>
        </servlet>
        <servlet-mapping>
          <servlet-name>DispatcherServlet</servlet-name>
          <url-pattern>/</url-pattern>
        </servlet-mapping>

    自定义DispatcherServlet

    package com.zzw.cn.springmvc.dispathcer;
    
    import com.zzw.cn.springmvc.annoation.AnController;
    import com.zzw.cn.springmvc.annoation.AnRequestMapping;
    import com.zzw.cn.utils.ClassUtils;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * @author Simple
     * @date 14:34 2019/8/26
     * @description 手写springmvc框架流程
     * <p>
     * 思路:自定义DispatcherServlet
     * 1.包扫描获取包下面所有的类
     * 2.初始化包下面所有的类
     * 3.初始化HandlerMapping方法,将url和方法对应上
     * 4.实现HttpServlet 重写dopost方法
     */
    
    public class DispatcherServlet extends HttpServlet {
    
        //springmvc 容器存放bean
        private ConcurrentHashMap<String, Object> mvcBeans = new ConcurrentHashMap<>();
        private ConcurrentHashMap<String, Object> mvcBeanUrl = new ConcurrentHashMap<>();
        private ConcurrentHashMap<String, String> mvcMethodUrl = new ConcurrentHashMap<>();
    
    
        @Override
        public void init(ServletConfig config) {
            String packagePath = "com.zzw.cn.springmvc";
            //1.进行报扫描获取当前包下面所有的类
            List<Class<?>> classes = comscanPackage(packagePath);
            try {
                //2.初始化springmvcbean
                initSpringMvcBean(classes);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //3.将请求地址和方法进行映射
            initHandMapping(mvcBeans);
        }
    
    
        public List<Class<?>> comscanPackage(String packagePath) {
            List<Class<?>> classes = ClassUtils.getClasses(packagePath);
            return classes;
        }
    
        /**
         * 初始化sprignbean
         *
         * @param classes
         * @throws Exception
         */
        public void initSpringMvcBean(List<Class<?>> classes) throws Exception {
            if (classes.size() == 0 || null == classes) {
                throw new Exception("包扫描后的classes为null");
            }
    
            for (Class<?> aClass : classes) {
                //获取被自定义注解的controller将其初始化到自定义sprignmvc容器中
                AnController declaredAnnotation = aClass.getDeclaredAnnotation(AnController.class);
                if (declaredAnnotation != null) {
                    //获取类的名字
                    String beanid = lowerFirstCapse(aClass.getSimpleName());
                    //获取对象
                    Object beanObj = aClass.newInstance();
                    //放入sprign容器
                    mvcBeans.put(beanid, beanObj);
                }
            }
    
        }
    
        /**
         * 初始化HandlerMapping方法
         *
         * @param mvcBeans
         */
        public void initHandMapping(ConcurrentHashMap<String, Object> mvcBeans) {
            //遍历springmvc 获取注入的对象值
            for (Map.Entry<String, Object> entry : mvcBeans.entrySet()) {
                Object objValue = entry.getValue();
                Class<?> aClass = objValue.getClass();
                //获取当前类 判断是否有自定义的requestMapping注解
                String mappingUrl = null;
                AnRequestMapping anRequestMapping = aClass.getDeclaredAnnotation(AnRequestMapping.class);
                if (anRequestMapping != null) {
                    mappingUrl = anRequestMapping.value();
                }
                //获取当前类所有方法,判断方法上是否有注解
                Method[] declaredMethods = aClass.getDeclaredMethods();
                for (Method method : declaredMethods) {
                    AnRequestMapping methodDeclaredAnnotation = method.getDeclaredAnnotation(AnRequestMapping.class);
                    if (methodDeclaredAnnotation != null) {
                        String methodUrl = methodDeclaredAnnotation.value();
                        mvcBeanUrl.put(mappingUrl + methodUrl, objValue);
                        mvcMethodUrl.put(mappingUrl + methodUrl, method.getName());
                    }
                }
    
            }
    
        }
    
        /**
         * @param str
         * @return 类名首字母小写
         */
        public static String lowerFirstCapse(String str) {
            char[] chars = str.toCharArray();
            chars[0] += 32;
            return String.valueOf(chars);
    
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            try {
                doServelt(req, resp);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    
        private void doServelt(HttpServletRequest req, HttpServletResponse resp) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ServletException {
            //获取请求地址
            String requestUrl = req.getRequestURI();
            //查找地址所对应bean
            Object object = mvcBeanUrl.get(requestUrl);
            if (object == null) {
                resp.getWriter().println("sorry http is not exit 404");
                return;
            }
            //获取请求的方法
            String methodName = mvcMethodUrl.get(requestUrl);
            if (methodName == null) {
                resp.getWriter().println("sorry method is not exit 404");
                return;
            }
            //通过构反射执行方法
            Class<?> aClass = object.getClass();
            Method method = aClass.getMethod(methodName);
            String invoke = (String) method.invoke(object);
            // 获取后缀信息
            String suffix = ".jsp";
            // 页面目录地址
            String prefix = "/";
            req.getRequestDispatcher(prefix + invoke + suffix).forward(req, resp);
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.doPost(req, resp);
        }
    
    
    }

    ClassUtils工具类封装

    package com.zzw.cn.utils;
    
    import java.io.File;
    import java.io.FileFilter;
    import java.io.IOException;
    import java.net.JarURLConnection;
    import java.net.URL;
    import java.net.URLDecoder;
    import java.util.ArrayList;
    import java.util.Enumeration;
    import java.util.List;
    import java.util.jar.JarEntry;
    import java.util.jar.JarFile;
    
    public class ClassUtils {
    
        /**
         * 从包package中获取所有的Class
         *
         * @param pack
         * @return
         */
        public static List<Class<?>> getClasses(String packageName) {
    
            // 第一个class类的集合
            List<Class<?>> classes = new ArrayList<Class<?>>();
            // 是否循环迭代
            boolean recursive = true;
            // 获取包的名字 并进行替换
            String packageDirName = packageName.replace('.', '/');
            // 定义一个枚举的集合 并进行循环来处理这个目录下的things
            Enumeration<URL> dirs;
            try {
                dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
                // 循环迭代下去
                while (dirs.hasMoreElements()) {
                    // 获取下一个元素
                    URL url = dirs.nextElement();
                    // 得到协议的名称
                    String protocol = url.getProtocol();
                    // 如果是以文件的形式保存在服务器上
                    if ("file".equals(protocol)) {
                        // 获取包的物理路径
                        String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                        // 以文件的方式扫描整个包下的文件 并添加到集合中
                        findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
                    } else if ("jar".equals(protocol)) {
                        // 如果是jar包文件
                        // 定义一个JarFile
                        JarFile jar;
                        try {
                            // 获取jar
                            jar = ((JarURLConnection) url.openConnection()).getJarFile();
                            // 从此jar包 得到一个枚举类
                            Enumeration<JarEntry> entries = jar.entries();
                            // 同样的进行循环迭代
                            while (entries.hasMoreElements()) {
                                // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
                                JarEntry entry = entries.nextElement();
                                String name = entry.getName();
                                // 如果是以/开头的
                                if (name.charAt(0) == '/') {
                                    // 获取后面的字符串
                                    name = name.substring(1);
                                }
                                // 如果前半部分和定义的包名相同
                                if (name.startsWith(packageDirName)) {
                                    int idx = name.lastIndexOf('/');
                                    // 如果以"/"结尾 是一个包
                                    if (idx != -1) {
                                        // 获取包名 把"/"替换成"."
                                        packageName = name.substring(0, idx).replace('/', '.');
                                    }
                                    // 如果可以迭代下去 并且是一个包
                                    if ((idx != -1) || recursive) {
                                        // 如果是一个.class文件 而且不是目录
                                        if (name.endsWith(".class") && !entry.isDirectory()) {
                                            // 去掉后面的".class" 获取真正的类名
                                            String className = name.substring(packageName.length() + 1, name.length() - 6);
                                            try {
                                                // 添加到classes
                                                classes.add(Class.forName(packageName + '.' + className));
                                            } catch (ClassNotFoundException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                    }
                                }
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return classes;
        }
    
        /**
         * 以文件的形式来获取包下的所有Class
         *
         * @param packageName
         * @param packagePath
         * @param recursive
         * @param classes
         */
        public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
                                                            List<Class<?>> classes) {
            // 获取此包的目录 建立一个File
            File dir = new File(packagePath);
            // 如果不存在或者 也不是目录就直接返回
            if (!dir.exists() || !dir.isDirectory()) {
                return;
            }
            // 如果存在 就获取包下的所有文件 包括目录
            File[] dirfiles = dir.listFiles(new FileFilter() {
                // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
                @Override
                public boolean accept(File file) {
                    return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
                }
            });
            // 循环所有文件
            for (File file : dirfiles) {
                // 如果是目录 则继续扫描
                if (file.isDirectory()) {
                    findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
                            classes);
                } else {
                    // 如果是java类文件 去掉后面的.class 只留下类名
                    String className = file.getName().substring(0, file.getName().length() - 6);
                    try {
                        // 添加到集合中去
                        classes.add(Class.forName(packageName + '.' + className));
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    自定义注解类AnController

    package com.zzw.cn.springmvc.annoation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @author Simple
     * @date 14:06 2019/8/27
     * @description
     */
    @Target({ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AnController {
    }

    自定义注解类AnRequestMapping

    package com.zzw.cn.springmvc.annoation;
    
    import java.lang.annotation.*;
    
    /**
     * @author Simple
     * @date 14:07 2019/8/27
     * @description
     */
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface AnRequestMapping {
        String value() default "";
    }

    HelloWorld类

    package com.zzw.cn.springmvc.controller;
    
    import com.zzw.cn.springmvc.annoation.AnController;
    import com.zzw.cn.springmvc.annoation.AnRequestMapping;
    
    /**
     * @author Simple
     * @date 15:15 2019/8/27
     * @description
     */
    @AnController
    @AnRequestMapping(value = "/hello")
    public class HelloWorld {
        @AnRequestMapping("/method")
        public String method(){
            return "index";
        }
    }

    index.jsp

    <html>
    <body>
    <h2>Hello World!</h2>
    </body>
    </html>

    访问地址:http://localhost:8080/hello/method

    成功结果:

  • 相关阅读:
    HTMLParser使用简介
    用自定义注解验证一些方法
    struts框架从.jsp页面直接访问action
    Hibernate更新数据(不用update也可以)
    设计模式的模板方式应用
    实现观察者模式设计方案
    struts2中的使用BaseAction获取Session
    brew,gem,rvm 和 bundler软件包的管理工具
    oh my zsh命令
    Ruby Gem命令
  • 原文地址:https://www.cnblogs.com/lusaisai/p/13121132.html
Copyright © 2011-2022 走看看