spring-mvc in Action
Agenda
- Context Initialization
- Request Routing
- Data Binding and Validation
- Exception Handling
- View Rendering
Overview
DispatcherServlet
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
- Create its own internal web application context
-
Manages WebApplicationContext instance per servlet
-
Highly customizable supporting installation of different adapter classes
-
Central dispatcher for HTTP request handlers/controllers
-
Publishes events on request processing
Initialization of Root WebContext
package javax.servlet;
import java.util.EventListener;
public interface ServletContextListener extends EventListener {
public void contextInitialized(ServletContextEvent sce);
public void contextDestroyed(ServletContextEvent sce);
}
Notified of context initialization before any filters or servlets in the web application are initialized
- contextInitialized() in the order of declaration
- contextDestroyed() in reverse
Initialization of Root WebContext (Cont...)
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
WEB-INF/web.xml
Initialization of Root WebContext (Cont...)
org.springframework.web.context.ContextLoaderListener
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
this.contextLoader.initWebApplicationContext(event.getServletContext());
//org.springframework.web.context.ContextLoader#initWebApplicationContext
}
-
contextClass
-
contextConfigLocation
Configuration:
Context Loader Performs the actual initialization work for the root application context
Initialization of WebContext (Cont..)
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.XmlWebApplicationContext</param-value>
</context-param>
i. xml based configuration
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
@EnableWebMvc
ii. annotation based configuration
contextClass configuration
Initialization of WebContext (Cont..)
contextConfigLocation configuration
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext1.xml, WEB-INF/applicationContext2.xml</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/*Context.xml,WEB-INF/spring*.xml</param-value>
</context-param>
i.
ii.
iii.
iv.
/** Default config location for the root context */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
Loading beans From configLocation
Loading the xml document
- org.springframework.beans.factory.xml.DefaultDocumentLoader
Default: javax.xml.parsers.DocumentBuilder
java -Djavax.xml.parsers.DocumentBuilderFactory=oracle.xml.jaxp.JXDocumentBuilderFactory
BeanDefinitionRegistry
Interface for registries that hold bean definitions
org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
private boolean allowBeanDefinitionOverriding = true;
HandlerMapping
HandlerInterceptor
HandlerAdapter
Responsibility
- Mapping between requests and handler objects
public interface Ordered {
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
int getOrder();
}
HandlerMapping
//DispatcherServlet.properties
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
BeanNameUrlHandlerMapping
beans with names that start with a slash ("/")
<bean id="/oldHome" class="net.therap.controller.OldProjectController"></bean>
<bean id="/oldHome.html" class="net.therap.controller.OldProjectController"></bean>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/project.htm">oldProjectController</prop>
<prop key="/oldProject*">oldProjectController</prop>
<prop key="/pilotProject">oldProjectController</prop>
</props>
</property>
</bean>
<bean name="oldProjectController" class="net.therap.controller.OldProjectController"/>
SimpleUrlHandlerMapping
HandlerMapping
Request Resolving
org.springframework.web.servlet.DispatcherServlet#getHandler(javax.servlet.http.HttpServletRequest)
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
HandlerMapping
For annotated request mapping
1. DefaultAnnotationHandlerMapping (@deprecated in spring mvc 3.2) in favor of
2. RequestMappingHandlerMapping (From spring mvc 3.1)
What's new
- HandlerMethodReturnValueHandler customization
- preHandle()
after: HandlerMapping determined handler object
before: HandlerAdapter invokes the handler
- postHandle()
after: HandlerAdapter actually invoked the handler
before: DispatcherServlet renders the view
- afterCompletion()
after: rendering the view
invoked in reverse order, so the first interceptor will be the last to be invoked.
HandlerInterceptor
Registering HandlerInterceptor
<mvc:interceptors>
<bean id="webContentInterceptor" class="org.springframework.web.servlet.mvc.WebContentInterceptor">
<property name="cacheSeconds" value="0"/>
<property name="useExpiresHeader" value="true"/>
<property name="useCacheControlHeader" value="true"/>
<property name="useCacheControlNoStore" value="true"/>
</bean>
</mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/resources/**"/>
<bean class="net.therap.interceptor.ProfilerInterceptor"/>
</mvc:interceptor>
ProfilerInterceptor extends HandlerInterceptorAdapter
Custom HandlerInterceptor
Request Resolving
public interface HandlerMapping {
...
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
public class HandlerExecutionChain {
private final Object handler;
private HandlerInterceptor[] interceptors;
private List<HandlerInterceptor> interceptorList;
}
org.springframework.web.servlet.DispatcherServlet#doDispatch
#1 Determining HanlderMapping for current request
- Check Ambiguous handler methods mapped for HTTP path
- Return a HandlerExecutionChain adding all path mathed interceptors
Request Resolving
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
#2 Determining HanlderAdapter
Request Resolving
1. mappedHandler.applyPreHandle(processedRequest, response) // HandlerExecutionChain
Invoke preHandle() of all registered interceptors
3. Determines viewName, if not provided - viewNameTranslator.getViewName(request)
2. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
4. Invoke postHandle() of all registered interceptors in reverse order
5. Render view
1. Map<Class<?>, ServletHandlerMethodResolver> methodResolverCache
2. AnnotationUtils.findAnnotation(method, RequestMapping.class)
First Step
Second Step
#2 Determining HanlderAdapter
Thank you
Spring In Action
By MUHAMMAD SHAKHAWAT HOSSAIN SAFAT
Spring In Action
- 885