故事是这样开始的。。。。。
sprng和springmvc是属于两个不同的容器
Spring的容器属于父容器,springmvc的容器是子容器
父容器不能访问子容器的bean,但是子容器可以访问父容器的bean
因此,不能再spring的配置中扫面所有的包,这样的话,springmvc就扫不到controller了
这样的话,当请求到达时,springmvc没有handler,就无法做出响应。
首先来说web.xml中的一个配置
<listener> <!--用来设定Listener接口-->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
这个配置定义了一个上下文加载监听器,当监听到web程序启动时就会执行该类的方法:
/**
* Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
可以看到执行了contextLoader的initWebApplicationContext(servletContext)该方法会初始化web的上下文
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//省掉了部分代码
//这个方法的用途主要是用来解决Spring共享环境的,即,如果我们有多个WAR包部署在同一个服务器上,而且这些WAR都共享某一套业务逻辑层。如何共享//一套业务逻辑包配置而不要每个WAR都单独配置,这时我们就可能需要Spring的共享环境了。
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);
//将得到的web上下文设为全web域的属性
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
我们来看下是怎么创建web上下文的
protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
//通过servlet的初始化参数得到定义的contextclass
Class<?> contextClass = determineContextClass(sc);
//首先判断自定义的或默认的webcontext类是不是ConfigurableWebApplicationContext的子类
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
try {
String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(contextPath));
}
catch (Exception ex) {
throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
}
//为wac设置相应的属性
wac.setParent(parent);
wac.setServletContext(sc);
//设置contextConfigLocation,这个属性在web.xml中配置
wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
customizeContext(sc, wac);
//最核心的方法,完成bean的初始化和实例化
wac.refresh();
return wac;
}
来看下refresh()这个核心的方法
//待更新。。。。。。
到此为止在web.xml中配置的contextConfigLocation的spring.xml已经加载完毕,已经加载完了spring.xml中配置的所有的bean对象。
接下来就是要加载web.xml中的springmvc的内容了
先看一下web.xml的相应配置
<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:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
从以上配置可以看到springmvc其实就是一个servlet而已,因为它的配置和普通的servlet一样,唯一不同的,就是servlet-class也就是DispatcherServlet会拦截所有的符合的请求,并执行处理操作,最后返回试图。
首先加载httpServletBean的init()方法
@Override
public final void init() throws ServletException {
// Set bean properties from init parameters.
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
// Let subclasses do whatever initialization they like.
initServletBean();
}
加载HttpServletBean的initServletBean()方法,它没有写这个方法,于是调用它的子类FrameworkServlet的该方法:
protected final void initServletBean() throws ServletException {
try {
this.webApplicationContext = initWebApplicationContext();
//* This method will be invoked after any bean properties have been set and
* the WebApplicationContext has been loaded. The default implementation is empt;
* subclasses may override this method to perform any initialization they require.
initFrameworkServlet();
}
}
主要就是这个两个方法,先看下initWebApplicationContext()
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
这里最核心的还是configureAndRefreshWebApplicationContext(cwac)
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
// Generate default id...
ServletContext sc = getServletContext();
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName +
"." + getServletName());
else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + getServletName());
}
wac.setServletContext(getServletContext());
//设置该servlet的servletConfig对象
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
}
就这样springmvc的上下文也解析完了。
DispatcherServlet 负责拦截所有request,并返回试图
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
try {
ModelAndView mv;
boolean errorView = false;
try {
processedRequest = checkMultipart(request);
// Determine handler for the current request.
//得到这个request,具体来说是url对应的处理类,并将该类以及相应的inteceptors封装在HandlerExecuteChain类中,这个地方使用了策略模式,优点是十分方便的扩展
mappedHandler = getHandler(processedRequest, false)
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
// Apply preHandle methods of registered interceptors.
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
if (interceptors != null) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
// Apply postHandle methods of registered interceptors.
if (interceptors != null) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
finally {
// Clean up any resources used by a multipart request.
if (processedRequest != request) {
cleanupMultipart(processedRequest);
}
}
}
首先来说下HandlerExecuteChain
mappedHandler = getHandler(processedRequest, false);
//首先这里的实现原理是循环调用springmvc默认的或者是自定义的(优先级高)HandlerMapping
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
最常用的就是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping分别通过配置的
bean的id以及添加的注解获取对应的处理器类,然后将相应的inteceptor添加到HandlerExecuteChain
对象中去,这样的做法可以符合框架设计的开闭原则,保证不会修改代码,只需要添加然后进行相应的配置就好了。
这里使用了策略模式。
接下来是handlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
在这里使用了适配器原则,首先说一下HandlerAdapter,大家想一下,如果没有这个类的话,那我们在执行处理器方法时,是不是需要
使用大量的if...else...而且还不能扩展,所以这里使用了适配器模式,每一个处理器类都会有一个对应的HandlerAdapter,每一个HandlerAdapter类都会有一个Handle方法和supports方法
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (ha.supports(handler)) {
return ha;
}
}
}
可以看到这里会遍历springmvc定义的HandlerAdapter,如果是这个处理器对应的适配器,就返回这个适配器。这样的做法就十分有利于扩展
开始执行处理器方法返回一个ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
这是的AnnotationMethodHandlerAdapter的handle方法,最后返回一个ModelAndView
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//先判断是不是被@SesionAttribute注解了,分别处理
Class<?> clazz = ClassUtils.getUserClass(handler);
Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
if (annotatedWithSessionAttributes == null) {
annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
}
if (annotatedWithSessionAttributes) {
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
// Prepare cached set of session attributes names.
}
else {
// Uses configured default cacheSeconds setting.
checkAndPrepare(request, response, true);
}
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandlerMethod(request, response, handler);
}
}
}
return invokeHandlerMethod(request, response, handler);
}
接下来是执行invokeHandlerMethod方法
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//得到处理器的处理方法
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
//得到请求对象
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ExtendedModelMap implicitModel = new BindingAwareModelMap()
//得到处理器方法返回的模型数据
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel)
//将模型数据和视图信息封装进ModelAndView对象中
ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
//做一些更新操作
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
return mav;
}
首先看下invokeHandlerMethod方法
public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
try {
boolean debug = logger.isDebugEnabled();
//查找@sessionAttribute绑定的参数,如果存在就加入model中,不存在则不处理
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
if (attrValue != null) {
implicitModel.addAttribute(attrName, attrValue);
}
}
//循环处理@modelAttribute绑定的方法
for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
//得到这个方法的所有的参数
Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
//得到@ModelAttribute绑定的数据的名字
String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value(); //如果之前已经得到了这个数据,就执行下一个@ModelAttribute方法,这意味着如果sessionAttribute中已经有了这个数据,就不会再接受这个方法返回的数据
if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
continue;
}
ReflectionUtils.makeAccessible(attributeMethodToInvoke);
Object attrValue = attributeMethodToInvoke.invoke(handler, args);
if ("".equals(attrName)) {
Class resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
}
//如果sessionAttribute中没有这个数据,则将它放进model中
if (!implicitModel.containsAttribute(attrName)) {
implicitModel.addAttribute(attrName, attrValue);
}
}
//得到处理器方法的所有的参数
Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
ReflectionUtils.makeAccessible(handlerMethodToInvoke);
return handlerMethodToInvoke.invoke(handler, args);
}
}
接下来看看怎么得到的modelAndView
ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
不论处理器方法返回什么值,最终都会组装成modelAndView对象。
另外在handle执行的前后都会先进行拦截器的处理
if (interceptors != null) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
// Apply postHandle methods of registered interceptors.
if (interceptors != null) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
在handle之前会执行inteceptor的preHandle方法,当执行完handle后会执行inteceptor的postHandle方法,最后在执行inteceptor的
afterCompletion方法
springmvc的配置:
<mvc:interceptors>
<!-- XXX会拦截所有的请求 -->
<bean class="cn.zcm.interceptor.XXX"></bean>
<mvc:interceptor>
<!-- AAA会拦截所有的"/user"的请求 -->
<mvc:mapping path="/user"/>
<bean class="cn.zcm.interceptor.AAA"></bean>
</mvc:interceptor>
</mvc:interceptors>
解析并渲染视图
render(mv, processedRequest, response);
首先解析modelAndView得到一个视图
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
这里与HandlerAdapter类似,也使用了策略模式,当我们要添加其他视图技术时,只需要在springmvc配置文件中配置就好了,然后就会出现在viewResolvers里面,然后视图解析器就能通过逻辑视图名解析出具体的视图。
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
最后就只差一步渲染视图了,将模型数据添加到视图中
view.render(mv.getModelInternal(), request, response);
主要是:有许多的具体的视图类
renderMergedOutputModel(mergedModel, request, response);
这些视图类会根据视图的不同利用不同的技术将模型数据进行相应的处理加工,形成自己需要的数据,然后将该视图以重定向或者转发的方式返回给用户,其实也是用的servlet的response.sendDirect和DispatcherServlet.forword();
到此大体流程就结束了。