Spring的MVC框架主要由DispatcherServlet、处理器映射、处理器(控制器)、视图解析器、视图组成。html
完整的Spring MVC处理 流程以下:前端
DispatcherServlet接口:java
Spring提供的前端控制器,全部的请求都有通过它来统一分发。在DispatcherServlet将请求分发给Spring Controller以前,须要借助于Spring提供的HandlerMapping定位到具体的Controller。node
HandlerMapping接口:git
可以完成客户请求到Controller映射。web
Controller接口:spring
须要为并发用户处理上述请求,所以实现Controller接口时,必须保证线程安全而且可重用。数组
Controller将处理用户请求,这和Struts Action扮演的角色是一致的。一旦Controller处理完用户请求,则返回ModelAndView对象给DispatcherServlet前端控制器,ModelAndView中包含了模型(Model)和视图(View)。spring-mvc
从宏观角度考虑,DispatcherServlet是整个Web应用的控制器;从微观考虑,Controller是单个Http请求处理过程当中的控制器,而ModelAndView是Http请求过程当中返回的模型(Model)和视图(View)。安全
ViewResolver接口:
Spring提供的视图解析器(ViewResolver)在Web应用中查找View对象,从而将相应结果渲染给客户。
本次实现没有视图解析内容。主要包括,自动扫描class类、经过解析注解实现bean的实例化、bean之间的依赖注入、经过注解映射路径返回正确的处理方法。
Spring MVC框架主要依赖于Java的反射机制实现。实现原理与上面描述一致。
工程名MySpringMVC
代码存放servlet包。
DispatcherServlet
package zqq.servlet; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import zqq.annotations.EnjoyAuthowired; import zqq.annotations.EnjoyController; import zqq.annotations.EnjoyRequestMapping; import zqq.annotations.EnjoyRequestParam; import zqq.annotations.EnjoyService; import zqq.controller.ZqqController; /** * Servlet implementation class DispatcherServlet */ public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; // 扫描获得的类名集合 List<String> classNames = new ArrayList<String>(); // 存放全部Spring实例的Map Map<String, Object> beans = new HashMap<String, Object>(); // 存放全部路径映射 Map<String, Object> handlerMap = new HashMap<String, Object>(); /** * @see HttpServlet#HttpServlet() */ public DispatcherServlet() { } /** * @see Servlet#init(ServletConfig) */ public void init(ServletConfig config) throws ServletException { // 一、扫描工程有多少class doScanPackage("zqq"); // 打印全部class for (String name : classNames) { System.out.println(name); } // 二、实例化 doInstance(); for (Map.Entry<String, Object> entry : beans.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); } // 三、注入 doIoC(); // 四、请求映射 buildMapping(); for (Map.Entry<String, Object> entry : handlerMap.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); } } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse * response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // springmvc /zqq/query String uri = request.getRequestURI(); String context = request.getContextPath(); // springmvc String path = uri.replace(context, ""); // /zqq/query // 获取映射对应的method Method method = (Method) handlerMap.get(path); ZqqController instance = (ZqqController) beans.get("/" + path.split("/")[1]); Object args[] = this.hand(request, response, method); try { method.invoke(instance, args); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } /** * @author qqz * @date 2018年7月12日 上午1:02:44 扫描当前路径下有多少个class类 * @param string */ private void doScanPackage(String basePackage) { // URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/")); String filepath = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/")).getFile(); try { filepath= java.net.URLDecoder.decode(filepath,"utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } // String fileStr = url.getFile(); // 目录对象 File file = new File(filepath); String[] filesStr = file.list(); // 递归处理路径basepackage下的类文件 for (String path : filesStr) { File filePath = new File(filepath + path); if (filePath.isDirectory()) { doScanPackage(basePackage + "." + path); } else { // 获得class 全类名路径 zqq.controller.ZqqController classNames.add(basePackage + "." + filePath.getName()); } } } /** * @author qqz * @date 2018年7月12日 上午1:11:04 TODO */ private void doInstance() { if (classNames.size() <= 0) { System.out.println("scan classes failed!"); } for (String className : classNames) { // 去掉.class后缀 String cn = className.replace(".class", ""); try { Class<?> clazz = Class.forName(cn); // 处理带有EnjoyController注解的类 if (clazz.isAnnotationPresent(EnjoyController.class)) { // 实例化对象 Object instance = clazz.newInstance(); EnjoyRequestMapping reqMapping = clazz.getAnnotation(EnjoyRequestMapping.class); String key = reqMapping.value(); beans.put(key, instance); } else if (clazz.isAnnotationPresent(EnjoyService.class)) { // 实例化对象 Object instance = clazz.newInstance(); EnjoyService service = clazz.getAnnotation(EnjoyService.class); String key = service.value(); beans.put(key, instance); } else { continue; } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } } /** * @author qqz 属性注入 * @date 2018年7月12日 上午1:21:10 TODO */ private void doIoC() { if (beans.entrySet().size() <= 0) { System.out.println("instance bean failed."); return; } for (Map.Entry<String, Object> entry : beans.entrySet()) { Object instance = entry.getValue(); Class<?> clazz = instance.getClass(); if (clazz.isAnnotationPresent(EnjoyController.class)) { // 获取类中全部属性 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { // 获取声明注入的属性 if (field.isAnnotationPresent(EnjoyAuthowired.class)) { EnjoyAuthowired authowired = field.getAnnotation(EnjoyAuthowired.class); // 获取注解EnjoyAutowired中命名的值 String value = authowired.value(); // 放开权限设置属性值 field.setAccessible(true); try { field.set(instance, beans.get(value)); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } else { continue; } } } } } /** * @author qqz * @date 2018年7月12日 上午1:32:25 TODO */ private void buildMapping() { if (beans.entrySet().size() <= 0) { System.out.println("instance bean failed."); return; } for (Map.Entry<String, Object> entry : beans.entrySet()) { Object instance = entry.getValue(); Class<?> clazz = instance.getClass(); // 映射是在Controller层 if (clazz.isAnnotationPresent(EnjoyController.class)) { // 获取类映射 EnjoyRequestMapping requestMapping = clazz.getAnnotation(EnjoyRequestMapping.class); String classPath = requestMapping.value(); // 获取方法上的映射 Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(EnjoyRequestMapping.class)) { EnjoyRequestMapping requestMapping1 = method.getAnnotation(EnjoyRequestMapping.class); String methodPath = requestMapping1.value(); // 构建方法路径与方法的映射 handlerMap.put(classPath + methodPath, method); } else { continue; } } } } } /** * @author qqz * @date 2018年7月12日 上午1:59:48 * 方法参数注解解析 * @param request * @param response * @param method * @return */ private static Object[] hand(HttpServletRequest request, HttpServletResponse response, Method method) { // 拿到当前执行的方法有哪些参数 Class<?>[] paramClazzs = method.getParameterTypes(); // 根据参数的个数,new 一个参数的数组, 将方法里全部参数赋值到args来 Object[] args = new Object[paramClazzs.length]; int arg_i = 0; int index = 0; for (Class<?> paramClazz : paramClazzs) { if (ServletRequest.class.isAssignableFrom(paramClazz)) { args[arg_i++] = request; } if (ServletResponse.class.isAssignableFrom(paramClazz)) { args[arg_i++] = response; } // 从0-3判断有没有RequestParam注解,很明显paramClazz为0和1时,不是,当为2和3时为@RequestParam,须要 // 解析[@zqq.annotation.EnjoyRequestParam(value=name)] Annotation[] paramAns = method.getParameterAnnotations()[index]; if (paramAns.length > 0) { for (Annotation paramAn : paramAns) { if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) { EnjoyRequestParam rp = (EnjoyRequestParam)paramAn; //找到注解里的name和age args[arg_i++] = request.getParameter(rp.value()); } } } index ++; } return args; } }
代码存放的包annotations中。包括以下几个
EnjoyAuthowired.java
EnjoyController.java
EnjoyRequestMapping.java
EnjoyRequestParam.java
EnjoyService.java
属性注解EnjoyAuthowired.java
package zqq.annotations; 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; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface EnjoyAuthowired { String value() default ""; }
Controller注解EnjoyController.java
package zqq.annotations; 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; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface EnjoyController { String value() default ""; }
映射注解EnjoyRequestMapping.java
package zqq.annotations; 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; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) public @interface EnjoyRequestMapping { String value() default ""; }
参数注解EnjoyRequestParam.java
package zqq.annotations; 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; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface EnjoyRequestParam { String value() default ""; }
Service注解EnjoyService.java
package zqq.annotations; 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; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface EnjoyService { String value() default ""; }
代码存放controller包。
/** * ZqqController.java */ package zqq.controller; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import zqq.annotations.EnjoyAuthowired; import zqq.annotations.EnjoyController; import zqq.annotations.EnjoyRequestMapping; import zqq.annotations.EnjoyRequestParam; import zqq.service.ZqqService; @EnjoyController @EnjoyRequestMapping("/zqq") public class ZqqController { @EnjoyAuthowired("ZqqServiceImpl") private ZqqService zqqService; @EnjoyRequestMapping("/query") public void query(HttpServletRequest req, HttpServletResponse resp, @EnjoyRequestParam("name") String name, @EnjoyRequestParam("age") String age) { PrintWriter pw; try { pw = resp.getWriter(); String result = zqqService.query(name, age); pw.write(result); } catch (IOException e) { e.printStackTrace(); } } }
代码存放service包
/** * ZqqService.java */ package zqq.service; public interface ZqqService { String query(String name,String age); }
Service实现类存放service/impl
/** * ZqqServiceImpl.java */ package zqq.service.impl; import zqq.annotations.EnjoyService; import zqq.service.ZqqService; @EnjoyService("ZqqServiceImpl") public class ZqqServiceImpl implements ZqqService { /* * (non-Javadoc) * * @see zqq.service.ZqqService#query(java.lang.String, java.lang.String) */ @Override public String query(String name, String age) { return "{name:" + name + ",age:" + age + "}"; } }
src/main/webapp/WEB-INF/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> <servlet> <servlet-name>DispatcherServlet</servlet-name> <display-name>DispatcherServlet</display-name> <description></description> <servlet-class>zqq.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
部署后访问localhost:8080/MySpringMVC/zqq/query?name=zqq&age=18
能够在页面上看到请求中的name和age。
参考资料:
SpringMVC框架介绍