手写实现SpringMVC

SpringMVC流程图

image.png

  1. 用户发送请求至前端控制器DispatcherServlet。

  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器。

  3. 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

  4. DispatcherServlet调用HandlerAdapter处理器适配器。

  5. HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

  6. Controller执行完成返回ModelAndView。

  7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

  9. ViewReslover解析后返回具体View。

  10. DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

  11. DispatcherServlet响应用户。

SpringMVC核心组件

1、前端控制器DispatcherServlet
作用:接收请求,响应结果,相当于转发器,中央处理器。有了dispatcherServlet减少了其它组件之间的耦合度。
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

2、处理器映射器HandlerMapping
作用:根据请求的url查找Handler
HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

3、处理器适配器HandlerAdapter
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

4、处理器Handler
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler。

5、视图解析器View resolver(不需要工程师开发),由框架提供
作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。

6、视图View
View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)

手写 SpringMVC 思路

处理器:

  1. 使用内嵌的tomcat作为servlet容器

注解方式(@Controller 定义处理器):

  1. 打开Spring Ioc 扫描,讲处理器放入容器,方便后面后置处理器从容器中取出,如果发现加了@Controller 则可以认为是一个处理器
  2. 实现SpringMVC 的@RequestMapping 注解,获取参数url,和处理器一起放入 Map<url,处理器>
  3. 获取请求传入的参数并处理参数,通过初始化好的handlerMapping中拿出url对应的方法名,反射调用

基于 servlet 定义处理器 调用 service()

接口方式(实现Controller接口 定义处理器)调用 handleRequest

接口方式(实现HttpRequestHandler 接口 定义处理器)调用 handleRequest

适配器(跟映射器一一对应):

  1. 统一的接口
  2. 判断适配是否成功 Object处理器 instanseOf Controller
  3. 执行处理逻辑 (Controller)处理器 调用 handleRequest

序列化返回:

  1. 第一种方式是spring2时代的产物,也就是每个json视图controller配置一个Jsoniew
  2. 第二种使用JSON工具将对象序列化成json,常用工具Jackson,fastjson,gson
  3. 注解@ResponseBody

参数绑定:

注解方式:@RequestParam(value)里面的参数值和我们的方法值进行匹配,匹配成功的则为该方法的参数

非注解方式:jdk7:asm字节码技术;jdk8:可以通过反射获取参数名

具体代码实现

内嵌的tomcat作为servlet容器,完成IOC注入,Servlet初始化工作
@ComponentScan("com.zy")
public class Start {

    public static void main(String[] args) throws Exception {
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);

        Context appContext = tomcat.addWebapp("/", "D:\\workspace\\springmvc\\src\\main\\webapp");
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Start.class);


        tomcat.addServlet(appContext,"dispatcherServlet",new DispatcherServlet(ac));
        appContext.addServletMapping("/","dispatcherServlet");

        tomcat.start();
        tomcat.getServer().await();

    }
}
映射器,基于注解和Controller接口实现

注解形式:

@Component
public class AnnotationHadlerMapping  implements HandlerMapping,InstantiationAwareBeanPostProcessor {

    public static Map<String,RequestMappingInfo> map = new HashMap<>();

    @Override
    //bean里面会有多个处理器
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        Method[] methods = bean.getClass().getDeclaredMethods();
        for(Method method : methods){
            RequestMappingInfo requestMappingInfo = createRequestMappingInfo(method,bean);
            map.put(requestMappingInfo.getUri(),requestMappingInfo);
        }
        return true;
    }

    private RequestMappingInfo createRequestMappingInfo(Method method,Object bean) {
        RequestMappingInfo requestMappingInfo = new RequestMappingInfo();
        if(method.isAnnotationPresent(RequestMapping.class)){
            requestMappingInfo.setMethod(method);
            requestMappingInfo.setUri(method.getDeclaredAnnotation(RequestMapping.class).value());
            requestMappingInfo.setObj(bean);
        }
        return requestMappingInfo;
    }

    @Override
    public Object getHandlerMapping(String requestURI) {
        return map.get(requestURI);
    }
}

Controller接口形式:

@Component
public class BeanNameHandlerMapping  implements HandlerMapping,InstantiationAwareBeanPostProcessor {

    public static Map<String, Controller> map = new HashMap<>();

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if(beanName.startsWith("/")){
            map.put(beanName,(Controller)bean);
        }
        return true;
    }

    @Override
    public Object getHandlerMapping(String requestURI) {
        return map.get(requestURI);
    }
}

适配器

注解形式:

@Component
public class AnnotationHandlerAdapter implements HandlerAdapter{

    @Override
    public boolean supports(Object handler) {
        //判断是否是RequestMapping子类
        return (handler instanceof RequestMappingInfo);
    }
    @Override
    //参数绑定
    public Object handle(HttpServletRequest request, HttpServletResponse response, Object handler){
        RequestMappingInfo requestMappingInfo = (RequestMappingInfo)handler;
        Map<String, String[]> paramMap = request.getParameterMap();//请求携带的参数
        Method method = requestMappingInfo.getMethod();//方法定义的参数
        Parameter[] parameters = method.getParameters();

        Object[] params = new Object[method.getParameterTypes().length];
        for(int i=0; i<parameters.length; i++){
            for(Map.Entry<String, String[]> entry : paramMap.entrySet()){
                if(parameters[i].getAnnotation(RequestParam.class) != null && entry.getKey()!= null &&
                        entry.getKey().equals(parameters[i].getAnnotation(RequestParam.class).value())){
                    params[i] = entry.getValue()[0];
                //jdk1.8实现反射获取方法名   1.8之前使用asm实现
                }else if(entry.getKey().equals(parameters[i].getName())){
                    params[i] = entry.getValue()[0];
                }
            }

            //传入request和response
            if(ServletRequest.class.isAssignableFrom(parameters[i].getType())){
                params[i] = request;
            }else if(ServletResponse.class.isAssignableFrom(parameters[i].getType())){
                params[i] = response;
            }
        }

        try {
            Object result = method.invoke(requestMappingInfo.getObj(),params);
            if (result instanceof String) {
                if ("forward".equals(((String) result).split(":")[0])) {
                    request.getRequestDispatcher(((String) result).split(":")[1]).forward(request, response);
                } else {
                    response.sendRedirect(((String) result).split(":")[1]);
                }
            }else{
                if(method.isAnnotationPresent(ResponseBody.class)){
                    return JSON.toJSONString(result);
                }
            }

            return result;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

Controller接口方式

@Component
public class BeanNameHandlerAdapter implements HandlerAdapter{
    @Override
//    //判断hanlde是否适配当前适配器
    public boolean supports(Object handler) {
        //判断是否是RequestMapping子类
        return (handler instanceof Controller);
    }

    @Override
    public Object handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        return ((Controller)handler).handler(request, response);
    }
}
控制器
public class DispatcherServlet extends HttpServlet {
    static Collection<HandlerAdapter> handlerAdapters ;
    static Collection<HandlerMapping> handlerMappings ;

    public DispatcherServlet() {
    }

    //构造器
    public DispatcherServlet(AnnotationConfigApplicationContext ac) {
        //映射器
        Map<String, HandlerMapping> handlerMappingMaps = ac.getBeansOfType(HandlerMapping.class);
        handlerMappings = handlerMappingMaps.values();
        //适配器
        Map<String, HandlerAdapter> handlerAdapterMaps = ac.getBeansOfType(HandlerAdapter.class);
        handlerAdapters = handlerAdapterMaps.values();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        Object handlerMapping = getHandlerMapping(req);
        if(handlerMapping == null){
            System.out.println("未匹配到handlerMapping");
            return;
        }
        HandlerAdapter handlerAdapter = getHandlerAdapter(handlerMapping);
        if(handlerAdapter == null){
            System.out.println("未匹配到handlerAdapter");
            return;
        }
        Object result = handlerAdapter.handle(req,resp,handlerMapping);

        PrintWriter writer = resp.getWriter();
        writer.println(result);
        writer.flush();
        writer.close();
    }

    protected Object getHandlerMapping(HttpServletRequest request) {
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                Object handler = mapping.getHandlerMapping(request.getRequestURI());
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

    protected HandlerAdapter getHandlerAdapter(Object handlerMapping) {
        if (this.handlerAdapters != null) {
            for (HandlerAdapter adapter : this.handlerAdapters) {
                boolean flag = adapter.supports(handlerMapping);
                if (flag) {
                    return adapter;
                }
            }
        }
        return null;
    }

}
lerMapping) {
        if (this.handlerAdapters != null) {
            for (HandlerAdapter adapter : this.handlerAdapters) {
                boolean flag = adapter.supports(handlerMapping);
                if (flag) {
                    return adapter;
                }
            }
        }
        return null;
    }

}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
手写Spring MVC是指自己实现一个简单的Spring MVC框架,而不是使用官方提供的Spring MVC框架。在手写Spring MVC时,你需要实现以下几个关键部分: 1. 创建一个前端控制器(Front Controller):前端控制器是整个请求处理过程的入口点,负责接收请求并进行路由。你可以使用Servlet作为前端控制器,接收所有的HTTP请求,并将它们分发给相应的控制器。 2. 定义控制器类:控制器类负责处理特定URL的请求,并根据请求参数进行相应的处理。你可以使用注解(如@RequestMapping)来定义控制器类和方法的映射关系。 3. 实现视图解析器(View Resolver):视图解析器负责解析控制器返回的逻辑视图名,并将其转换为具体的视图对象或视图模板。你可以使用模板引擎(如Thymeleaf、Freemarker等)来渲染动态内容。 4. 注册控制器和视图解析器:在前端控制器中,你需要注册所有的控制器类和视图解析器,以便能够正确地处理请求和渲染视图。 5. 处理请求和响应:在控制器中,你需要编写相应的方法来处理请求,并根据业务逻辑生成响应。你可以使用HttpServletRequest和HttpServletResponse对象来访问请求参数和生成响应。 6. 配置URL映射:你需要在配置文件中配置URL与控制器方法的映射关系,以便能够正确地将请求分发给对应的控制器。 手写Spring MVC的过程可以帮助你更好地理解Spring MVC框架的工作原理和核心组件。但请注意,手写一个完整的Spring MVC框架可能会比较繁琐和复杂,特别是对于初学者来说。因此,如果你只是想学习Spring MVC的基本原理和用法,我建议你先阅读官方文档或参考一些教程来快速入门。如果你确实有兴趣手写Spring MVC,你可以参考引用中的博客文章,里面提供了一个手写Spring MVC框架的实现示例。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [手写 springmvc](https://download.csdn.net/download/knight_black_bob/10207699)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [SpringMvc手写简单实现篇 - MVC完结篇](https://blog.csdn.net/qq_35551875/article/details/121811048)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值