# 手写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);
}
}
工具类 →