Spring版本是5.1.x
代码示例
web.xml
web.xml是用来初始化配置信息的,即为Context配置监听器,过滤器以及Servlet。
<?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"
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">
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<servlet>
<!--名称 -->
<servlet-name>springmvc</servlet-name>
<!-- Servlet类 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--SpringMVC配置参数文件的位置 -->
<param-name>contextConfigLocation</param-name>
<!--默认名称为ServletName-servlet.xml -->
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
applicationContext.xml
applicationContext.xml是作为spring的配置文件,用于创建spring容器,配置了采用注解方式以及扫描的路径,这里主要是扫描Service和Dao
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:annotation-config />
<context:component-scan base-package="com.huangsz.springmvcdemo">
</context:component-scan>
</beans>
spring-mvc.xml
applicationContext.xml是作为springmvc的配置文件,用于创建springmvc子容器,配置了采用注解方式,扫描的路径,这里主要是扫描Controller,还有视图解析器。
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!-- scan the package and the sub package -->
<context:component-scan base-package="com.huangsz.springmvcdemo"/>
<!-- don't handle the static resource -->
<mvc:default-servlet-handler />
<!-- if you use annotation you must configure following setting -->
<mvc:annotation-driven />
<!-- configure the InternalResourceViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
TestController
package com.huangsz.springmvcdemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
/**
* @author: hsz
* @date: 2021/3/18 18:12
* @description:
*/
@Controller
@RequestMapping("/test")
public class TestController {
@Autowired
private TestService testService;
@RequestMapping("/print")
public String print(@RequestParam("p1") String p1, @RequestParam("p2") int p2) {
testService.test();
System.out.println("p1=" + p1 + ", p2=" + p2);
return "test";
}
}
TestService
package com.huangsz.springmvcdemo;
import org.springframework.stereotype.Service;
/**
* @author: hsz
* @date: 2022/3/7 14:13
* @description:
*/
@Service
public class TestService {
public void test() {
System.out.println("Hello World");
}
}
test.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
Hello World
</body>
</html>
启动Tomcat,访问http://localhost:8080/test/print?p1=1&p2=2
源码解析
创建Spring容器并刷新
在解析Tomcat源码一章中,启动StandardContext时会去加载web.xml并解析,然后会实例化应用配置的监听器,即这里web.xml中配置的ContextLoaderListener。
下面是Tomcat启动StandardContext的代码:
protected synchronized void startInternal() throws LifecycleException {
// 省略。。。
try {
if (ok) {
// 当前状态为STARTING_PREP,发布CONFIGURE_START_EVENT事件,去解析web.xml,完善Context,构建Wrapper组件
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
// 启动context下的wrapper组件,其实这里已经启动过了,在添加到context时会启动
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}
}
// 实例化应用配置的监听器
if (ok) {
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
try {
// 启动session管理器
Manager manager = getManager();
if (manager instanceof Lifecycle) {
((Lifecycle) manager).start();
}
} catch(Exception e) {
log.error(sm.getString("standardContext.managerFail"), e);
ok = false;
}
// 实例化应用配置的过滤器
if (ok) {
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
} finally {
// Unbinding thread
unbindThread(oldCCL);
}
}
这里面会调用listenerStart方法来初始化监听器
public boolean listenerStart() {
// 省略
// 获取全部的应用监听器
Object instances[] = getApplicationLifecycleListeners();
if (instances == null || instances.length == 0) {
return ok;
}
ServletContextEvent event = new ServletContextEvent(getServletContext());
ServletContextEvent tldEvent = null;
if (noPluggabilityListeners.size() > 0) {
noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
tldEvent = new ServletContextEvent(noPluggabilityServletContext);
}
for (int i = 0; i < instances.length; i++) {
// 如果不是ServletContextListener类型的监听器则跳过
if (!(instances[i] instanceof ServletContextListener))
continue;
ServletContextListener listener =
(ServletContextListener) instances[i];
try {
fireContainerEvent("beforeContextInitialized", listener);
if (noPluggabilityListeners.contains(listener)) {
listener.contextInitialized(tldEvent);
} else {
listener.contextInitialized(event);
}
fireContainerEvent("afterContextInitialized", listener);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
fireContainerEvent("afterContextInitialized", listener);
getLogger().error
(sm.getString("standardContext.listenerStart",
instances[i].getClass().getName()), t);
ok = false;
}
}
return ok;
}
ContextLoaderListener刚好实现了ServletContextListener接口
ContextLoaderListener的作用就是启动Web容器时,读取在contextConfigLocation中定义的xml文件(一般存放spring的相关配置),自动装配ApplicationContext的配置信息,并产生WebApplicationContext对象,然后将这个对象放置在ServletContext的属性里,这样我们只要得到Servlet就可以得到WebApplicationContext对象,并利用这个对象访问spring容器管理的bean。
摘自:https://blog.csdn.net/shang_0122/article/details/119334625
所以会去调ContextLoaderListener的contextInitialized方法
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
在这里会去初始化Spring容器(父容器)并刷新,该操作由它的父类ContextLoader完成
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
servletContext.log("Initializing Spring root WebApplicationContext");
Log logger = LogFactory.getLog(ContextLoader.class);
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// 如果是采用注解方式的,这里的context就不为空,
// 用xml方式的,这里context为空,需要创建
if (this.context == null) {
// 创建spring容器
// 根据“ contextClass ”和“ contextConfigLocation ”上下文参数创建
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
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 ->
// determine parent for root web application context, if any.
// 设置父容器,spring容器一般没有父容器
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// 刷新spring容器
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException | Error ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
}
创建spring容器
首先会创建spring容器
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// 得到容器的类型
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
protected Class<?> determineContextClass(ServletContext servletContext) {
// 获取web.xml中配置的contextClass参数
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
// 根据类名获取Class类型
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
// 如果没有配置,则使用默认的类型
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); // 默认是WebApplicationContext
try {
// 根据类名获取Class类型
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
会先判断web.xml中是否配置了contextClass参数,如果配了容器类型,则实例化该类。如果没有配置,则使用默认的类型,从defaultStrategies中获取WebApplicationContext对应的属性值。defaultStrategies中的属性是从ContextLoader类所在的类路径中的ContextLoader.properties文件加载,而该文件中配置了一个默认的WebApplicationContext。
private static final Properties defaultStrategies;
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
// 从ContextLoader类所在的类路径中的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());
}
}
所以默认spring容器是 XmlWebApplicationContext。
然后通过(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass)
实例化一个容器出来
public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
Assert.notNull(clazz, "Class must not be null");
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
// 通过构造方法实例化
return instantiateClass(clazz.getDeclaredConstructor());
}
catch (NoSuchMethodException ex) {
Constructor<T> ctor = findPrimaryConstructor(clazz);
if (ctor != null) {
return instantiateClass(ctor);
}
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
catch (LinkageError err) {
throw new BeanInstantiationException(clazz, "Unresolvable class definition", err);
}
}
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
从下面这个截图也可以看出创建出来的容器是叫根容器
刷新spring容器
然后就是配置spring容器并刷新了
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
// 为容器设置 ServletContext
wac.setServletContext(sc);
// 获取web.xml中设置的contextConfigLocation参数,即spring的配置文件,
// 后面需要根据该配置文件中的配置刷新spring容器
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
// 在刷新之前初始化容器,可在刷新之前先对容器进行处理
customizeContext(sc, wac);
// 最终刷新容器
wac.refresh();
}
这里在最终刷新spring容器之前,会先初始化Spring ConfigurableApplicationContext的回调接口,调用该类的 initialize 方法。并将 ConfigurableApplicationContext 类的实例传递给该方法。
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
// 获得ApplicationContextInitializer列表
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(sc);
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
throw new ApplicationContextException(String.format(
"Could not apply context initializer [%s] since its generic parameter [%s] " +
"is not assignable from the type of application context used by this " +
"context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
wac.getClass().getName()));
}
this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
}
// 可以排序
AnnotationAwareOrderComparator.sort(this.contextInitializers);
// 调用每个ApplicationContextInitializer实现类的initialize,
// 并将ConfigurableApplicationContext 作为参数传入
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
initializer.initialize(wac);
}
}
可以在web.xml中配置contextInitializerClasses参数来配置自定义的ApplicationContextInitializer
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.huangsz.springmvcdemo.CustomApplicationContextInitializer</param-value>
</context-param>
该接口典型的应用场景是web应用中需要编程方式对应用上下文做初始化。比如,注册属性源(property sources)或者针对上下文的环境信息environment激活相应的profile。一般是留给外部扩展的,springboot就有几个类实现了该接口。
最后刷新spring容器,调用了AbstractApplicationContext类的refresh方法,refresh方法的解析可以看Spring IOC 源码解析之refresh(二)。
下面是spring容器刷新的调用栈,可以看到就是从tomcat启动StandardContext为入口,在初始化监听器时创建并刷新spring容器的。
创建SpringMVC容器并刷新
创建springmvc容器
接下来就该创建springmvc容器了,那这个容器是在哪里创建的呢?
其实也可以通过跟上面一样的调用栈来看
可以看到也是在启动StandardContext里,在调用loadOnstartup方法里。
还记得在web.xml中我们为DispatcherServlet配置了一个load-on-startup
<servlet>
<!--名称 -->
<servlet-name>springmvc</servlet-name>
<!-- Servlet类 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--SpringMVC配置参数文件的位置 -->
<param-name>contextConfigLocation</param-name>
<!--默认名称为ServletName-servlet.xml -->
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>
当值为0或者大于0时,表示在应用启动时就加载这个servlet。
我们通过Tomcat的StandardContext里的代码来看下
if (ok) {
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
// Check constraints for uncovered HTTP methods
// Needs to be after SCIs and listeners as they may programmatically
// change constraints
if (ok) {
checkConstraintsForUncoveredMethods(findConstraints());
}
try {
// Start manager
Manager manager = getManager();
if (manager instanceof Lifecycle) {
((Lifecycle) manager).start();
}
} catch(Exception e) {
log.error(sm.getString("standardContext.managerFail"), e);
ok = false;
}
// Configure and call application filters
if (ok) {
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
// Load and initialize all "load on startup" servlets
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
可以看到,在listenerStart下面的不远处,就有一处loadOnStartup,看注释也清楚这里是加载并初始化所有配置了“load on startup”的servlet。
public boolean loadOnStartup(Container children[]) {
// 使用TreeMap来存放需要在启动时就加载的servlet,根据loadOnStartup排序
TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
for (int i = 0; i < children.length; i++) {
Wrapper wrapper = (Wrapper) children[i];
int loadOnStartup = wrapper.getLoadOnStartup();
// 这里如果loadOnStartup >= 0,就会将该servlet存放到map中
if (loadOnStartup < 0)
continue;
Integer key = Integer.valueOf(loadOnStartup);
ArrayList<Wrapper> list = map.get(key);
if (list == null) {
list = new ArrayList<>();
map.put(key, list);
}
list.add(wrapper);
}
// Load the collected "load on startup" servlets
for (ArrayList<Wrapper> list : map.values()) {
for (Wrapper wrapper : list) {
try {
// 加载servlet
wrapper.load();
} catch (ServletException e) {
getLogger().error(sm.getString("standardContext.loadOnStartup.loadException",
getName(), wrapper.getName()), StandardWrapper.getRootCause(e));
// NOTE: load errors (including a servlet that throws
// UnavailableException from the init() method) are NOT
// fatal to application startup
// unless failCtxIfServletStartFails="true" is specified
if(getComputedFailCtxIfServletStartFails()) {
return false;
}
}
}
}
return true;
}
加载servlet,在这里就是DispatcherServlet
public synchronized void load() throws ServletException {
instance = loadServlet();
// 。。。
}
public synchronized Servlet loadServlet() throws ServletException {
// 如果已经有一个该servlet实例,则直接返回该实例
if (!singleThreadModel && (instance != null))
return instance;
PrintStream out = System.out;
if (swallowOutput) {
SystemLogHandler.startCapture();
}
Servlet servlet;
try {
long t1=System.currentTimeMillis();
// Complain if no servlet class has been specified
if (servletClass == null) {
unavailable(null);
throw new ServletException
(sm.getString("standardWrapper.notClass", getName()));
}
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
// 实例化该servlet
servlet = (Servlet) instanceManager.newInstance(servletClass);
} catch (ClassCastException e) {
unavailable(null);
// Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.notServlet", servletClass), e);
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
unavailable(null);
// Added extra log statement for Bugzilla 36630:
// https://bz.apache.org/bugzilla/show_bug.cgi?id=36630
if(log.isDebugEnabled()) {
log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);
}
// Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.instantiate", servletClass), e);
}
// 。。。
// 初始化servlet
initServlet(servlet);
fireContainerEvent("load", this);
loadTime=System.currentTimeMillis() -t1;
} finally {
if (swallowOutput) {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
if (getServletContext() != null) {
getServletContext().log(log);
} else {
out.println(log);
}
}
}
}
return servlet;
}
初始化DispatcherServlet
private synchronized void initServlet(Servlet servlet)
throws ServletException {
if (instanceInitialized && !singleThreadModel) return;
// Call the initialization method of this servlet
try {
if( Globals.IS_SECURITY_ENABLED) {
boolean success = false;
try {
Object[] args = new Object[] { facade };
SecurityUtil.doAsPrivilege("init",
servlet,
classType,
args);
success = true;
} finally {
if (!success) {
// destroy() will not be called, thus clear the reference now
SecurityUtil.remove(servlet);
}
}
} else {
// 初始化
servlet.init(facade);
}
instanceInitialized = true;
} catch (UnavailableException f) {
unavailable(f);
throw f;
} catch (ServletException f) {
// If the servlet wanted to be unavailable it would have
// said so, so do not call unavailable(null).
throw f;
} catch (Throwable f) {
ExceptionUtils.handleThrowable(f);
getServletContext().log(sm.getString("standardWrapper.initException", getName()), f);
// If the servlet wanted to be unavailable it would have
// said so, so do not call unavailable(null).
throw new ServletException
(sm.getString("standardWrapper.initException", getName()), f);
}
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public final void init() throws ServletException {
// 获取在web.xml配置的初始化参数<init-param>,并将其设置到DispatcherServlet中
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;
}
}
// 初始化
initServletBean();
}
这里主要就是设置在web.xml中配置的contextConfigLocation属性,此属性指定SpringMVC的配置文件地址。然后调用FrameworkServlet的initServletBean方法初始化,在这里面会进行springmvc容器的创建和刷新。
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
// 创建springmvc容器并刷新
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
// 初始化并发布此 servlet 的 WebApplicationContext。委托createWebApplicationContext实际创建上下文
protected WebApplicationContext initWebApplicationContext() {
// 获取根容器,即spring容器
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) {
// 在ServletContext中寻找是否有Spring MVC容器,初次运行是没有的,
// Spring MVC初始化完毕ServletContext就有了Spring MVC容器
wac = findWebApplicationContext();
}
if (wac == null) {
// 没有则创建一个,并将根容器传进去,这里就是spring容器作为根容器
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.
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// 获取容器类型,默认也是XmlWebApplicationContext
Class<?> contextClass = getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
// 实例化springmvc容器
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
// 将spring容器设置为父容器
wac.setParent(parent);
// 获取web.xml中配置的springmvc配置文件地址
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
// 配置并刷新容器
configureAndRefreshWebApplicationContext(wac);
return wac;
}
刷新springmvc容器
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
// 这里是留给子类重写,也是可以对容器进行设置修改
postProcessWebApplicationContext(wac);
applyInitializers(wac);
// 刷新容器
wac.refresh();
}
最后刷新springmvc容器,也是调用了AbstractApplicationContext类的refresh方法。
总结下就是下面这张图
处理@RequestMapping注解,建立url和方法的关系
入口在RequestMappingHandlerMapping类中,这是一个处理器映射器,在实例化后进行初始化时调用afterPropertiesSet方法,来完成url和controller的映射。
@Override
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
super.afterPropertiesSet();
}
前面代码不管,直接看调用父类AbstractHandlerMethodMapping的afterPropertiesSet方法
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
// 获取容器中所有的bean
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// 处理bean
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
需要先获取到所有的bean
protected String[] getCandidateBeanNames() {
// detectHandlerMethodsInAncestorContexts 表示是否从子容器以及root容器中获取,
// 这里默认是false,表示只从子容器获取,因为通过前面对容器的解析,controller相关的bean就是在子容器中
return (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
}
只要bean名称前缀不是“scopedTarget.”就进一步处理
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
// 获取bean的类型
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// 判断是不是处理器,通过判断类上是否有@Controller或@RequestMapping注解
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
// 确保是被代理的类,即原始的类,而不是代理类
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 解析controller类,将Method与RequestMappingInfo对应
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
// 获得可调用的方法实例,即被Aop代理包装后的方法实例,因为最终调的是代理类,如果有的话
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 进行注册
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
先解析controller类,建立Method与RequestMappingInfo的对应关系。
RequestMappingInfo是解析@RequestMapping得到的一个类,可以认为就是@RequestMapping,包含请求路径,请求方法,请求头等信息。
遍历每个方法,根据@RequestMapping注解创建对应的RequestMappingInfo
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 根据方法上的@RequestMapping创建RequestMappingInfo
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// 根据类上的@RequestMapping创建RequestMappingInfo
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
// 将类上的映射信息和方法上的映射信息结合起来
info = typeInfo.combine(info);
}
// 如果有前缀,再加上前缀
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).build().combine(info);
}
}
return info;
}
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// 获取注解
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);// 获取方法上的@RequestMapping注解
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
// 有这个注解就创建RequestMappingInfo,没有则返回null
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
建立完所有的Method和RequestMappingInfo的对应关系后,然后遍历这个map开始注册
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
委托给内部类MappingRegistry处理
// mapping就是RequestMappingInfo,handler就是Controller实例名称,method方法实例
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
// 先创建HandlerMethod实例,保存controller中方法的信息,比如,所属类,方法实例,参数列表等
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// 验证是否已经注册过相同的映射,因为映射应该是唯一的
assertUniqueMethodMapping(handlerMethod, mapping);
// 建立 RequestMappingInfo 与 HandlerMethod 的映射
this.mappingLookup.put(mapping, handlerMethod);
// 建立 url 与 RequestMappingInfo 的映射,因为一个接口可能有多个路径,
// 所以这里用list,可能会映射多次
// 比如这样的:@RequestMapping(value = {"/print1", "/print2"})
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
// 这里跟跨域相关的,可以不管
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// 将所有信息封装为MappingRegistration然后与RequestMappingInfo关联,后续就可以通过url获取到相关的所有映射信息
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
这里会将方法信息封装为HandlerMethod,包含所属类,方法实例,参数列表等,然后建立RequestMappingInfo 与 HandlerMethod 的映射。
还会建立 url 与 RequestMappingInfo 的映射
最后将RequestMappingInfo与MappingRegistration关联,MappingRegistration中封装了RequestMappingInfo、HandlerMethod、url以及name。
这样就完成了url到controller中方法的映射了。一个请求过来,就可以根据请求url从urlLookup中找出对应的RequestMappingInfo ,再根据RequestMappingInfo 从mappingLookup中找出对应的HandlerMethod,根据HandlerMethod中的方法实例反射调用即可。
初始化DispacherServlet
在真正启动完成之前,还需要为DispacherServlet实例初始化
可以看到目前DispacherServlet里的这些组件还都是null的,需要为这些组件赋值后该DispacherServlet才可以使用。那在什么时候初始化的呢?
DispacherServlet中有一个onRefresh方法,调用了initStrategies方法,看方法名就知道是初始化方法
通过在initStrategies的调用处打一个断点,debug下
可以看出在springmvc容器完成刷新后,会发布完成刷新的事件,监听器监听到该事件后,会调用到DispacherServlet的onRefresh方法,再调用initStrategies方法。
protected void initStrategies(ApplicationContext context) {
// 初始化文件上传处理器
initMultipartResolver(context);
// 初始化国际化配置
initLocaleResolver(context);
// 初始化主题处理器
initThemeResolver(context);
// 初始化处理器映射器,用于处理url到controller的映射
initHandlerMappings(context);
// 初始化处理器适配器,用来调用处理器方法
initHandlerAdapters(context);
// 初始化异常处理器
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
// 初始化视图解析器
initViewResolvers(context);
initFlashMapManager(context);
}
这里主要看下initHandlerMappings,因为这几个方法逻辑都差不多
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// 是否要从容器中获取所有的HandlerMapping实现类,默认是true
// web.xml:
// <init-pararn>
// <pararn-narne>detectAllHandlerMappings</pararn-narne>
// <pararn-value>false</pararn-value>
// </init-pararn>
if (this.detectAllHandlerMappings) {
// 查找 ApplicationContext 中的所有 HandlerMapping,包括祖先上下文
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
// 赋值给handlerMappings
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// 按优先级排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
// 只从容器中获取beanName为handlerMapping的HandlerMapping
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
// 如果没有名为handlerMapping的HandlerMapping,则使用一个默认的,
// 使用“DispatcherServlet.properties”文件(与 DispatcherServlet 类位于同一包中)来确定类名
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
DispacherServlet处理请求
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 检查是否有文件上传
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 根据当前请求获得执行链
mappedHandler = getHandler(processedRequest);
// 没找到处理器的处理
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 根据HandlerMethod确定当前请求的处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
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;
}
// 调用controller,处理请求,返回视图
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 执行拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 处理结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
首先是文件上传的处理,这个不是我们的重点,所以跳过,然后根据当前请求获得执行链。
根据当前请求获得执行链
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 根据request获取HandlerExecutionChain,一般是由RequestMappingHandlerMapping来获取
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
这里需要通过处理器映射器来生成一条执行链,handlerMappings默认有3个
由于我们是使用注解@RequestMapping,所以这里使用的是RequestMappingHandlerMapping
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 由具体子类完成,获取到HandlerMethod
Object handler = getHandlerInternal(request);
// 如果没有匹配到的处理器,则用默认的处理器处理
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 获取到执行链,即封装了处理器和拦截器
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (CorsUtils.isCorsRequest(request)) {// 跨域处理
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
/**
* 查找给定请求的处理器方法
*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 获取request中的url
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try {
// 根据url查询HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// 如果handlerMethod中的bean只是名称而不是controller实例,则要根据名称获取到实例
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 从urlLookup根据url找到匹配的RequestMappingInfo,
// 因为可能一个url对应多个RequestMappingInfo,所以这里用list
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// 如果有匹配的,则检查其它属性是否符合要求,比如请求方法,请求参数,请求头等
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// 没有匹配的则只能所有的RequestMappingInfo都拿去重新匹配下
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
// 匹配到了,则选一个最佳匹配
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
// 返回最佳匹配的handlerMethod
return bestMatch.handlerMethod;
}
// 还没有匹配的,那就说明真没有一个方法可以处理该请求
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
// 获取匹配的RequestMappingInfo
// 检查给定的 RequestMappingInfo 是否与当前请求匹配,比如请求类型,请求参数等是否匹配,
// 并返回一个(可能是新的)实例
// match不为空时,就说明当前请求与给定的 RequestMappingInfo匹配
T match = getMatchingMapping(mapping, request);
if (match != null) {
// 根据RequestMappingInfo从mappingLookup中取出对应的HandlerMethod,
// 然后将新的RequestMappingInfo实例和HandlerMethod封装成Match
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
获取到处理器后,即需要再获取到拦截器,然后封装成一个执行链
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// 创建一个执行链
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
// 请求路径
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
// 如果是MappedInterceptor类型的拦截器,需要判断下是否跟请求路径匹配
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
// 不是MappedInterceptor类型的拦截器直接添加到执行链中
chain.addInterceptor(interceptor);
}
}
return chain;
}
确定请求的处理器适配器
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
在这里handler就是HandlerMethod,handlerAdapters是如下3个
循环遍历每个适配器,调用supports方法判断是否适用于handler
HttpRequestHandlerAdapter
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
SimpleControllerHandlerAdapter
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
HandlerMethod没有实现HttpRequestHandler接口和Controller接口,所以都返回false。
RequestMappingHandlerAdapter
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
明显返回true,所以请求的处理器适配器是RequestMappingHandlerAdapter。
反射调用处理请求的方法,返回ModelAndView
在调controller的方法之前,还需要执行拦截器的preHandle方法。
然后调用适配器的handle方法
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 验证请求
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
// 默认false
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 调用
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 从给定的HandlerMethod定义创建一个ServletInvocableHandlerMethod,
// ServletInvocableHandlerMethod继承自HandlerMethod,扩展了功能,
// 可以设置参数解析器和返回值处理器
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 设置参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 设置返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 用来记录model和view
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 解析请求参数,调用controller方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 返回ModelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
// 标记请求完成
webRequest.requestCompleted();
}
}
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 调用处理方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 设置响应状态
setResponseStatus(webRequest);
// 返回值为空的处理
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
// 表示请求处理完了,不需要视图解析
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
// 表示还没处理完
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 将返回值封装成ReturnValueMethodParameter,
// 使用返回值解析器解析返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 解析请求参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 通过反射调用方法
return doInvoke(args);
}
解析请求参数值
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 得到方法参数,MethodParameter封装了参数的所有信息
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 判断是否有参数解析器可以支持该参数
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 解析参数
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);// 从请求中解析出当前参数的值
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
通过委托给已注册的HandlerMethodArgumentResolvers列表来解析方法参数。先判断是否有参数解析器可以支持该参数,resolvers是HandlerMethodArgumentResolverComposite类,也实现了HandlerMethodArgumentResolver接口,是一个组合解析器,它是一个代理,具体代理其余干活的那些参数解析器,持有所有的HandlerMethodArgumentResolver接口实现类。
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 看看是否有可以解析该参数的解析器
return getArgumentResolver(parameter) != null;
}
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
// 先从缓存中看看是否有该参数对应的解析器,因为第一次调完就将参数与参数解析器的对应关系
// 缓存起来,后面再调就不需要重新判断了,直接将对应参数的解析器取出就行了
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
// 遍历所有解析器,判断是否支持,遇到一个支持就结束,并放到缓存中
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
通过遍历所有的参数解析器来看看是不是支持解析该参数。
spring默认提供了26种参数解析器,用来解析各种参数类型,比如我们最常用的参数注解 @RequestParam 就是由 RequestParamMethodArgumentResolver 解析的,PathVariableMethodArgumentResolver 用来解析 @PathVariable 注解。
知道了有参数解析器可以解析当前的参数后,就使用该解析器来解析。跟上面一样,也是通过HandlerMethodArgumentResolverComposite来代理
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 获取该参数对应的参数解析器,这个方法上面已经解析过了,这里就不细说了
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
// 使用具体参数解析器解析
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
这里使用 RequestParamMethodArgumentResolver 来做一个解析。
RequestParamMethodArgumentResolver
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 获取给定方法参数的NamedValueInfo,就是解析@RequestParam注解,
// 然后将参数名,是否必传以及默认值封装成NamedValueInfo
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
// 解析出真正参数名,因为@RequestParam注解中的参数名有可能使用表达式来引用外部变量,
// 比如@RequestParam("${param}") 这样,需要解析出${param}真正的参数名称
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
// 根据参数名从请求中解析出参数值
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
// 如果没解析出来则用默认值,也是使用resolveStringValue方法,因为默认值也可以使用表达式
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
// 没有设置默认值,则再看该参数是否是必传的,如果是必传的则抛出异常
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
// 非必传则就使用null,如果是boolean类型,则是false
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
// 如果解析出来的值是空字符串"",但设置了默认值则使用默认值
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
// 做类型转换,因为request中的参数统一都是String类型的,需要转换成需要的类型
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
// 参数解析后的处理,这里面没有代码,由子类实现
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
看下如何从请求中解析出参数值
@Override
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
// 处理文件上传
if (servletRequest != null) {
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
return mpArg;
}
}
Object arg = null;
MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}
if (arg == null) {
// 根据参数名从request获取到对应的值返回
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}
上面两个if都是处理文件上传的,最后一个if就是解析出参数值,直接根据参数名从request获取到对应的值返回。
参数解析出来后,就是通过反射调用Controller中的方法了。
调用Controller方法返回结果
@Nullable
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 直接通过反射调用
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}
接下来就是对返回值进行处理。
解析Controller方法返回值
与参数解析器一样,这里是使用返回值处理器处理,并且也是使用一个组合处理// 器来解析。HandlerMethodReturnValueHandlerComposite 持有所有的 HandlerMethodReturnValueHandler 接口实现类。
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 根据返回值和类型选择一个处理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
// 使用具体处理器处理
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
// 判断是不是异步返回值
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
// 如果是异步的但处理器不是用来处理异步的就跳过
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
// 判断该处理器支不支持处理当前返回值类型
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
spring默认也提供了15种处理器,来处理各种返回值
比如 ModelAndViewMethodReturnValueHandler 用来处理返回 ModelAndView 的,ModelMethodProcessor 用来处理 Model类型的,RequestResponseBodyMethodProcessor 用来处理 @ResponseBody 注解的,而如果是返回一个视图名字符串,则使用 ViewNameMethodReturnValueHandler 处理。
这里以 ViewNameMethodReturnValueHandler 为例解析如何处理
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
// 给ModelAndViewContainer这是视图名
mavContainer.setViewName(viewName);
// 判断是否是重定向,即以redirect:开头
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else if (returnValue != null) {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
如果是 RequestResponseBodyMethodProcessor 则使用输出流将结果响应回客户端,不会有后面的视图解析。
返回ModelAndView
这里开始到后面的处理都是针对需要视图返回的
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
// 已经处理完了就返回null
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
// 根据视图名,模型和状态新建一个ModelAndView返回
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
解析以及渲染视图
这是最后一步了,通过视图解析器将视图名解析成具体的某一资源,比如jsp页面。
最终底层就是调用request.getRequestDispatcher(path).forward(request, response)
来返回页面。
就不再具体解析了,知道就行了。