zhongliwen 2020-06-13
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
1、配置web.xml,加载自定义的DispatcherServlet。
2、初始化阶段,在DispatcherServlet类中,实现下面几个步骤:
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
成功结果: