您的位置:首页 > 编程语言 > Java开发

【WEB】初探Spring MVC框架

2016-04-12 17:21 633 查看
Spring MVC框架算是当下比较流行的Java开源框架。但实话实说,做了几年WEB项目,完全没有SpringMVC实战经验,乃至在某些交流场合下被同行严重鄙视“奥特曼”了。“心塞”的同时,只好默默的打开IDE从HelloWorld开始。

初步认识

宏观视野决定微观实现的质量,首先对Spring MVC框架组件及其流程做一个简单的认识。以下是从互联网中某Spring MVC教材扣来一张介绍图(懒得重复造轮子了):



从上图可以看出,Spring MVC框架的核心组件有DispatcherServlet、HandlerMapping、HandlerAdapter、Handler、ModelAndView、Model、View以及ViewResolver。既然是核心组件,怎么也得结合组件源码来探索个究竟吧:

DispatcherServlet

从名字可以看出,这就是一个Servlet实例,既然是Servlet,那当然是Srping MVC框架入口了,也是web.xml的一个Spring MVC配置项:

<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>


其中springmvc为servlet的自定义命名名称,其中Spring MVC配置文件也是默认名称为[servletName]-servlet.xml。

从DispatcherServlet源码看到到,DispatcherServlet的基础结构是:

DispatcherServlet extend FrameworkServlet

FrameworkServlet extend HttpServletBean

HttpServletBean extend HttpServlet

初略的看了一下DispatcherServlet的干系源码,主要做了两大部分,其一是初始化WEB容器的上下文信息和一些Spring MVC策略容器(如HandlerMapping、HandlerAdapter等),在启动WEB容器时可以通过控制台输出看到Spring MVC的一些初始化操作:

……
信息: Starting Servlet Engine: Apache Tomcat/6.0.13
2016-4-12 10:51:54 org.apache.catalina.core.ApplicationContext log
信息: Initializing Spring FrameworkServlet 'springmvc'
2016-4-12 10:51:54 org.springframework.web.servlet.FrameworkServlet initServletBean
信息: FrameworkServlet 'springmvc': initialization started
2016-4-12 10:51:54 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing WebApplicationContext for namespace 'springmvc-servlet': startup date [Tue Apr 12 10:51:54 CST 2016]; root of context hierarchy
2016-4-12 10:51:54 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [springmvc-servlet.xml]
2016-4-12 10:51:55 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@c7f06: defining beans [helloWorldAnnotation,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping#0,org.springframework.format.support.FormattingConversionServiceFactoryBean#0,org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#0,org.springframework.web.servlet.handler.MappedInterceptor#0,org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping#0,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter#0,org.springframework.web.servlet.view.InternalResourceViewResolver#0,/helloWorldController]; root of factory hierarchy
2016-4-12 10:51:55 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/helloWorldAnnotation] onto handler [com.maventest.springmvc.HelloWorldAnnotation@9c393d]
2016-4-12 10:51:55 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/helloWorldAnnotation.*] onto handler [com.maventest.springmvc.HelloWorldAnnotation@9c393d]
2016-4-12 10:51:55 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/helloWorldAnnotation/] onto handler [com.maventest.springmvc.HelloWorldAnnotation@9c393d]
2016-4-12 10:51:55 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/helloWorldController] onto handler [com.maventest.springmvc.HelloWorldController@85ce5a]
2016-4-12 10:51:55 org.springframework.web.servlet.FrameworkServlet initServletBean
信息: FrameworkServlet 'springmvc': initialization completed in 1222 ms
……


其二就是对MVC容器的流程控制,其主要流程控制方法是doDispatch,接下来结合源码针对此方法的一些重要操作进行分析和学习:

//检查请求是否是multipart(如文件上传),如果是则通过MultipartResolver解析
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;

//获取请求对应的mappedHandler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}

//获取请求对应的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

//由适配器执行处理器(调用处理器相应功能处理方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

//如果HandlerAdapter没有对应的ModelAndView响应,怎通过上下文获取默认对应的view,接着
applyDefaultViewName(request, mv);

//看applyPostHandle得知,这是定义拦截器的处理方法
mappedHandler.applyPostHandle(processedRequest, response, mv);

//解析视图并进行视图的渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);


从doDispatch方法流程分析可以看出,跟以上Spring MVC框架流程图的处理流程是一致的,整个DispatcherServlet组件就是Spring MVC的总流程控制器,再形象一点就如下图所示:



HandlerMapping

察人先察色,HandlerMapping中文意思就是“处理映射”,作为一个强大的开源框架,命名自然不会乱来,通过名称就大概知其所以。看看getHandle这个方法:

protected HandlerExecutionChain getHandler(HttpServletRequest request)…


先不看源码,就大概可以猜个一二,这是通过request参数,获取一个对应的的处理类,而这个HandlerExecutionChain就是这个返回的处理类。这个HandlerMapping已经在项目启动的时候跟随Servlet一同初始化了:

initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);


而getHandler方法可以通过request获取请求的所有信息,包括请求方法、URL路径等,就可以通过这个映射容器找出对应的处理类了。下面再看看这个HandlerExecutionChain响应类属性:

private final Object handler;
private HandlerInterceptor[] interceptors;
private List<HandlerInterceptor> interceptorList;
private int interceptorIndex = -1;


它包括了请求处理的所有拦截实例和核心处理handler实例,这都会在DispatcherServlet往下几个步骤会使用到的,具体可以往上回看DispatcherServlet的处理流程。

HandlerAdapter

还是从名称理解开始,HandlerAdapter中文意思就是处理对象适配器,按意思就是说Spring MVC有很多个Handler处理对象,这个处理器实际就是一个Handler代理。那么如果不自己定义Handler代理的话,那默认有多少个呢,那就可以看看DispatcherServlet.properties这个配置文件了:

Name:org.springframework.web.servlet.HandlerAdapter
Value:org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter


哦,原来默认有HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter一共三个默认的Handler代理。那他们分别有什么用呢,看看我自己亲自动手做过Spring MVC HelloWorld实例就很明白了,我通过两种方式实现了两个HelloWorld Handler,一个在配置文件配置的bean:

<bean name="/helloWorldController" class="com.maventest.springmvc.HelloWorldController"/>


public class HelloWorldController implements Controller{

public ModelAndView handleRequest(HttpServletRequest arg0,
HttpServletResponse arg1) throws Exception {

ModelAndView mv = new ModelAndView();
mv.addObject("message", "Hello World!,i am HelloWorldController.");
mv.setViewName("hello");

return mv;
}
}


而另一个是通过注解实现的HelloWorld Handler:

@Controller
public class HelloWorldAnnotation{

@RequestMapping(value="/helloWorldAnnotation")
public String hello(ModelMap model){

model.addAttribute("message", "Hello, World!I am HelloWorldAnnotation.");

return "hello";
}
}


这两种方式就是分别通过SimpleControllerHandlerAdapte和AnnotationMethodHandlerAdapter处理的,那这样一说就很明白了。另外这三个个Handler代理都实现了HandlerAdapter接口,就是Spring MVC规定了Handler代理的规则,分别有以下定义方法:

public interface HandlerAdapter {
//判断处理适配器是不是支持该Handler
boolean supports(Object handler);
//调用对应的Handler中适配到的方法,并返回一个ModelView
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
//这个暂时还没看懂具体想干什么(不是重点,暂时放下)
long getLastModified(HttpServletRequest request, Object handler);
}


其中判断是否找到合适的Handler代理就靠这个supports方法的具体实现,如果适配成功,这个代理会替这个Handler实现业务路基处理。再简单这三个代理的supports实现:

//HttpRequestHandlerAdapter
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}

//SimpleControllerHandlerAdapte
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}

//AnnotationMethodHandlerAdapter
@Override
public boolean supports(Object handler) {
return getMethodResolver(handler).hasHandlerMethods();
}


再来简单分别说说以上三个代理对handle方法的实现:

HttpRequestHandlerAdapter和SimpleControllerHandlerAdapte都是直接调用handler的handleRequest方法,而AnnotationMethodHandlerAdapter稍微复杂一点,它是通过注释和反射获取相关自定义信息,进行匹配和封装,具体可自行参考其源码。

Handler

这就是自己实现的具体业务处理类了,上文提到很多,不用多说了。

ModelAndView

通过handler代理完成业务流程后返回一个ModelAndView对象,从名称就大概可以知道这是一个装载的数据模型(Model)和数据视图的对象(View)。

Model

从源码可以看出,model集成了LinkedHashMap<String,Object>类,这个model对象装载了所有在Handler响应给页面的数据。例如在我自己例子中的message数据:

model.addAttribute("message", "Hello, World!I am HelloWorldAnnotation.");


这些数据将会在页面上通过JSTL获取。

View

View接口表示一个响应给用户的视图,例如jsp文件,pdf文件,html文件等,该接口定义如下:

public interface View {
//HttpServletRequest中的属性名,其值为响应状态码
String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
//HttpServletRequest中的属性名,前一篇文章用到了该变量,它的对应值是请求路径中的变量,及@PathVariable注解的变量
String PATH_VARIABLES = View.class.getName() + ".pathVariables";
//该视图的ContentType
String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";
//获取该视图ContentType
String getContentType();
//渲染该视图
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}


该接口只有两个方法定义,分别表明该视图的ContentType和如何被渲染。Spring中提供了丰富的视图支持,并且可以自定义视图。

ViewResolver

ViewResolver接口定义了如何通过view 名称来解析对应View实例的行为。例如在我自己的一个注解Handler实现里面,我返回的是“hello”view name字符串,意思就是响应到对应的hello.jsp视图(在springmvc-servlet.xml配置文件定义了):

//controller
@RequestMapping(value="/helloWorldAnnotation")
public String hello(ModelMap model){
…
return "hello";
}


springmvc-servlet.xml:

<!-- ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>


在这里,我选择了默认Spring MVC JSP的实现类InternalResourceViewResolver。再来看看ViewResolver的接口定义:

public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}


该接口只有一个方法,通过view name 解析出View。还是以我例子为准,通过“hello”view name字符串,通过ViewResolver. resolveViewName方法生成View实例。再通过View实例的render方法渲染该视图,剩下的具体细节可自行学习。

总结

两天学习下来,终于对Spring MVC有个大概的了解。毕竟是一个通用的框架,除了默认的实现,Spring MVC框架还定义了大量的标准可供用户自定义实现,整体也算是采用了Open-Closed原则,扩展性好,但有不失整体优雅。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: