Spring source code analysis 1: SpringMVC loading mechanism
1. spring-framework
included in 06177cd001242e
Before analyzing SpringMVC
, let's take a look at spring-framework
, and what are the functions of each.
spring-framework official warehouse
spring-jcl
: General log processing of thespring
spring-core
:spring
framework, including the operation processing and dynamic generation of Java bytecode, dependency injection mechanism (also called inversion of control), tool library,
Annotation operation, coding processing and data conversion, resource loading and processing, environment loading mechanism, etc.spring-beans
: Definition, loading, analysis, editing, etc. ofspring bean
spring-context
:spring
framework, including application caching, application events, application configuration, core annotation definition and processing, resource loading, asynchronous and timing tasks, data verification and formatting, etc.spring-aop
: Encapsulation and processing of aspect-oriented programmingspring-aspects
: -oriented programming using 16177cd001262d AspectJ as the underlying implementationspring-tx
: Encapsulation and processing of transactionsspring-jdbc
: Encapsulation and processing of database linksspring-context-indexer
: Support for annotation@Indexed
spring-context-support
: Optional support for some third-party libraries, such asehcache, javamail, quartz, freemarker
spring-oxm
: Packaging of O/X Mapperspring-messaging
:http, rsocket, simp
messaging protocols such as 06177cd001271cspring-jms
: Encapsulation of JMS (Java Message Service)spring-expression
: Spring Expression Language ( ) 16177cd0012787 Spring Expression Language Implementationspring-r2dbc
: package R2DBCspring-orm
: JPA and hibernatespring-web
: Provides the basic structure and technology of the Web framework, such as Http calling, filtering, processing, etc.spring-webmvc
: Implementation of Web MVC architecture, including Servlet container initialization, route mapping, view rendering, response processing, etc.spring-websocket
: Support for WebSocketspring-webflux
: Implementation of the Reactive Web framework, as opposedspring-webmvc
The core modules of the SpringMVC framework are mainly: spring-core
, spring-beans
, spring-context
, spring-web
, spring-webmvc
, which are mainly analyzed later.
1.1. spring-core
spring-core
that need to be briefly introduced here:
spring-core
has powerful Java bytecode operation processing functions and dynamic generation functions, which are the basis for aspect-oriented programming, data type conversion, SpEL expressions and other functionsspring-core
provides a dependency injection mechanism, which isspring bean
loading, and it is also the underlying mechanism by which we can use@Autowired
automatically load objects and other functionsspring-core
provides a mechanism for environment loading, so we can useapplication-dev.yml, application-test.yml, application-prod.yml, ...
To load different configurations according to the environmentspring-core
provides an extension mechanism similar to Java SPI, which can automatically instantiate classes specified by other packages.spring-boot, spring-cloud
all rely on this mechanism to automatically load resources.
Define the classes that need to be automatically loaded in theMETA-INF/spring.factories
Spring Factories
1.2. spring-beans
There are several points of Spring bean loading and extension mechanism that need to be briefly introduced here:
- There are two main types of Spring bean definitions: annotation-based definitions and XML file-based definitions
spring-beans
provides a third-party command space expansion mechanism for beans based on XML configuration, mainly in
The command space that needs to be expanded is defined in theMETA-INF/spring.handlers, META-INF/spring.schemas
Such as<dubbo:application name="name"/>, <dubbo:registry address="address"/>
- Annotation-based custom extensions need to rely on the extension loading mechanism of
spring-boot
1.3. spring-context
spring-context
is the core processing part of the application, including:
- App cache
- Application event
- Application configuration
- Core annotation definition and processing
- Resource loading
- Asynchronous and timed tasks
- Data verification and formatting
Etc., such framework core annotations @ComponentScan, @Profile, @Conditional, @Bean, @Async, @Controller, @Service, @Component, @Validated
1.4. spring-web
spring-web
is the core processing part of Http, mainly including:
- Core Http request and response processing (including cookies, caching, multimedia, etc.)
- Http request and response codec and conversion (including Json, XML, ProtoBuf, etc.)
- Reactive web framework basic processing
- Call the client (such as
RestTemplate
) - Servlet context
- Request filter
- Multipart file upload processing
Etc., such Web core annotations such @RequestMapping, @RequestParam, @PathVariable, @ResponseBody, @RestController
1.5. spring-webmvc
spring-webmvc
depends on spring-web
, the main functions include:
- Servlet container initialization
- Route map
- View rendering
- Response processing
Wait, if you don't use Spring MVC, but want to take advantage of other web-related technologies supported by Spring, you only need to rely on spring-web
, such as spring-webflux
.
1.6. spring-webflux
spring-webflux
corresponds to spring-webmvc
, webmvc
is a synchronous blocking framework, and webflux
is an asynchronous non-blocking framework,
It is a new responsive web framework introduced in Spring Framework 5.0.
Reference: Spring WebFlux 、
Spring WebFlux :: Spring Docs
2. A simple spring-webmvc
project configuration
Configure the following in the WEB-INF/web.xml
<?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/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>springMVC</display-name>
<!-- 部署 DispatcherServlet -->
<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>
<!-- 容器再启动时立即加载servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 处理所有URL -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 定义应用程序监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
There are two entry classes:
servlet-class
org.springframework.web.servlet.DispatcherServlet
: Specify the class used to process the corresponding URL requestlistener-class
org.springframework.web.context.ContextLoaderListener
: Set up an event listener, the event listener will be notified when the session or servlet environment is established, modified, and deleted
These two classes are defined in spring-webmvc
and spring-web
respectively, and we will analyze them one by one below.
3. DispatcherServlet
Let's take a look at the inheritance relationship of DispatcherServlet
- javax.servlet.Servlet
- javax.servlet.GenericServlet
- javax.servlet.http.HttpServlet
- HttpServletBean
- FrameworkServlet
- DispatcherServlet
3.1. javax.servlet.Servlet
First look at javax.servlet.Servlet
javax.servlet.Servlet
mainly defines 2 methods:
init
: Initialize Servlet, only execute onceservice
: In response to a request, this method will be called every time an http request
public interface Servlet {
// 初始化 Servlet,只执行一次
public void init(ServletConfig config) throws ServletException;
// 响应请求,每次http请求都会调用这个方法
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
// 销毁 Servlet
public void destroy();
}
3.2. javax.servlet.GenericServlet
Let's take a look at javax.servlet.GenericServlet
javax.servlet.GenericServlet
mainly overloads the init
method
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable {
public GenericServlet() {}
// 添加配置初始化
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
// 保留无参初始化
public void init() throws ServletException {}
// 留给子类实现
public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
}
3.3. javax.servlet.http.HttpServlet
Let's take a look at javax.servlet.http.HttpServlet
javax.servlet.http.HttpServlet
mainly overloads the service
method and extends 7 methods:
doGet
: Processing GET request, only input error information, not implementeddoHead
: Processing HEAD request, only input error information, not implementeddoPost
: Processing POST request, only input error information, not implementeddoPut
: Processing PUT request, only input error information, not implementeddoDelete
: Processing DELETE request, only input error information, not implementeddoOptions
: Handling OPTIONS requestsdoTrace
: Process TRACE requests
public abstract class HttpServlet extends GenericServlet {
private static final String METHOD_DELETE = "DELETE";
private static final String METHOD_HEAD = "HEAD";
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String METHOD_POST = "POST";
private static final String METHOD_PUT = "PUT";
private static final String METHOD_TRACE = "TRACE";
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
NoBodyResponse response = new NoBodyResponse(resp);
// 调用 doGet,但body设为空body
doGet(req, response);
response.setContentLength();
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// ... 代码省略
}
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// ... 代码省略
}
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// ... 代码省略
}
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// ... 代码省略
}
protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// ... 代码省略
}
// 实现了GET, HEAD, POST PUT, DELETE, OPTIONS, TRACE七个Http方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
// ... 代码省略
doGet(req, resp);
// ... 代码省略
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
// 把 Servlet 转化为 HttpServlet
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}
}
3.4. HttpServletBean
Let's take a look at HttpServletBean
HttpServletBean
mainly overloads the init
method and extends 2 methods:
initBeanWrapper
: Initialize the Java Bean defined by Servlet Config, implemented by subclasses, and not implemented by defaultinitServletBean
: Initialize Servlet bean, implemented by subclass
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
// 初始化
@Override
public final void init() throws ServletException {
// 把Servlet配置参数设置到bean属性中
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// 初始化Servlet bean
initServletBean();
}
// 初始化由Servlet Config定义的Java Bean,由子类实现,默认不实现
protected void initBeanWrapper(BeanWrapper bw) throws BeansException {
}
// 初始化Servlet bean,由子类实现
protected void initServletBean() throws ServletException {
}
}
3.5. FrameworkServlet
Let's take a look at FrameworkServlet
FrameworkServlet
is the core Servlet of the framework, which mainly overloads the initServletBean
method and extends 2 methods:
initFrameworkServlet
: Initialize the framework Servlet, implemented by subclasses, not implemented by defaultonRefresh
: refresh context data, implemented by subclasses
Overloaded the service, doGet, doPost, doPut, doDelete, doOptions, doTrace
method, and extended 1 method:
doService
: Processing response request
3.5.1. FrameworkServlet.initServletBean
After the parent class HttpServletBean
initialized, two hooks initBeanWrapper, initServletBean
left. initBeanWrapper
is not implemented by default.
So take a look at the implementation of the initServletBean
FrameworkServlet.initServletBean
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
@Override
protected final void initServletBean() throws ServletException {
// ... 代码省略
try {
// 初始化Web应用上下文
this.webApplicationContext = initWebApplicationContext();
// 初始化Web框架Servlet
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
// ... 代码省略
}
// 初始化框架Servlet,由子类实现,默认不实现
protected void initFrameworkServlet() throws ServletException {}
}
Let's take a look at FrameworkServlet.initWebApplicationContext
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
protected WebApplicationContext initWebApplicationContext() {
// 获取应用根上下文
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// 对webApplicationContext进行配置
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
// 未激活
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
// 配置并刷新应用上下文
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 如果没有,则在ServletContext中查找是否注册过
wac = findWebApplicationContext();
}
if (wac == null) {
// 如果任然没有,则以rootContext为父上下文创建一个新的上下文
// 并调用 configureAndRefreshWebApplicationContext(cwac) 配置并刷新新的上下文
// 默认使用 XmlWebApplicationContext(基于XML加载)作为应用上下文
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// 重载上下文数据
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
// 把上下文注册到ServletContext中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
// 以parent为父上下文创建一个新的上下文
// 并调用 configureAndRefreshWebApplicationContext(cwac) 配置并刷新新的上下文
// 默认使用 XmlWebApplicationContext(基于XML加载)作为应用上下文
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// 这里默认使用 XmlWebApplicationContext(基于XML加载)
Class<?> contextClass = getContextClass();
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
configureAndRefreshWebApplicationContext(wac);
return wac;
}
}
There are two methods that need to be analyzed in depth: configureAndRefreshWebApplicationContext
, onRefresh
Let's take a look at FrameworkServlet.configureAndRefreshWebApplicationContext
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
// ... 代码省略
// 设置ServletContext
wac.setServletContext(getServletContext());
// 设置ServletConfig
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
// 添加应用事件监听器,应用事件会触发当前对象的onApplicationEvent方法
// 进一步,会调用当前对象的onRefresh方法,刷新上下文数据
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// ... 代码省略
// 初始化一些需要初始加载的类,调用这些类的initialize方法
applyInitializers(wac);
// 应用上下文刷新
wac.refresh();
}
// 应用事件会触发此方法,然后调用当前对象的onRefresh方法,刷新上下文数据
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}
}
Let's take a look at FrameworkServlet.onRefresh
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
protected void onRefresh(ApplicationContext context) {
// 由子类来实现
}
}
3.5.2. FrameworkServlet.service
Let's take a look at FrameworkServlet.service
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
// 如果Http方法是Patch或没有,扩展处理
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 扩展处理
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 扩展处理
processRequest(request, response);
}
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 扩展处理
processRequest(request, response);
}
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 扩展处理
processRequest(request, response);
}
@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
// 扩展处理
processRequest(request, response);
if (response.containsHeader("Allow")) {
return;
}
}
// ... 代码省略
}
@Override
protected void doTrace(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (this.dispatchTraceRequest) {
// 扩展处理
processRequest(request, response);
if ("message/http".equals(response.getContentType())) {
return;
}
}
super.doTrace(request, response);
}
}
Let's take a look at the extended processing method FrameworkServlet.processRequest
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// ... 代码省略
// 记录请求属性与上下文环境,请求处理完后派发事件
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response);
}
// ... 代码省略
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
// 由子类来实现
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
}
3.6. DispatcherServlet
DispatcherServlet
mainly extends 2 methods: onRefresh
, doService
, so take a look at DispatcherServlet
How is it achieved
3.6.1. DispatcherServlet.onRefresh
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
// 初始化Multipart文件上传处理
initMultipartResolver(context);
// 初始化本地化处理
initLocaleResolver(context);
// 初始化主题处理
initThemeResolver(context);
// 初始化处理器映射
initHandlerMappings(context);
// 初始化处理器适配
initHandlerAdapters(context);
// 初始化处理器异常
initHandlerExceptionResolvers(context);
// 初始化视图查找处理
initRequestToViewNameTranslator(context);
// 初始化视图解析处理
initViewResolvers(context);
// 初始化内存暂存session数据管理器
initFlashMapManager(context);
}
private void initMultipartResolver(ApplicationContext context) {
try {
// 获取bean
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
// ... 代码省略
}
catch (NoSuchBeanDefinitionException ex) {
// ... 代码省略
}
}
private void initLocaleResolver(ApplicationContext context) {
try {
// 获取bean
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
// ... 代码省略
}
catch (NoSuchBeanDefinitionException ex) {
// ... 代码省略
}
}
private void initThemeResolver(ApplicationContext context) {
try {
// 获取bean
this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
// ... 代码省略
}
catch (NoSuchBeanDefinitionException ex) {
// ... 代码省略
}
}
private void initFlashMapManager(ApplicationContext context) {
try {
// 获取bean
this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
}
catch (NoSuchBeanDefinitionException ex) {
// 没有bean,则获取默认策略
this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
}
}
}
3.6.1.1. DispatcherServlet.initHandlerMappings
DispatcherServlet.initHandlerMappings
public class DispatcherServlet extends FrameworkServlet {
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// 默认是探测所有的HandlerMapping,包括父上下文
if (this.detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
// 否则直接获取bean
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {}
}
// 如果以上两种都没有定义,则获取默认的处理策略
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
// ... 代码省略
}
// 获取默认的处理策略
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
// 尝试从DispatcherServlet.properties文件中加载
if (defaultStrategies == null) {
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
// 创建bean
Object strategy = createDefaultStrategy(context, clazz);
// 装载到 strategies 中
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
// ... 代码省略
}
catch (LinkageError err) {
// ... 代码省略
}
}
return strategies;
}
else {
return Collections.emptyList();
}
}
// 创建bean
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
return context.getAutowireCapableBeanFactory().createBean(clazz);
}
}
DispatcherServlet.properties
The files (the developer cannot customize the coverage) are as follows:
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.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
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
DispatcherServlet.properties
file specifies:
- Put AcceptHeaderLocaleResolver
As the default localized parser - Put FixedThemeResolver
As the default theme parser - Put BeanNameUrlHandlerMapping
、RequestMappingHandlerMapping
、RouterFunctionMapping
As the default processor mapping component - Put HttpRequestHandlerAdapter
、SimpleControllerHandlerAdapter
、RequestMappingHandlerAdapter
、HandlerFunctionAdapter
As the default processor adapter component - Put ExceptionHandlerExceptionResolver
、ResponseStatusExceptionResolver
、DefaultHandlerExceptionResolver
As the default processor exception parser - Put DefaultRequestToViewNameTranslator
As the default view lookup processor - Put InternalResourceViewResolver
As the default view resolver - Put SessionFlashMapManager
As the default memory staging session data manager
3.6.1.2. DispatcherServlet.initHandlerAdapters
DispatcherServlet.initHandlerAdapters
public class DispatcherServlet extends FrameworkServlet {
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
// 默认是探测所有的HandlerAdapter,包括父上下文
if (this.detectAllHandlerAdapters) {
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
// 否则直接获取bean
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {}
}
// 如果以上两种都没有定义,则获取默认的处理策略
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
}
}
}
3.6.1.3. DispatcherServlet.initHandlerExceptionResolvers
DispatcherServlet.initHandlerExceptionResolvers
public class DispatcherServlet extends FrameworkServlet {
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
// 默认是探测所有的HandlerExceptionResolver,包括父上下文
if (this.detectAllHandlerExceptionResolvers) {
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
else {
// 否则直接获取bean
try {
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {}
}
// 如果以上两种都没有定义,则获取默认的处理策略
if (this.handlerExceptionResolvers == null) {
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
}
}
}
3.6.1.4. DispatcherServlet.initRequestToViewNameTranslator
DispatcherServlet.initRequestToViewNameTranslator
public class DispatcherServlet extends FrameworkServlet {
private void initRequestToViewNameTranslator(ApplicationContext context) {
try {
// 获取bean
this.viewNameTranslator =
context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
}
catch (NoSuchBeanDefinitionException ex) {
// 如果没有定义bean,则获取默认的处理策略
this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
}
}
}
3.6.1.5. DispatcherServlet.initViewResolvers
DispatcherServlet.initViewResolvers
public class DispatcherServlet extends FrameworkServlet {
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;
// 默认是探测所有的ViewResolver,包括父上下文
if (this.detectAllViewResolvers) {
Map<String, ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.viewResolvers = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
}
else {
// 否则直接获取bean
try {
ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
this.viewResolvers = Collections.singletonList(vr);
}
catch (NoSuchBeanDefinitionException ex) {}
}
// 如果以上两种都没有定义,则获取默认的处理策略
if (this.viewResolvers == null) {
this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
}
}
}
3.6.2. DispatcherServlet.doService
Just finished parsing DispatcherServlet.onRefresh
, now let’s take a look at DispatcherServlet.doService
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ... 代码省略
// 给请求对象添加一些上下文数据
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
// ... 代码省略
try {
doDispatch(request, response);
}
finally {
// ... 代码省略
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
// 处理器链
HandlerExecutionChain mappedHandler = null;
// 是Multipart文件上传
boolean multipartRequestParsed = false;
// 异步处理管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 检测Multipart文件上传
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 获取处理器,从handlerMappings中查找符合请求的处理器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 未找到处理器,404
noHandlerFound(processedRequest, response);
return;
}
// 获取处理器适配器,从handlerAdapters中查找符合处理器的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
// 如果是GET或HEAD请求,检查Last-Modified
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 前置处理,调用处理器的preHandle方法,如果有一个不成功,返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 调用处理器
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// ... 代码省略
// 如果没有视图名字,添加默认的视图名
applyDefaultViewName(processedRequest, mv);
// 后置处理,调用处理器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
// ... 代码省略
}
catch (Throwable err) {
// ... 代码省略
}
// 处理handler返回的结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// ... 代码省略
}
catch (Throwable err) {
// ... 代码省略
}
finally {
// ... 代码省略
}
}
// 处理handler返回的结果
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
// ... 代码省略,如果有异常,调用handlerExceptionResolvers处理
}
if (mv != null && !mv.wasCleared()) {
// 渲染视图
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
// ... 代码省略
}
// 渲染视图
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// ... 代码省略
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 调用viewResolvers来解析视图
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
// ... 代码省略
}
else {
// ... 代码省略
}
// ... 代码省略
try {
if (mv.getStatus() != null) {
// 设置http状态码
response.setStatus(mv.getStatus().value());
}
// 真实渲染
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
// ... 代码省略
}
}
}
3.6.3. Several points that need to be resolved later
DispatcherServlet
the class 06177cd002a94c is basically the same, but there are still a few points that are not resolved:
- FrameworkServlet(L702)
:ConfigurableWebApplicationContext.refresh
- DispatcherServlet(L514)
:ApplicationContext.getBean
- DispatcherServlet.properties
Policy processing defined in the file - DispatcherServlet(L1393)
:View.render
We will analyze these points later.
4. ContextLoaderListener
Let's take a look at the inheritance relationship of ContextLoaderListener
- ContextLoader
- ContextLoaderListener
ContextLoaderListener
Relatively simple, there are only two methods to monitor events
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
// ContextLoader.initWebApplicationContext
initWebApplicationContext(event.getServletContext());
}
@Override
public void contextDestroyed(ServletContextEvent event) {
// ContextLoader.closeWebApplicationContext
closeWebApplicationContext(event.getServletContext());
// 销毁上下文中以"org.springframework."开头的可销毁bean
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
ContextLoader
Static initialization
public class ContextLoader {
static {
try {
// 从ContextLoader.properties文件中加载默认的策略
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
}
ContextLoader.properties
The content of the file is as follows:
# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
ContextLoader.properties
file specifies the use of XmlWebApplicationContext
as the default Web application context environment
Let's take a look at ContextLoader initWebApplicationContext
and closeWebApplicationContext
4.1. ContextLoaderListener.initWebApplicationContext
ContextLoaderListener.initWebApplicationContext
public class ContextLoader {
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// ... 代码省略
try {
// 如果没有上下文对象,则创建一个新的上下文
// 并调用 configureAndRefreshWebApplicationContext(cwac) 配置并刷新新的上下文
// 默认使用 XmlWebApplicationContext(基于XML加载)作为应用上下文
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
// 未激活
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// 配置并刷新应用上下文
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// 把上下文注册到ServletContext中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
// ... 代码省略
return this.context;
}
catch (RuntimeException | Error ex) {
// ... 代码省略
}
}
}
ContextLoader.configureAndRefreshWebApplicationContext
and FrameworkServlet.configureAndRefreshWebApplicationContext
The treatment is basically the same.
In other words, when the container starts (such as Tomcat, Jetty, Undertow, etc.), the Spring framework will automatically initialize.
4.2. ContextLoaderListener.closeWebApplicationContext
ContextLoaderListener.closeWebApplicationContext
public class ContextLoader {
public void closeWebApplicationContext(ServletContext servletContext) {
try {
if (this.context instanceof ConfigurableWebApplicationContext) {
// 调用上下文对象的close方法
((ConfigurableWebApplicationContext) this.context).close();
}
}
finally {
// ... 代码省略
}
}
}
5. Summary
DispatcherServlet.init
and ContextLoaderListener.contextInitialized
will initialize the application context. The main process is:
- Initialize the Web application context, use
XmlWebApplicationContext
(based on XML loading) as the application context byrefresh
method - Instantiate the classes defined
globalInitializerClasses
andcontextInitializerClasses
- Instantiate the necessary components of WebMVC:
MultipartResolver
,LocaleResolver
,ThemeResolver
,HandlerMapping
,HandlerAdapter
,HandlerExceptionResolver
,RequestToViewNameTranslator
,ViewResolver
,FlashMapManager
Each request will enter DispatcherServlet.service
, the main process is:
- Initialize the request object so that subsequent processing can be applied
- Process Multipart file upload, get the processor to process the current request
- If an exception occurs in the current request processing, perform exception handling
- Perform view rendering
6. Unfinished
So far, the analysis only ends with the DispatcherServlet
ContextLoaderListener
. The next article will delve into other classes and continue to explore.
ConfigurableWebApplicationContext.refresh
refresh contextApplicationContext.getBean
Get the bean from the context- The policy processing defined in the
DispatcherServlet.properties
- The policy processing defined in the
ContextLoader.properties
View.render
view rendering
Follow-up
For more blogs, check out https://github.com/senntyou/blogs
Author: Shen Yuzhi (@senntyou)
Copyright statement: Freely reprinted-non-commercial-non-derivative-keep the signature ( Creative Commons 3.0 License )
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。