# 手写Spring MVC

Spring MVC

Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,可以选择是使用内置的 Spring Web 框架还是 Struts 这样的 Web 框架。通过策略接口,Spring 框架是高度可配置的,而且包含多种视图技术,例如 JavaServer Pages(JSP)技术、Velocity、Tiles、iText 和 POI。Spring MVC 框架并不知道使用的视图,所以不会强迫您只使用 JSP 技术。Spring MVC 分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。

# Spring MVC 核心流程

protected void initStrategies(ApplicationContext context) {
	//用于处理上传请求。处理方法是将普通的request包装成MultipartHttpServletRequest,后者可以直接调用getFile方法获取File.
	initMultipartResolver(context);
	//SpringMVC主要有两个地方用到了Locale:一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候。
	initLocaleResolver(context); 
	//用于解析主题。SpringMVC中一个主题对应一个properties文件,里面存放着跟当前主题相关的所有资源、
	//如图片、css样式等。SpringMVC的主题也支持国际化, 
	initThemeResolver(context);
	//用来查找Handler的。
	initHandlerMappings(context);
	//从名字上看,它就是一个适配器。Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。
	//如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情
	initHandlerAdapters(context);
	//其它组件都是用来干活的。在干活的过程中难免会出现问题,出问题后怎么办呢?
	//这就需要有一个专门的角色对异常情况进行处理,在SpringMVC中就是HandlerExceptionResolver。
	initHandlerExceptionResolvers(context);
	//有的Handler处理完后并没有设置View也没有设置ViewName,这时就需要从request获取ViewName了,
	//如何从request中获取ViewName就是RequestToViewNameTranslator要做的事情了。
	initRequestToViewNameTranslator(context);
	//ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。
	//View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。
	initViewResolvers(context);
	//用来管理FlashMap的,FlashMap主要用在redirect重定向中传递参数。
	initFlashMapManager(context); 
}

自定义类注解ExtController

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtController {
    String value() default "";
}

自定义方法注解ExtRequestMapping

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtRequestMapping {
    String value() default "";
}


控制层

@ExtController
@ExtRequestMapping("/ext")
public class IndexController {

    @ExtRequestMapping("/index")
    public String index(String name,Integer age){
        System.out.println("hello world index");
        return "index";
    }

    
}

jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>SpringBoot</title>
</head>
<body>
	<h1>SpringBoot中可以拦截自定义实现springmvc跳转了</h1>
</body>
</html>

maven

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xianyu</groupId>
    <artifactId>fascade</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>fascade</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

核心类MyDispatcherServlet

/**
 * @create.date: 2019/3/10
 * @comment: <p>自定义SpringMVC</p>
 * @author: Jackie.Yang
 * @see:com.xianyu.facade.config
 */

@WebServlet(name = "MyDispatcherServlet",value = "/ext/index")
public class MyDispatcherServlet extends HttpServlet {
	//存放 bean 名字和实例化类对象
    ConcurrentHashMap<String,Object> mvcBean = new ConcurrentHashMap<>();
	//存放 bean 名字和对应方法名字
    ConcurrentHashMap<String,String> urlMethod = new ConcurrentHashMap<>();
	//存放 bean 名字和实例化类对象
    ConcurrentHashMap<String,Object> urlController = new ConcurrentHashMap<>();

    public void init(){

        List<Class<?>> tClass  = ClassUtil.getClasses("com.xianyu.facade.modules.controller");

        for (Class<?> beanClass : tClass){

            ExtController extController = beanClass.getAnnotation(ExtController.class);

            if(null != extController){

                try {

                    mvcBean.put(beanClass.getName(), beanClass.newInstance());
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }


            }
        }
		//处理url映射关系,放到容器中
        handlerMapping();




    }

  public void handlerMapping(){
	String baseUrl = "";
	// 循环Map 集合
	for (Map.Entry beanMap : mvcBean.entrySet()){

		beanMap.getKey();
		Object object = beanMap.getValue();
		//判断类上是否有ExtRequestMapping注解
		ExtRequestMapping extRequestMapping = object.getClass().getAnnotation(ExtRequestMapping.class);
		if(extRequestMapping!=null){
			baseUrl = extRequestMapping.value();
		}

		Method [] method =  object.getClass().getMethods();
		for (Method beanMethod : method){
			//拿到方法上的注解
			ExtRequestMapping extRequestMapping1 = beanMethod.getAnnotation(ExtRequestMapping.class);
			if(extRequestMapping1!=null){
				// 组装完整的URL
				String subUrl = baseUrl + extRequestMapping1.value();

				urlMethod.put(subUrl, beanMethod.getName());

				urlController.put(subUrl, beanMap.getValue());
			}
		}
	}
}


    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String url = req.getRequestURI();

        Object object = urlController.get(url);

        if(object == null){

            resp.getWriter().println("can not found url!");
        }


        String crul = urlMethod.get(url);

        if(crul == null){

            resp.getWriter().println(" this method nof found!");
        }

        try {
            Method method  =  object.getClass().getDeclaredMethod(crul,String.class,Integer.class);

            String result = (String)method.invoke(object,"jackie.yang",30);

            viewsResolver(result,req,resp);

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }


    }


    public void viewsResolver(String result,HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String prefix = "/WEB-INF/views/";

        String subfix = ".jsp";

        req.getRequestDispatcher(prefix+result+subfix).forward(req, resp);


    }
}

启动类

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@ServletComponentScan
public class FacadeApplication {
    public static void main(String[] args) {

        SpringApplication.run(FacadeApplication.class, args);
    
    }
}