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

模拟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

成功结果:

springmvc入门及模拟springmvc

相关推荐