课程概要
spring mvc 设计思想与体系结构组成
mvc 执行流程解析
自定义MVC的框架实现
spring mvc 功能特性 回顾servlet 与jsp 执行过程
流程说明:
请求Servlet
处理业务逻辑
设置业务Model
forward jsp Servlet
jsp Servlet 解析封装html 返回
spring mvc 功能特性: spring mvc本质上还是在使用Servlet处理,并在其基础上进行了封装简化了开发流程,提高易用性、并使用程序逻辑结构变得更清晰
基于注解的URL映射
表单参数映射,参数接受
全局统一异常拦截处理
文件上传与下载
MVC拦截器 HandlerInterceptor
请求处理流程
Web 发起request请求
dispatchServlet通过HandlerMapping映射关系查找到对应的Handler
HandlerAdapter适配handler并且执行handler逻辑
handler设置业务model
handler设置返回页面
dispatchServlet接受到模型视图对象
dispatchServlet通过viewAdapter找到view
view基于模板与模型将mv渲染成写成html
渲染结果通过response返回Web前端
使用示例 目录结构
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <dependencies > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.11</version > <scope > test</scope > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > javax.servlet-api</artifactId > <version > 3.1.0</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 4.3.8.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-web</artifactId > <version > 4.3.8.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 4.3.8.RELEASE</version > </dependency > </dependencies >
spring-mvc.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc ="http://www.springframework.org/schema/mvc" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <bean name ="/hello" class ="study.springmvc.controller.MyController" /> <bean name ="/hello1" class ="study.springmvc.controller.MyRequestHandler" /> <mvc:annotation-driven /> <context:component-scan base-package ="study.springmvc" /> <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/page/" /> <property name ="suffix" value =".jsp" /> <property name ="viewClass" value ="org.springframework.web.servlet.view.JstlView" /> </bean > </beans >
controller 1 2 3 4 5 6 7 8 public class MyController implements Controller { @Override public ModelAndView handleRequest (HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView modelAndView = new ModelAndView("userView" ); modelAndView.addObject("name" , "24kHandsome" ); return modelAndView; } }
userView.jsp 1 2 3 4 5 6 7 8 9 10 11 12 13 <%@ 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 > Insert title here</title > </head > <body > the man is ${name}</body > </html >
mvc 体系结构详解 spring mvc 框架解决的问题 从技术角度去思考 任何一个现存的框架都有其存在理由,而这个理由就是解决实际的问题。或者提供更好的解决问题的方案。spring mvc 它解决了什么问题呢?
URL映射
表单参数映射
调用目标Control
数据模型映射视图解析
异常处理
上术解决在spring mvc 中都体现在如下组件当中
HandlerMapping ‘hændlə ‘mæpɪŋ
HandlerAdapter ‘hændlə ə’dæptə
ViewResolver vjuː riː’zɒlvə
view
HandlerExceptionResolver ‘hændlə ɪk’sepʃ(ə)n riː’zɒlvə
HandlerInterceptor ‘hændlə ɪntə’septə
其对应具体uml如下 图:
mvc 各组件执行流程
SpringMVC默认引入组件 根据 org/springframework/web/servlet/DispatcherServlet.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 org.springframework.web.servlet.LocaleResolver =org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver =org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping =org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping org.springframework.web.servlet.HandlerAdapter =org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver =org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator =org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver =org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager =org.springframework.web.servlet.support.SessionFlashMapManager
HandlerMapping 详解 其为mvc 中url路径与Control对像的映射,DispatcherServlet 就是基于此组件来寻找对应的Control,如果找不到就会报 No mapping found for HTTP request with URI的异常。
HandlerMapping 接口结构分析:
HandlerMapping 作用是通过url找到对应的Handler ,但其HandlerMapping.getHandler()方法并不会直接返回Handler 对像,而是返回 HandlerExecutionChain 对像在通过 HandlerExecutionChain.getHandler() 返回最终的handler
目前主流的三种mapping 如下:
SimpleUrlHandlerMapping:基于手动配置 url 与control 映谢
BeanNameUrlHandlerMapping: 基于ioc name 中已 “/“ 开头的Bean时行 注册至映谢.
RequestMappingHandlerMapping:基于@RequestMapping注解配置对应映谢
SimpleUrlHandlerMapping SimpleUrlHandlerMapping体系结构:基于手动配置 url 与control 映谢
初始化SimpleUrlHandlerMapping流程关键源码:
1 2 3 4 5 6 >org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#setUrlMap >org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#initApplicationContext >org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#registerHandlers >org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#registerHandler()
获取 Handler流程关键源码:
1 2 3 4 5 6 7 8 9 >org.springframework.web.servlet.DispatcherServlet#doService>org.springframework.web.servlet.DispatcherServlet#doDispatch >org.springframework.web.servlet.DispatcherServlet#getHandler>org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler >org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getHandlerInternal >org.springframework.web.util.UrlPathHelper#getPathWithinApplication >org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#lookupHandler >org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain
BeanNameUrlHandlerMapping BeanNameUrlHandlerMapping 实现上与 SimpleUrlHandlerMapping 一至,唯一区别在于 继承自AbstractDetectingUrlHandlerMapping ,通过对应detectHandlers 可以在无配置的情况下发现url 与handler 映射。
RequestMappingHandlerMapping (后续补充)其基于注解实现,在后续章节讲解注解映谢的时候在详细讲
HandlerAdapter 在 AbstractUrlHandlerMapping 我们可以看到存储handler 的Map 值类型是Object ,是否意味着所有的类都可以做来Handler 来使用?
Handler 对应类型如下如图:
Controller
HttpRequestHandler
HttpServlet
@RequestMapping
可以看出 Handler 没有统一的接口,当dispatchServlet获取当对应的Handler之后如何调用呢?调用其哪个方法?这里有两种解决办法,
一是用instanceof 判断Handler 类型然后调用相关方法 。
二是通过引入适配器实现,每个适配器实现对指定Handler的调用。
spring 采用后者。
HandlerAdapter详解
这里spring mvc 采用适配器模式来适配调用指定Handler,根据Handler的不同种类采用不同的Adapter,其Handler与 HandlerAdapter 对应关系如下:
Handler类别
对应适配器
描述
Controller
SimpleControllerHandlerAdapter
标准控制器,返回ModelAndView
HttpRequestHandler
HttpRequestHandlerAdapter
业务自行处理 请求,不需要通过modelAndView 转到视图
Servlet
SimpleServletHandlerAdapter
基于标准的servlet 处理
HandlerMethod
RequestMappingHandlerAdapter
基于@requestMapping对应方法处理
HandlerAdapter 接口方法
HandlerAdapter 接口结构图
上述例子中当IOC中实例化这些类之后DispatcherServlet就会通过org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter() 方法查找对应handler的适配器 ,如果找不到就会报 如下异常 。
javax.servlet.ServletException: No adapter for handler [com.tuling.control.SimpleControl@3c06b5d5]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handlerorg.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(DispatcherServlet.java:1198)org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
ViewResolver 与View 找到应的Adapter 之后就会基于适配器调用业务处理,处理完之后业务方会返回一个ModelAndView ,在去查找对应的视图进行处理。
其在org.springframework.web.servlet.DispatcherServlet#resolveViewName() 中遍历 viewResolvers 列表查找,如果找不到就会报一个 Could not resolve view with name 异常。
BeanNameViewREsolver示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 添加自定义视图:public class MyView implements View { @Override public void render (Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { response.getWriter().print("hello luban good man." ); } } 修改视图跳转方法 :public ModelAndView handleRequest (HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView mv = new ModelAndView("myView" ); mv.addObject("name" , "24kHandsome is good man" ); return mv; }
1 2 3 4 5 6 7 8 9 配置视图解析器:<bean name ="myView" class ="com.tuling.control.MyView" /> <bean class ="org.springframework.web.servlet.view.BeanNameViewResolver" /> <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/page/" /> <property name ="suffix" value =".jsp" /> <property name ="viewClass" value ="org.springframework.web.servlet.view.InternalResourceView" /> </bean >
在下一步就是基于ViewResolver**.**resolveViewName() 获取对应View来解析生成Html并返回 。
对应VIEW结构如下:
MVC异常拦截处理HandlerExceptionResolver HandlerExceptionResolver的类关系图
ResponseStatuExceptionResolver
用于解析带@ResponseStatus的自定义异常
DefaultHandlerExceptionResolver
SimpleMappingExceptionResolver
该组件用于指示 当出现异常时 mvc 该如何处理。 dispatcherServlet 会调用org.springframework.web.servlet.DispatcherServlet#processHandlerException() 方法,遍历 handlerExceptionResolvers 处理异常,处理完成之后返回errorView 跳转到异常视图。
自定义异常解析器
1 2 3 4 5 6 7 8 9 10 11 public class MyHandlerExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView mv = new ModelAndView("argumentError" ); mv.addObject("stack" ,ex); return mv; } }
SimpleMappingExceptionResolver 示例:
1 2 3 4 5 6 7 8 9 10 11 <bean class ="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" > <property name ="defaultErrorView" value ="error" /> <property name ="defaultStatusCode" value ="500" /> <property name ="exceptionMappings" > <map > <entry key ="java.lang.RuntimeException" value ="error" /> <entry key ="java.lang.IllegalArgumentException" value ="argumentError" /> </map > </property > </bean >
提问: IllegalArgumentException 是 RuntimeException子类,如果IllegalArgumentException 异常同时满足映射的两个条件,这时会怎么选择跳转的视图?
答案是会选择IllegalArgumentException 这个子类,而不是根据先后顺序
HandlerInterceptor 调用拦截 HandlerInterceptor 用于对请求拦截,
HandlerInterceptor 常常用于对某些路径进行特殊配置,可用于
参数打印 业务处理前 后
登陆/权限拦截 业务处理前
数字验签 业务处理前
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class SimpleHandlerInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle" ); return true ; } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle" ); } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion" ); } }
preHandle :业务处理前执行
postHandle:业务处理后(异常则不执行)
afterCompletion:视图处理后
Filter与Interceptor联系与区别
拦截器不依赖servlet容器,过滤器依赖于servlet容器。
拦截器只能对action(handler)起作用,而过滤器可以对几乎所有的请求起作用(可以保护资源)。
拦截器可以访问action上下文,堆栈里面的对象,而过滤器不可以。
执行顺序:过滤前-拦截前-Action处理-拦截后-过滤后。
细粒度的不同 拦截器提供更精细的控制,可以在controller对请求处理之前或之后被调用,也可以在渲染视图呈现给用户之后调用
中断链执行的难易程度不同 拦截器可以 preHandle方法内返回 false 进行中断,过滤器就比较复杂,需要处理请求和响应对象来引发中断,需要额外的动作,比如将用户重定向到错误页面
三者使用场景 三者功能类似,但各有优势,从过滤器–》拦截器–》切面,拦截规则越来越细致,执行顺序依次是过滤器、拦截器、切面。
**一般情况下数据被过滤的时机越早对服务的性能影响越小 **,因此我们在编写相对比较公用的代码时,优先考虑过滤器,然后是拦截器,最后是aop。
比如权限校验,一般情况下(经供参考)
所有的请求都需要做登陆校验,此时就应该使用过滤器在最顶层做校验
日志记录,一般日志只会针对部分逻辑做日志记录,而且牵扯到业务逻辑完成前后的日志记录,因此使用过滤器不能细致地划分模块,此时应该考虑拦截器,然而拦截器也是依据URL做规则匹配,因此相对来说不够细致,因此我们会考虑到使用AOP实现,AOP可以针对代码的方法级别做拦截,很适合日志功能。
@RequestMapping的使用与原理 演示基于注解配置mvc mapping
1 2 3 4 5 6 7 8 9 10 11 <mvc:annotation-driven /> <context:component-scan base-package ="study.springmvc" /> <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/page/" /> <property name ="suffix" value =".jsp" /> <property name ="viewClass" value ="org.springframework.web.servlet.view.JstlView" /> </bean >
1 2 3 4 5 6 public class MyRequestHandler implements HttpRequestHandler { @Override public void handleRequest (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("userView" ).forward(request,response); } }
提问
为什么基于<mvc:annotation-driven /> 配置就能实现mvc 的整个配置了
之前所提到的 handlerMapping 、与handlerAdapter 组件都不适用了?只要查看以类的源就可以知晓其中原因:认识 NamespaceHandler 接口查看 MvcNamespaceHandler查看AnnotationDrivenBeanDefinitionParser
结论 :
在<mvc:annotation-driven /> 对应的解析器,自动向ioc 里面注册了两个BeanDefinition。
分别是:RequestMappingHandlerMapping 与BeanNameUrlHandlerMapping
.png)
RequestMappingHandlerMapping :URL 映射器
RequestMappingHandlerAdapter:执行适配器
InvocableHandlerMethod:Control目标对象,包含了control Bean 及对应的method 对像,及调用方法
HandlerMethodArgumentResolverComposite:参数处理器
ParameterNameDiscoverer:参数名称处理器
HandlerMethodReturnValueHandlerComposite:返回结构处理器
查找mapping源码解析
1 2 3 4 5 org.springframework.web.servlet.DispatcherServlet#getHandler >org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler >org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod >org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl
调用执行过程源码解析
1 2 3 4 5 >org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle >org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal >org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod >org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest >org.springframework.web.method.support.InvocableHandlerMethod#doInvoke
自定义MVC的框架实现 框架需求与目标 框架需求: 框架的需求包含功能性需求 和非功能性需求 ,功能性需求框架本身所提供的功能,而非功能性需求通常也指定体验性需求,即该框架对于开发者而言,是否易上手,是否需要较多的学习成本,以及在开发时需要过多的配置。
有个时候两者是互相矛盾冲突的,比如当我们想让框架支持更的功能时,那么它的结构设计将会更复杂,抽像的层次将会越多,带来的负面影响时对框架使用者的学习成本增加了。
到底该选择更多的功能,还是更好的体验?这就需要框架作者要作出准确的定位与范围。
定位是该框架要完成什么目标?范围是实现该目标需实现哪些功能?两者清晰之后 自然知道哪些是必须做的,哪些是可以做的。而体验则是在保证必须功能的情况越高越好,甚至可以为了提供体验可以牺牲部分功能的完整性。
功能性需求用例图:
URL映射
参数自动解析
请求调用
视图支持
基于返回的结果跳转至视图处理
支持的有jsp 视图,freemarke视图,Json视图
统一异常处理
非功能性需求与目标:
框架设计与编码实现 框架环境依赖:
框架名称:study-myspringMVC
jdk:1.8
依赖包:spring、freemarker、java-servlet-api
框架流程分解:
实现组件:
FreemakerView
HandlerServlet (原DispatchServlet)
MvcBeanFactory (类似HandlerMapping 持有 url对应handler的map)
Mvc bean 工厂 ,从spring ioc 中扫描类装载MVC Bean
MvcBean (原HandlerMethod,因为只有一种handler,所以无需HandlerAdaptor)
MvcMaping (原@RequestMapping)
MVC注解,用于注解MVC Bean,并配置url 路径
代码:
1、模拟DispatchServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 public class HandlerServlet extends HttpServlet { private WebApplicationContext context; private MvcBeanFactory beanFactory; final ParameterNameDiscoverer parameterUtil = new LocalVariableTableParameterNameDiscoverer(); private Configuration freemarkeConfig; @Override public void init () throws ServletException { context = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); beanFactory = new MvcBeanFactory(context); Configuration freemarkeConfig = null ; try { freemarkeConfig = context.getBean(Configuration.class); } catch (NoSuchBeanDefinitionException e) { } if (freemarkeConfig == null ) { freemarkeConfig = new Configuration(Configuration.VERSION_2_3_23); freemarkeConfig.setDefaultEncoding("UTF-8" ); freemarkeConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); try { freemarkeConfig.setDirectoryForTemplateLoading(new File(getServletContext().getRealPath("/WEB-INF/ftl/" ))); } catch (IOException e) { throw new RuntimeException(e); } } this .freemarkeConfig = freemarkeConfig; } @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doHandler(req, resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doHandler(req, resp); } public void doHandler (HttpServletRequest req, HttpServletResponse resp) { String uri = req.getServletPath(); if (uri.equals("/favicon.ico" )) { return ; } MvcBeanFactory.MvcBean mvcBean = beanFactory.getMvcBean(uri); if (mvcBean == null ) { throw new IllegalArgumentException(String.format("not found %s mapping" , uri)); } Object[] args = buildPrams(mvcBean, req, resp); try { Object result = mvcBean.run(args); processResult(result, resp); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (TemplateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private Object[] buildPrams(MvcBeanFactory.MvcBean mvcBean, HttpServletRequest req, HttpServletResponse resp) { Method method = mvcBean.getTargetMethod(); List<String> paramNames = Arrays.asList(parameterUtil.getParameterNames(method)); Class<?>[] paramTypes = method.getParameterTypes(); Object[] args = new Object[paramTypes.length]; for (int i = 0 ; i < paramNames.size(); i++) { if (paramTypes[i].isAssignableFrom(HttpServletRequest.class)) { args[i] = req; } else if (paramTypes[i].isAssignableFrom(HttpServletResponse.class)) { args[i] = resp; } else { if (req.getParameter(paramNames.get(i)) == null ) { args[i] = null ; } else { args[i] = convert(req.getParameter(paramNames.get(i)), paramTypes[i]); } } } return args; } }
2、模拟@RequstMapping:MvcMapping和Mvc子容器持有HandleMapping:MvcBeanFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 @Retention(RetentionPolicy.RUNTIME) @Target(value = ElementType.METHOD) public @interface MvcMapping { public String value () ; public String contentType () default "JSON" ; }public class MvcBeanFactory { private ApplicationContext applicationContext; public MvcBeanFactory (ApplicationContext applicationContext) { Assert.notNull(applicationContext, "argument 'applicationContext' must not be null" ); this .applicationContext = applicationContext; loadApiFromSpringBeans(); } private HashMap<String, MvcBean> apiMap = new HashMap<String, MvcBean>(); private void loadApiFromSpringBeans () { apiMap.clear(); String[] names = applicationContext.getBeanDefinitionNames(); Class<?> type; for (String name : names) { type = applicationContext.getType(name); for (Method m : type.getDeclaredMethods()) { MvcMapping MvcMapping = m.getAnnotation(MvcMapping.class); if (MvcMapping != null ) { addApiItem(MvcMapping, name, m); } } } } public MvcBean getMvcBean (String apiName) { return apiMap.get(apiName); } private void addApiItem (MvcMapping MvcMapping, String beanName, Method method) { MvcBean apiRun = new MvcBean(); apiRun.apiName = MvcMapping.value(); apiRun.targetMethod = method; apiRun.targetName = beanName; apiRun.context = this .applicationContext; apiMap.put(MvcMapping.value(), apiRun); } public boolean containsApi (String apiName, String version) { return apiMap.containsKey(apiName + "_" + version); } public ApplicationContext getApplicationContext () { return applicationContext; } public static class MvcBean { String apiName; String targetName; Object target; Method targetMethod; ApplicationContext context; public Object run (Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (target == null ) { target = context.getBean(targetName); } return targetMethod.invoke(target, args); } public Class<?>[] getParamTypes() { return targetMethod.getParameterTypes(); } public String getApiName () { return apiName; } public String getTargetName () { return targetName; } public Object getTarget () { return target; } public Method getTargetMethod () { return targetMethod; } } }
3、FreeMaker试图解析器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class FreemarkeView { private String ftlPath; private Map<String, Object> models = new HashMap<>(); public FreemarkeView (String ftlPath) { this .ftlPath = ftlPath; } public FreemarkeView (String ftlPath, Map<String, Object> model) { this .ftlPath = ftlPath; this .models = model; } public void setModel (String key, Object model) { models.put(key, model); } public void removeModel (String key) { models.remove(key); } public String getFtlPath () { return ftlPath; } public void setFtlPath (String ftlPath) { this .ftlPath = ftlPath; } public Map<String, Object> getModels () { return models; } public void setModels (Map<String, Object> models) { this .models = models; } }
4、web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns ="http://java.sun.com/xml/ns/javaee" xmlns:web ="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation ="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id ="WebApp_ID" version ="3.0" > <display-name > my spring mvc </display-name > <context-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:spring.xml</param-value > </context-param > <listener > <description > spring监听器</description > <listener-class > org.springframework.web.context.ContextLoaderListener</listener-class > </listener > <servlet > <servlet-name > dispatcherServlet</servlet-name > <servlet-class > com.study.mvc.HandlerServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > dispatcherServlet</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > <welcome-file-list > <welcome-file > /index.html</welcome-file > </welcome-file-list > </web-app >