深度解读springMVC底层实现

使用JDK8 ,idea2018.2, maven3.5.4

使用XML 解析 + 反射来写一个springMVC框架:
如下思路实现;

首先需要一个前置控制器 DispatcherServlet,作为整个流程的核心,由它去调用其他组件,共同完成业务。主要组件有两个:
一是 Controller,调用其业务方法 Method,执行业务逻辑;
二是 ViewResolver 视图解析器,将业务方法的返回值解析为物理视图 + 模型数据,返回客户端。

深度解读springMVC底层实现_第1张图片

Spring MVC 的实现流程:

客户端请求被 DispatcherServlet(前端控制器)接收。
->根据 HandlerMapping 映射到 Handler。
->生成 Handler 和 HandlerInterceptor(如果有则生成)。
->Handler 和 HandlerInterceptor 以 HandlerExecutionChain 的形式一并返回给 DispatcherServlet。
->DispatcherServlet 通过 HandlerAdapter 调用 Handler 的方法做业务逻辑处理。
->返回一个 ModelAndView 对象给 DispatcherServlet。
->DispatcherServlet 将获取的 ModelAndView 对象传给 ViewResolver 视图解析器,将逻辑视图解析成物理视图 View。
->ViewResolver 返回一个 View 给 DispatcherServlet。
->DispatcherServlet 根据 View 进行视图渲染(将模型数据填充到视图中)。
->DispatcherServlet 将渲染后的视图响应给客户端。

分析:

HTTP 请求是通过注解找到对应的 Controller 对象…;
Controller 的 Method 也是通过注解与 HTTP 请求映射的;
使用map 当做 ioC 容器,完成储存所有参数与业务的class;
.

业务逻辑:

初始化工作完成,接下来处理 HTTP 请求,业务流程如下:
DispatcherServlet 接收请求,通过映射从 IoC 容器中获取对应的 Controller 对象;
根据映射获取 Controller 对象对应的 Method;
调用 Method,获取返回值;
将返回值传给视图解析器,返回物理视图;
完成页面跳转。

深度解读springMVC底层实现_第2张图片

doPost 方法处理 HTTP 请求:

解析 HTTP,分别得到 Controller 和 Method 对应的 URL
通过 URL 分别在 iocContainer 和 handlerMapping 中获取对应的 Controller 及 Method
使用反射调用 Method,执行业务方法,获取结果
结果传给 MyViewResolver 进行解析,返回真正的物理视图(JSP 页面)
完成 JSP 的页面跳转


项目结构如图

深度解读springMVC底层实现_第3张图片

自定义注解:
@MyController
@MyRequestMapping

/**
 * @auther SyntacticSugar
 * @data 2018/11/13 0013下午 9:15
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
    String value() default "";
}
/**
 *   自定义  @RequestMapping  注解
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
    String value() default "";
}

定义一个核心控制器MyDispatcherServlet ;

/**
 * @auther SyntacticSugar
 * @data 2018/11/13 0013下午 9:20
 * 

* 创建控制器 */ public class MyDispatcherServlet extends HttpServlet { //创建ioC 创建 handler存放容器 private HashMap<String, Object> ioC = new HashMap<>(); private HashMap<String, Method> handlerMapping = new HashMap<>(); //自定义视图解析 private MyViewResolver myViewResolver; @Override public void init(ServletConfig config) throws ServletException { // 把controller放到ioC中 scanController(config); //初始化handler 映射 initHandlerMapping(); //加载视图解析器 loadViewResolver(config); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String handlerUri = req.getRequestURI().split("/")[2]; String methodUri = req.getRequestURI().split("/")[3]; // Object o = ioC.get(handlerUri); Method method = handlerMapping.get(methodUri); // 使用反射机制,调用执行 业务 try { String value = (String) method.invoke(o); // 将逻辑视图 转化为 物理视图,交给 view渲染返回前端 String result = myViewResolver.jspMapping(value); req.getRequestDispatcher(result).forward(req,resp ); } catch (Exception e) { e.printStackTrace(); } } /** * saxReader 解析springmvc.xml * @param config */ private void scanController(ServletConfig config) { SAXReader saxReader = new SAXReader(); try { String path = config.getServletContext().getRealPath("") + "\\WEB-INF\\classes\\" + config.getInitParameter("contextConfigLocation"); Document document = saxReader.read(path); // 获取根元素 Element rootElement = document.getRootElement(); Iterator iterator = rootElement.elementIterator(); // 遍历 nodes 、sax解析每一行xml while (iterator.hasNext()) { Element next = (Element) iterator.next(); // 把每一个元素的name和 component-scan 比较,获取base-package值 if (next.getName().equals("component-scan")) { String packageName = next.attributeValue("base-package"); // 获取包下 每个子包 List<String> classNames = getClassNames(packageName); for (String className : classNames) { /** 通过反射获取clazz ,判断class上是否存在MyController注解 * 若存在 、获取MyRequestMapping 的 value , 并将其 装入 自定义的ioC */ Class<?> clazz = Class.forName(className); if (clazz.isAnnotationPresent(MyController.class)) { MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class); String value = annotation.value().substring(1); // 放入ioC ioC.put(value,clazz.newInstance() ); } } } } } catch (Exception e) { e.printStackTrace(); } } /** * 获取 所有class 全路径名 * @param packageName * @return */ private List<String> getClassNames(String packageName) { List<String> classNameList = new ArrayList<String>(); String path = packageName.replace(".", "/"); //已知存在包路径,获取每一级路径下的file、 获取类加载器, ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); URL url = classLoader.getResource(path); //非空判断 if (url != null) { File[] files = new File(url.getPath()).listFiles(); //遍历取值 for (File childFile : files) { String className = packageName + "." + childFile.getName().replace(".class", ""); classNameList.add(className); } } // return return classNameList; } /** * 初始化 handler */ private void initHandlerMapping() { //从ioC中取出 MyController注解的 class for (String s : ioC.keySet()) { Class<?> clazz = ioC.get(s).getClass(); Method[] methods = clazz.getMethods(); //遍历 for (Method method : methods) { // 判断哪一个被 @MyRequestMapping 注解标识 if (method.isAnnotationPresent(MyRequestMapping.class)) { MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class); String value = annotation.value().substring(1); // 存入 handler handlerMapping.put(value,method ); } } } } /** * 加载自定义视图 saxReader 解析springmvc.xml * @param config */ private void loadViewResolver(ServletConfig config) { SAXReader reader = new SAXReader(); try { String path = config.getServletContext().getRealPath("")+"\\WEB-INF\\classes\\"+config.getInitParameter("contextConfigLocation"); Document document = reader.read(path); Element root = document.getRootElement(); Iterator iter = root.elementIterator(); //遍历 while(iter.hasNext()){ Element ele = (Element) iter.next(); if(ele.getName().equals("bean")){ String className = ele.attributeValue("class"); Class clazz = Class.forName(className); Object obj = clazz.newInstance(); //获取 setter 方法 Method prefixMethod = clazz.getMethod("setPrefix", String.class); Method suffixMethod = clazz.getMethod("setSuffix", String.class); Iterator beanIter = ele.elementIterator(); //获取 property 值 Map<String,String> propertyMap = new HashMap<String,String>(); while(beanIter.hasNext()){ Element beanEle = (Element) beanIter.next(); String name = beanEle.attributeValue("name"); String value = beanEle.attributeValue("value"); propertyMap.put(name, value); } for(String str:propertyMap.keySet()){ //反射机制调用 setter 方法,完成赋值 if(str.equals("prefix")){ prefixMethod.invoke(obj, propertyMap.get(str)); } if(str.equals("suffix")){ suffixMethod.invoke(obj, propertyMap.get(str)); } } myViewResolver = (MyViewResolver) obj; } } } catch (Exception e) { e.printStackTrace(); } } }


自定义一个视图解析器,MyViewResolver

/**
 * @auther SyntacticSugar
 * @data 2018/11/13 0013下午 9:31
 *
 * 自定义视图解析器 MyViewResolver
 */
public class MyViewResolver {
    private  String prefix;
    private  String suffix;
    //  目标资源路径
    public String jspMapping(String value){
        return this.prefix+value+this.suffix;
    }
    //setter  getter
    ......
}

创建测试 TestController ,对自定义的springmvc 进行测试;

/**
 * @auther SyntacticSugar
 * @data 2018/11/13 0013下午 10:47
 */
@MyController
@MyRequestMapping("/testController")
public class TestController {
    @MyRequestMapping("/test")
    public  String test(){
        System.out.println("执行test相关业务");
        return "index";
    }
}

springmvc.xml配置:


<beans>
    <component-scan base-package="com.baidu"/>
    
    <bean class="com.baidu.view.MyViewResolver">
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    bean>
beans>

启动tomcat访问:
http://localhost:8080/SpringMVCImitate-master/testController/test

深度解读springMVC底层实现_第4张图片
深度解读springMVC底层实现_第5张图片

源码下载:
https://download.csdn.net/download/weixin_42323802/10783231

你可能感兴趣的:(SpringMVC)