springmvc执行原理及自定义mvc框架

  springmvc是spring的一部分,也是一个优秀的mvc框架,其执行原理如下:

  (1)浏览器提交请求经web容器(比如tomcat)转发到中央调度器dispatcherServlet。

  (2)中央调度器调用处理器映射器handerMapping,处理器映射器根据请求的url找到处理该请求对应的处理器hander及相关的拦截器intercepter,将它们封装成一个处理器执行链并返回给中央调度器

  (3)中央调度器根据处理器执行链中的处理器找到对应的处理器适配器handerAdaptor

  (4)处理适配器调用处理器执行对应的方法并将返回的结果封装为一个对象modelAndView中返回给中央处理器,当然在处理器执行方法前如果方法有拦截器的话会先依次执行拦截器的prehander方法,方法执行结束后会依次执行拦截器的posthander方法。

  (5)中央调度器获取到modelAndView对象后,调用视图解析器viewResolver,将modelAndView封装为视图对象

  (6)中央调度器获取到视图对象后,进行渲染,生成最后的响应返回给浏览器。

  

  下面就参照springmvc的原理,写一个我们自己的mvc框架,springmvc是依赖于servlet容器的,所以我们也要依赖javax.servlet-api-3.1.0.jar这个包,另外需要在web.xml中配置我们自己定义的中央处理器,以便web容器在启动时初始化mvc上下文。web.xml配置如下

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
  <servlet>
    <servlet-name>MySpringMvc</servlet-name>
    <servlet-class>io.powerx.servlet.MyDispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.properties</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>MySpringMvc</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

  简单期间,我们就不去解析xml,使用springmvc.properties来代替,springmvc.properties的配置很简单,就是配置了一下扫描类的路径:basePackage=io.powerx。MyDispatcherServlet代码如下,具体代码里我都有注释

package io.powerx.servlet;

import io.powerx.annotation.*;
import io.powerx.util.ParseUtil;

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 java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MyDispatcherServlet extends HttpServlet{

    private static final long serialVersionUID = -2661065041652722638L;

    //存自动扫描包下的所有类全名
    private List<String> classNames = new ArrayList<>();

    //存controller和 service 实例,key:实例名 valu:实例对象
    private Map<String,Object> instanceMap = new HashMap<>();

    //存 url 和对应的method
    private Map<String,Method> handerMaps = new HashMap<>();

    //存controller 名字和对象
    private Map<String,Object> controllerMap = new HashMap<>();

    @Override
    public void init(ServletConfig config) throws ServletException {

        try {
            //1、读取配置文件,获取扫描包信息
            String mvcConfig  = config.getInitParameter("contextConfigLocation").replace("classpath:","");
            String packageName = ParseUtil.getBasePackageName(mvcConfig);

            //2、自动扫描包下所有类,获取所有的类全名
            scanBasePackage(packageName);

            //3、利用反射机制,获取实例,存储在instanceMapzhing
            reflectBeanInstance();

            //4、依赖注入
            doIoc();

            //5、初始化handerMapping,建立url和method的映射关系
            initHanderMapping();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initHanderMapping() throws Exception{

        if(instanceMap.isEmpty()){
            throw new Exception("实例对象为空");
        }
        for (Map.Entry<String,Object> entry : instanceMap.entrySet()) {
            Class<?> aclazz = entry.getValue().getClass();
            if(aclazz.isAnnotationPresent(MyController.class)){
                String classUrl ="";
                if(aclazz.isAnnotationPresent(MyRequestMapping.class)){
                    classUrl = aclazz.getAnnotation(MyRequestMapping.class).value();
                }
                Method[] methods = aclazz.getMethods();
                for (Method method : methods) {
                    if(method.isAnnotationPresent(MyRequestMapping.class)){
                        String methodUrl = method.getAnnotation(MyRequestMapping.class).value();
                        String url = classUrl + methodUrl;
                        handerMaps.put(url,method);
                        controllerMap.put(url,entry.getValue());
                    }
                }
            }
        }

    }

    private void doIoc() throws Exception{

        if(instanceMap.isEmpty()) {
            throw new Exception("无可注入的实例");
        }
        for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if(field.isAnnotationPresent(MyAotuwired.class)){
                    String insKey = field.getAnnotation(MyAotuwired.class).value();
                    if("".equals(insKey)){
                        insKey = ParseUtil.toLowerFirstName(field.getType().getSimpleName());
                    }
                    field.setAccessible(true);
                    //注入实例
                    field.set(entry.getValue(),instanceMap.get(insKey));
                }
            }
        }

    }

    private void reflectBeanInstance() throws Exception{

        if(classNames.isEmpty()){
            return;
        }
        for(String className: classNames){
            Class<?> aclazz = Class.forName(className);
            if(aclazz.isAnnotationPresent(MyController.class)){
                MyController myController = aclazz.getAnnotation(MyController.class);
                String beanName = myController.value();
                if("".equals(beanName)){
                    beanName = ParseUtil.toLowerFirstName(aclazz.getSimpleName());
                }
                instanceMap.put(beanName,aclazz.newInstance());

            }else if(aclazz.isAnnotationPresent(MyService.class)){
                MyService myService = aclazz.getAnnotation(MyService.class);
                String beanName = myService.value();
                if("".equals(beanName)){
                    beanName = ParseUtil.toLowerFirstName(aclazz.getSimpleName());
                }
                instanceMap.put(beanName,aclazz.newInstance());
            }

        }

    }

    private void scanBasePackage(String basePackName) throws Exception{
        System.out.println(basePackName);

        String path = "/" + ParseUtil.tranferQualifiedToPath(basePackName);
        System.out.println(path);
        URL url = this.getClass().getClassLoader().getResource(path);
        System.out.println(url);
        File dir = new File(url.getFile());
        File[] files = dir.listFiles();
        for (File file: files) {
            if(file.isDirectory()){
                scanBasePackage(basePackName +"."+file.getName());
            }else if(file.isFile()){
                classNames.add(basePackName +"." + file.getName().replace(".class",""));
                System.out.println("扫描到的类有" + basePackName +"." + file.getName().replace(".class",""));
            }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理请求
     * @param req
     * @param resp
     */
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{

        resp.setContentType("text/html;charset=UTF-8");
        String uri = req.getRequestURI();
        System.out.println(uri);
        String contextPath = req.getContextPath();
        System.out.println(contextPath);
        uri = uri.replace(contextPath,"");
        Method method = handerMaps.get(uri);
        PrintWriter pw = resp.getWriter();
        if(method == null){
            pw.print("404,请求路径不存在");
            return;
        }
        Parameter[] parameters  = method.getParameters();
        Object[] paramValues = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            if(ServletRequest.class.isAssignableFrom(parameters[i].getType())){
                paramValues[i] = req;
            }else if (ServletResponse.class.isAssignableFrom(parameters[i].getType())){
                paramValues[i] = resp;
            }else{
                String bindingParam = parameters[i].getName();
                if(parameters[i].isAnnotationPresent(MyRequestParam.class)){
                    bindingParam = parameters[i].getAnnotation(MyRequestParam.class).value();
                }
                String paramValue = req.getParameter(bindingParam);
                paramValues[i] = paramValue;
                if(Integer.class.isAssignableFrom(parameters[i].getType())){
                    paramValues[i] = Integer.parseInt(paramValue);
                }else if(Float.class.isAssignableFrom(parameters[i].getType())){
                    paramValues[i] = Float.parseFloat(paramValue);
                }else if(Double.class.isAssignableFrom(parameters[i].getType())){
                    paramValues[i] = Double.parseDouble(paramValue);
                }
            }

        }
        Object result = method.invoke(controllerMap.get(uri),paramValues);
        pw.print(result);
    }
}

  MyDispatcherServlet中依赖了我们自定义的注解类,和ParseUtil工具类,代码如下

package io.powerx.annotation;

import java.lang.annotation.*;

/**
 * Created by Administrator on 2019/1/2.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface MyAotuwired {

    String value() default "";
}
package io.powerx.annotation;

import java.lang.annotation.*;

/**
 * Created by Administrator on 2019/1/2.
 */
@Target(ElementType.TYPE )
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {

    String  value() default "";
}
package io.powerx.annotation;

import java.lang.annotation.*;

/**
 * Created by Administrator on 2019/1/2.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
public @interface MyRequestMapping {

    String value() default "";
}
package io.powerx.annotation;

import java.lang.annotation.*;

/**
 * Created by Administrator on 2019/1/2.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Documented
public @interface MyRequestParam {

    String value() default "";
}
package io.powerx.annotation;

import java.lang.annotation.*;

/**
 * Created by Administrator on 2019/1/2.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface MyService {

    String value() default "";
}
package io.powerx.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * Created by Administrator on 2019/1/2.
 */
public class ParseUtil {

    /**
     * 获取配置文件的扫描包
     * @param contextConfigLocation
     * @return
     */
    public static String getBasePackageName(String contextConfigLocation){
        InputStream in = ParseUtil.class.getClassLoader().getResourceAsStream(contextConfigLocation);
        Properties pp = new Properties();
        try {
            pp.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return pp.getProperty("basePackage");
    }

    /**
     * 将限定名转换为路径,如pers.hdh -> pers/hdh
     * @param qualifiedName
     * @return
     */
    public static String tranferQualifiedToPath(String qualifiedName)throws Exception{
        if(qualifiedName==null){
            throw  new Exception("空字符串不能转换");
        }
        return qualifiedName.replace(".","/");
    }

    /**
     * 转换第一个字母为小写
     * @param name
     * @return
     */
    public static String  toLowerFirstName(String name){
        char[] charArray = name.toCharArray();
        charArray[0] += 32;
        return String.valueOf(charArray);
    }

    public static void main(String[] args) throws Exception {

        System.out.println(tranferQualifiedToPath("io.powerx"));
    }
}

  测试的controller和service

package io.powerx.controller;

import io.powerx.annotation.MyAotuwired;
import io.powerx.annotation.MyController;
import io.powerx.annotation.MyRequestMapping;
import io.powerx.annotation.MyRequestParam;
import io.powerx.service.TestService;

/**
 * Created by Administrator on 2019/1/3.
 */
@MyController
@MyRequestMapping("/test")
public class TestController {

    @MyAotuwired
    private TestService testService;

    @MyRequestMapping("/a01")
    public void test01(){
        testService.mvcTest();
    }

    @MyRequestMapping("/a02")
    public String test02(@MyRequestParam("age") Integer age){
        return  testService.mvcTest2(age);
    }

}
package io.powerx.service;

import io.powerx.annotation.MyService;

/**
 * Created by Administrator on 2019/1/3.
 */
@MyService
public class TestService {

    public void mvcTest(){
        System.out.println("执行了测试方法");
    }

    public String mvcTest2(Integer a){
        System.out.println(a);
        return "我的响应";
    }
}

  编译打包,发布到tomcat中,可以看到控制台打印了我们扫描类和初始化mvc容器的过程

  打开浏览器,访问http://localhost:8888/myMvc/test/a02?age=12,可以看到后台收到浏览器的请求并执行,返回了结果

原文地址:https://www.cnblogs.com/hhhshct/p/10218405.html

时间: 2024-10-06 06:17:31

springmvc执行原理及自定义mvc框架的相关文章

Java Web自定义MVC框架详解 (转)

转自:http://blog.csdn.net/jackfrued/article/details/42774459 最近给学生讲Java Web,希望他们能够在学完这部分内容后自己实现一个MVC框架.但是突然发现百度上能搜索到的靠谱的资料并不是很多,有些只是原理没有代码实现,有些有 代码实现但是对于初学者来说理解起来还是比较困难,于是决定把自己讲自定义MVC框架的内容放在这里分享给大家,不仅仅是代码,也有原理和探讨.内容会比 较长,因为我打算用递增的方式讲解如何写一个自定义MVC框架,重点是前

SpringMVC执行原理

SpringMVC执行原理 听了秦老师的讲解受益匪浅,感谢秦老师 视频链接 图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现. 自己分析 1.用户发起请求,经过前端控制器DispatcherServlet.2.DispatcherServlet根据请求找到对应映射器,3.映射器映射了谁,4.把这个映射返回,5.根据映射器再去适配这个映射器,6.适配的映射器说白了就是Controller,78.由具体Controller

Struts2 自定义MVC框架

一.Model1与Model2: Model1:就是一种纯jsp开发技术,将业务逻辑代码和视图渲染代码杂糅在一起. Model2:Model2是在Model1的基础上,将业务逻辑的代码分离开来,单独形成一个Servlet,Model2也是基于MVC开发 二.MVC设计模式: 由3个部分组成,各部分作用如下: Model:模型,主要用于数据和业务的处理 View:视图,用于数据的显示 Controller:控制器,用于进行流程控制 MVC设计模式的特点: ①一个模型可以对应多个视图 ②显示与逻辑控

自定义MVC框架之工具类-模型类

截止目前已经改造了5个类: ubuntu:通过封装验证码类库一步步安装php的gd扩展 自定义MVC框架之工具类-分页类的封装 自定义MVC框架之工具类-文件上传类 自定义MVC框架之工具类-图像处理类 这个模型类支持以下功能: >连贯操作,js叫链式操作,连贯操作的函数可以打乱顺序,最后一个函数必须是执行语句的那个函数,如select, delete, update, add等 如 $db->table( 'user' )->where( 'id=1' )->select() 等

自定义MVC框架之工具类-图像处理类

截止目前已经改造了4个类: ubuntu:通过封装验证码类库一步步安装php的gd扩展 自定义MVC框架之工具类-分页类的封装 自定义MVC框架之工具类-文件上传类 图像处理类: 1,图片加水印处理( 支持任意位置与九宫格位置 ) 2,图片缩放处理( 等比缩放,不变形裁剪 ) 1 <?php 2 3 class Image{ 4 //文件路径 5 protected $path; 6 //是否允许随机文件名称 7 protected $enableRandName; 8 //文件类型 9 pro

简单封装自定义MVC框架

自定义Mvc框架结构及其使用方法 一,什么是MVC框架 MVC框架全名是model(模型)controller(控制器)view(视图文件)所构成的一种开发框架,是一种典型的软件设计典范,用一种业务逻辑,数据,显示相分离的方法组织代码,其好处是将业务逻辑聚集到一个部件里面,在改进和指定个性化页面的同时不需要重新编写业务逻辑,MVC被独特的发展起来用于映射传统的输入,处理,显示在一个业务逻辑的图形化业务界面中. 二,组成 ①,model(模型):主要负责具体功能的实现,一个模型对应一种功能 ②,v

自定义MVC框架

一.实现自定义MVC的体系结构图 1.Model I模式开发Web应用时,分两种情况: *纯JSP技术方式开发 *JSP+JavaBean方式开发 2.Model I模式开发的不足: *JSP页面中嵌入大量的Java代码,可读性差. *大量代码在JSP中难以复用. *后期维护及扩展的难度大. 3.为了克服Model I模式的缺陷,引入了Model II的模式开发 *Model II模式体现了基于MVC(Model-View-Controller,模型-视图-控制器)的设计模式,简单的说,Mode

使用Intellij Idea自定义MVC框架

今天我学习了自定义一个简单的MVC框架,这个我们首先要知道什么是MVC框架! MVC框架: MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑.MVC被独特的发展起来用于映射传统的输入.处理和输出功能在一个逻辑的图形化用户界面的结构中. 我们今天自己定义的

自定义MVC框架之工具类-分页类的封装

以前写过一个MVC框架,封装的有点low,经过一段时间的沉淀,打算重新改造下,之前这篇文章封装过一个验证码类. 这次重新改造MVC有几个很大的收获 >全部代码都是用Ubuntu+Vim编写,以前都是windows上开发,这次彻底迷上Ubuntu Linux >裸装php,用php自带的服务器解释执行php,缺哪个扩展就装哪个,最后通过整个MVC框架的开发,把Lamp所有的常用配置与细节搞懂 >通过扩展安装,学习扩展开发与php底层源码分析 总之,终于感觉层次又提升了不少. 分页类代码: