01-IOC_init()
- Servlet = server + applet(服务端小程序)
- Servlet生命周期
init()
service()
destroy()
- Servlet关键类
DispathcherServlet
,关键配置web.xml
- Tomcat第一件事加载
web.xml
每写一个Servlet都要写以下原生配置,太麻烦
<!-- SpringMVC配置 -->
<!-- 配置DispatcherServlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始化参数,读取SpringMVC的核心配置文件 -->
<!-- 不使用`<init-param>`,默认路径:/WEB-INF/<servlet-name>-servlet.xml -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring_mvc.xml</param-value>
</init-param>
<!-- 启动立即初始化servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 映射路径为
/:匹配所有资源,JSP除外
/*:全部servlet + JSP
-->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- SpringMVC是Spring的扩展,在启动SpringMVC前必须先把Spring容器启动起来
web.xml
是Tomcat进行加载、解析- SpringMVC继承Spring容器(两容器目的:隔离)。子容器可以使用父容器bean,父容器不能使用子容器
1. Spring容器创建
- 入口:
ContextLoaderListener#contextInitialized()
<!-- Spring配置 -->
<!-- ServletContext中添加Spring核心配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application_context.xml</param-value>
</context-param>
<!-- 监听器,JAVAWEB项目中的ServletContext创建时,创建一个Spring的容器,放入ServletContext对象中 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
1. ContextLoaderListener
package org.springframework.web.context;
/**
* Bootstrap listener to start up and shut down Spring's root {@link WebApplicationContext}.
* Simply delegates to {@link ContextLoader} as well as to {@link ContextCleanupListener}.
*
* <p>As of Spring 3.1, {@code ContextLoaderListener} supports injecting the root web
* application context via the {@link #ContextLoaderListener(WebApplicationContext)}
* constructor, allowing for programmatic configuration in Servlet 3.0+ environments.
* See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
*/
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
/**
* Initialize the root web application context.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
// 1... ContextLoader
initWebApplicationContext(event.getServletContext());
}
/**
* Close the root web application context.
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
2. ContextLoader
- spring-web模块下
ContextLoader.properties
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-config.xml</param-value>
</context-param>
package org.springframework.web.context;
/**
* @see ContextLoaderListener
* @see ConfigurableWebApplicationContext
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
public class ContextLoader {
/**
* Name of the class path resource (relative to the ContextLoader class)
* that defines ContextLoader's default strategy names.
*/
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
private static final Properties defaultStrategies;
/**
* The root WebApplicationContext instance that this loader manages.
*/
@Nullable
private WebApplicationContext context;
public static final String CONTEXT_CLASS_PARAM = "contextClass";
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
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());
}
}
/**
* Initialize Spring's web application context for the given servlet context,
* using the application context provided at construction time, or creating a new one
* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
* @param servletContext current servlet context
* @return the new WebApplicationContext
* @see #ContextLoader(WebApplicationContext)
* @see #CONTEXT_CLASS_PARAM
* @see #CONFIG_LOCATION_PARAM
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
// web.xml中存在多次ContextLoader定义
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 {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
// 1.. 初始化context,第一次执行的时候获取到一个root webApplicationcontext
// Root WebApplicationContext, started on Thu Jan 01 08:00:00 CST 1970
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.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// 2.. 创建并且准备好了Spring容器
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// 将创建的context对象记录在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;
}
}
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// 1.. 获取contextClass的Class对象
Class<?> contextClass = determineContextClass(sc);
// 如果是自定义的contextClass对象,那么必须要实现ConfigurableWebApplicationContext此接口
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
// 2.
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
protected Class<?> determineContextClass(ServletContext servletContext) {
// 加载contextClass配置,一般无此配置
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); // contextClass
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
// 1. XmlWebApplicationContext
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
// 2.
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
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()));
}
}
wac.setServletContext(sc);
// contextConfigLocation => classpath:spring-config.xml
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
// 1. Spring配置文件设置
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);
// 2. Spring.refresh()
wac.refresh();
}
}
1. ContextLoader.properties
spring-web-5.2.2.RELEASE.jar/org/springframework/web/context/ContextLoader.properties
# 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
3. XmlWebApplicationContext
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
/** Default config location for the root context. */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
/** Default prefix for building a config location for a namespace. */
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
/** Default suffix for building a config location for a namespace. */
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
}
- Spring 的拓展方法
public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {
public AbstractRefreshableWebApplicationContext() {
setDisplayName("Root WebApplicationContext");
}
/**
* Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
*/
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
// mvc增加session、request作用域
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
/**
* Initialize the theme capability.
*/
@Override
protected void onRefresh() {
this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}
}
2. xml_MVC
Spring全家桶框架,都是一个框架在Spring框架基础之上做的延伸、扩展
ServletContext
是web.xml
整个配置ServletConfig
为某个Servlet的配置DispatcherServlet.init()
生命周期第一步,Tomcat调用执行
<!-- SpringMVC配置 -->
<!-- 配置DispatcherServlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始化参数,读取SpringMVC的核心配置文件 -->
<!-- 不使用`<init-param>`,默认路径:/WEB-INF/<servlet-name>-servlet.xml -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring_mvc.xml</param-value>
</init-param>
<!-- 启动立即初始化servlet -->
<load-on-startup>1</load-on-startup>
<!--<async-supported>true</async-supported>-->
</servlet>
<!-- 映射路径为
/:匹配所有资源,JSP除外
/*:全部servlet + JSP
-->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
1. HttpServletBean#init()
- 入口:
DispatcherServlet#init()
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
@Nullable
private ConfigurableEnvironment environment;
private final Set<String> requiredProperties = new HashSet<>(4);
/**
* Map config parameters onto bean properties of this servlet, and
* invoke subclass initialization.
* @throws ServletException if bean properties are invalid (or required
* properties are missing), or if subclass initialization fails.
*/
@Override
public final void init() throws ServletException {
// 1. Set bean properties from init parameters.
// 将<servlet>中配置的<init-param>参数封装到pvs变量中。contextConfigLocation -> classpath:spring-mvc.xml
// GenericServlet#getServletConfig()
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 将当前的servlet对象转化成BeanWrapper对象,从而能够以spring的方法来将pvs注入到该beanWrapper中
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
// 注册自定义属性编辑器,一旦有Resource类型的属性,将会使用ResourceEditor进行解析
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
// 模板方法,可以在子类调用,做一些初始化工作,bw代表的是DispatcherServlet
initBeanWrapper(bw);
// 以spring的方式来将pvs注入到该beanWrapper对象中,将配置的初始化值(contextConfigLocation)设置到DispatcherServlet
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// 2... FrameworkServlet. Let subclasses do whatever initialization they like.
// 模板方法,子类初始化的入口方法。FrameworkServlet#initServletBean()
initServletBean();
}
}
2. FrameworkServlet
- 195:
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
/**
* Default context class for FrameworkServlet.
*
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
/**
* WebApplicationContext implementation class to create.
*/
private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
/**
* Overridden method of {@link HttpServletBean}, invoked after any bean properties
* have been set. Creates this servlet's WebApplicationContext.
*/
@Override
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 {
// 1.. 创建或刷新WebApplicationContext实例并对servlet功能所使用的变量进行初始化
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为基础,在使用SpringMVC时,默认把自己的容器设置成ServletContext的属性
* 默认根容器的key为`WebApplicaitonContext.Root`,定义在WebApplicationContext中,调用ServletContext.getAttribute即可获取
* <p>
* Initialize and publish the WebApplicationContext for this servlet.
* <p>Delegates to {@link #createWebApplicationContext} for actual creation
* of the context. Can be overridden in subclasses.
*
* @return the WebApplicationContext instance
* @see #FrameworkServlet(WebApplicationContext)
* @see #setContextClass
* @see #setContextConfigLocation
*/
protected WebApplicationContext initWebApplicationContext() {
// 1. **GenericServlet 获取Spring的root_webApplicationContext对象**
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
// 获得webApplicationContext wac对象
WebApplicationContext wac = null;
// 如果构造方法中已经传入webApplicationContext属性,则直接使用
// 此方式主要用于servlet3.0之后的环境,Tomcat可以通过ServletContext.addServlet()注册servlet,此时就可以在创建FrameworkServlet和
// 其子类的时候通过构造方法传递已经准备好的webApplicationContext
if (this.webApplicationContext != null) { // skip
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
// 如果是ConfigurationWebApplicationContext类型,并且未激活,则进行初始化
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);
}
}
}
// 从servletContext获取对应的webApplicationContext对象
// 此方式需要在配置Servlet的时候将servletContext中的webApplicationContext的name配置到contextAttribute属性就可以
if (wac == null) { // skip
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
// 当前面两种方式都无效的情况下会创建一个webApplicationContext对象,一般情况下都是使用这样的方式
if (wac == null) {
// 2.. No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
// 将contextRefreshedEvent事件没有触发时调用此方法,模板方法,可以在子类重写
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);
}
}
// 将applicationContext设置到servletContext中
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) {
// 1..
return createWebApplicationContext((ApplicationContext) parent);
}
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// 1.. 获取servlet的初始化参数contextClass,默认为XmlWebApplicationContext.class
Class<?> contextClass = getContextClass();
// 如果非ConfigurableWebApplicationContext类型,抛出ConfigurableWebApplicationContext异常
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");
}
// 2. 通过反射方式实例化contextClass
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
// 设置environment
wac.setEnvironment(getEnvironment());
// 3. **parent为Spring_ApplicationContext**
wac.setParent(parent);
// 获取contextConfigLocation属性,配置在servlet初始化参数中
String configLocation = getContextConfigLocation();
if (configLocation != null) {
// 4. 将设置的contextConfigLocation参数传给wac,默认传入WEB-INFO/servletName-servlet.xml
wac.setConfigLocation(configLocation);
}
// 5.. 配置和初始化wac
configureAndRefreshWebApplicationContext(wac);
return wac;
}
/**
* Return the custom context class.
*/
public Class<?> getContextClass() {
return this.contextClass; // XmlWebApplicationContext
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
// 如果wac使用了默认编号,则重新设置id属性
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
// 使用contextId属性
if (this.contextId != null) {
wac.setId(this.contextId);
}
// 自动生成
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
// 1. 设置wac的servletContext、servletConfig、namespace属性
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
// 2. 添加监听器SourceFilteringListener到wac中,实际监听的是ContextRefreshListener所监听的事件,监听ContextRefreshedEvent事件,
// 当接收到消息之后会调用onApplicationEvent(),调用onRefresh(),并将refreshEventReceived标志设置为true,表示已经refresh过
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
// 3. 获取环境对象并且添加相关的属性
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
// 此处为空方法,不做任何实现。可拓展
postProcessWebApplicationContext(wac);
// 4.. 执行自定义初始化context
applyInitializers(wac);
// 5. 刷新wac,从而初始化wac
wac.refresh();
}
protected void applyInitializers(ConfigurableApplicationContext wac) {
// web.xml中加载<context-param>,<param-name>globalInitializerClasses</param-name>
String globalClassNames = getServletContext().getInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM);
if (globalClassNames != null) {
for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
this.contextInitializers.add(loadInitializer(className, wac));
}
}
if (this.contextInitializerClasses != null) {
for (String className : StringUtils.tokenizeToStringArray(this.contextInitializerClasses,
INIT_PARAM_DELIMITERS)) {
this.contextInitializers.add(loadInitializer(className, wac));
}
}
AnnotationAwareOrderComparator.sort(this.contextInitializers);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
// 1. SpringBoot里有initializer
initializer.initialize(wac);
}
}
}
3. MVC_refresh()_finishRefresh()
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
/*
* 前戏,做容器刷新前的准备工作
* 1. 设置容器的启动时间
* 2. 设置关闭标志位为false,激活标志位为true
* 3. 设置Environment,并加载当前系统的属性值到Environment
* 4. 准备监听器、事件集合,Spring默认为空
*/
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
/*
* 1. 创建容器对象:DefaultListableBeanFactory
* 2. 加载xml到BF => BeanDefinition
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// BF前戏,各种属性填充`add(), register()`
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
/*
* 1. 模板方法,后续拓展
* 2. 子类重写做额外的处理,查看web中的代码,有具体实现的
*/
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
/*
* 1. 实例化 & 执行BFPP
* 2. 首次getBean()
*/
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
/*
* 1. 实例化前戏
* 2. 实例化 & 注册BPP。真正调用是getBean()
*/
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
/*
* 1. 国际化处理。为上下文初始化message源,即不同语言消息体
* 2. SpringMVC,国际化的代码重点讲
*/
initMessageSource();
// Initialize event multicaster for this context.
/*
* 1. 实例化前戏
* 2. init Multicaster(多路广播器),用于bean各阶段事件监听
*/
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 模板方法,后续拓展
onRefresh();
// Check for listener beans and register them.
/*
* 1. 实例化前戏
* 2. 注册监听器。bean中查找listener_bean,注册到消息广播器中
*/
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 1. 生命周期处理器lifecycleProcessor初始化并onRefresh
// 2. 发布ContextRefreshEvent
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
// 为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件bean
destroyBeans();
// Reset 'active' flag.
// 重置active标志
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
/**
* 完成刷新
* Finish the refresh of this context, invoking the LifecycleProcessor's
* onRefresh() method and publishing the
* {@link org.springframework.context.event.ContextRefreshedEvent}.
*/
protected void finishRefresh() {
// 1. Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
// 2. 默认DefaultLifecycleProcessor
initLifecycleProcessor();
// 3. Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// 4.. Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
// 5. LiveBeansView: Spring用于支持JMX服务
LiveBeansView.registerApplicationContext(this);
}
@Override
public void publishEvent(ApplicationEvent event) {
// 1..
publishEvent(event, null);
}
/**
* 将给定事件发布到所有监听器
* <p>
* Publish the given event to all listeners.
*
* @param event the event to publish (may be an {@link ApplicationEvent}
* or a payload object to be turned into a {@link PayloadApplicationEvent})
* @param eventType the resolved event type, if known
* @since 4.2
*/
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
// 如果event为null,抛出异常
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
} else {
// PayloadApplicationEvent:携带任意有效负载的ApplicationEvent
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
// 如果可能的话,现在就进行组播——或者在组播初始化后延迟
// earlyApplicationEvents:在多播程序设置之前发布的ApplicationEvent
// 如果earlyApplicationEvents不为null,这种情况只在上下文的多播器还没有初始化的情况下才会成立,会将applicationEvent
// 添加到earlyApplicationEvents保存起来,待多博器初始化后才继续进行多播到适当的监听器
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
// 1... SimpleApplicationEventMulticaster 多播applicationEvent到适当的监听器
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
// 通过父上下文发布事件
// 如果parent不为null
if (this.parent != null) {
// 如果parent是AbstractApplicationContext的实例
if (this.parent instanceof AbstractApplicationContext) {
// 将event多播到所有适合的监听器。如果event不是ApplicationEvent实例,会将其封装成PayloadApplicationEvent对象再进行多播
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
} else {
// 通知与event事件应用程序注册的所有匹配的监听器
this.parent.publishEvent(event);
}
}
}
}
1. SimpleAppEventMulticaster
SourceFilteringListener
来源
FrameworkServlet#configureAndRefreshWebApplicationContext()
=> ApplicationContextwac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
AbstractApplicationContext#registerListeners()
=> Multicaster
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
/**
* 广播事件
*
* @param event the event to multicast
* @param eventType the type of event (can be {@code null})
*/
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
// 如果eventType不为null就引用eventType;否则将event转换为ResolvableType对象再引用
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 获取此多播器的当前任务线程池
Executor executor = getTaskExecutor();
// getApplicationListeners方法是返回与给定事件类型匹配的应用监听器集合
// 遍历获取所有支持event的监听器
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
// 如果executor不为null
if (executor != null) {
// 使用executor回调listener的onApplicationEvent方法,传入event
executor.execute(() -> invokeListener(listener, event));
} else {
// 1.. 回调listener的onApplicationEvent方法,传入event
invokeListener(listener, event);
}
}
}
/**
* 使用给定的事件调用给定的监听器
* <p>
* Invoke the given listener with the given event.
*
* @param listener the ApplicationListener to invoke
* @param event the current event to propagate
* @since 4.1
*/
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
// 获取此多播器的当前错误处理程序
ErrorHandler errorHandler = getErrorHandler();
// 如果errorHandler不为null
if (errorHandler != null) {
try {
// 回调listener的onApplicationEvent方法,传入event
doInvokeListener(listener, event);
} catch (Throwable err) {
// 交给errorHandler接收处理err
errorHandler.handleError(err);
}
} else {
// 1.. 回调listener的onApplicationEvent方法,传入event
doInvokeListener(listener, event);
}
}
/**
* 回调listener的onApplicationEvent方法,传入 event
*/
@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 1.. 传入event
// ContextRefreshListener#onApplicaitonEvent() => FrameworkServlet.this.onApplicationEvent()
listener.onApplicationEvent(event);
} catch (ClassCastException ex) {
// 获取异常信息
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
} else {
// 抛出异常
throw ex;
}
}
}
}
2. SourceFilteringListener
- FrameworkServlet
195: wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
public class SourceFilteringListener implements GenericApplicationListener, SmartApplicationListener {
@Nullable
private GenericApplicationListener delegate;
public SourceFilteringListener(Object source, ApplicationListener<?> delegate) {
this.source = source;
this.delegate = (delegate instanceof GenericApplicationListener ?
(GenericApplicationListener) delegate : new GenericApplicationListenerAdapter(delegate));
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event.getSource() == this.source) {
// 1..
onApplicationEventInternal(event);
}
}
protected void onApplicationEventInternal(ApplicationEvent event) {
if (this.delegate == null) {
throw new IllegalStateException(
"Must specify a delegate object or override the onApplicationEventInternal method");
}
// 1... GenericApplicationListenerAdapter
this.delegate.onApplicationEvent(event);
}
}
1. GenericAppListenerAdapter
public class GenericApplicationListenerAdapter implements GenericApplicationListener, SmartApplicationListener {
private final ApplicationListener<ApplicationEvent> delegate;
/**
* Create a new GenericApplicationListener for the given delegate.
* @param delegate the delegate listener to be invoked
*/
@SuppressWarnings("unchecked")
public GenericApplicationListenerAdapter(ApplicationListener<?> delegate) {
Assert.notNull(delegate, "Delegate listener must not be null");
this.delegate = (ApplicationListener<ApplicationEvent>) delegate;
this.declaredEventType = resolveDeclaredEventType(this.delegate);
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 1... FrameworkServlet$ContextRefreshListener
this.delegate.onApplicationEvent(event);
}
}
2. ContextRefreshListener
@SuppressWarnings("serial")
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
/**
* ApplicationListener endpoint that receives events from this servlet's WebApplicationContext
* only, delegating to {@code onApplicationEvent} on the FrameworkServlet instance.
*/
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 1..
FrameworkServlet.this.onApplicationEvent(event);
}
}
/**
* Callback that receives refresh events from this servlet's WebApplicationContext.
* <p>The default implementation calls {@link #onRefresh},
* triggering a refresh of this servlet's context-dependent state.
* @param event the incoming ApplicationContext event
*/
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
// 1... DispatcherServlet
onRefresh(event.getApplicationContext());
}
}
}
3. DispatcherServlet
public class DispatcherServlet extends FrameworkServlet {
private static final Properties defaultStrategies;
/**
* Name of the class path resource (relative to the DispatcherServlet class)
* that defines DispatcherServlet's default strategy names.
*/
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
/**
* Well-known name for the LocaleResolver object in the bean factory for this namespace.
*/
public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
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());
}
}
/**
* multipart数据文件处理器
* MultipartResolver used by this servlet.
*/
@Nullable
private MultipartResolver multipartResolver;
/**
* 语言处理器,提供国际化的支持
* LocaleResolver used by this servlet.
*/
@Nullable
private LocaleResolver localeResolver;
/**
* 主题处理器,设置需要应用的整体格式
* ThemeResolver used by this servlet.
*/
@Nullable
private ThemeResolver themeResolver;
/**
* 处理器匹配器,返回请求对应的处理器和拦截器
* List of HandlerMappings used by this servlet.
*/
@Nullable
private List<HandlerMapping> handlerMappings;
/**
* 处理器适配器,用于执行处理器
* <p>
* List of HandlerAdapters used by this servlet.
*/
@Nullable
private List<HandlerAdapter> handlerAdapters;
/**
* 异常处理器,用于解析处理器发生的异常
* List of HandlerExceptionResolvers used by this servlet.
*/
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/**
* 视图名称转换器
* RequestToViewNameTranslator used by this servlet.
*/
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
/**
* FlashMap管理器,负责重定向保存参数到临时存储(默认session)中
* FlashMapManager used by this servlet.
*/
@Nullable
private FlashMapManager flashMapManager;
/**
* 视图解析器,根据视图名称和语言,获取View视图
* List of ViewResolvers used by this servlet.
*/
@Nullable
private List<ViewResolver> viewResolvers;
/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
// 1.. XmlWebApplicationContext
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
// 1. MultipartResolver:处理文件上传。可自定义
initMultipartResolver(context);
// 2.. LocaleResolver:处理国际化配置
// 基于URL参数配置(AcceptHeaderLocaleResolver),基于session配置(SessionLocaleResolver),基于cookie配置(CookieLocaleResolver)
initLocaleResolver(context);
// 3. ThemeResolver:设置主题Theme
initThemeResolver(context);
// 4. HandlerMapping:映射器,用来将对应的Request跟Controller进行对应
initHandlerMappings(context);
// 5. HandlerAdapter:处理适配器,主要包含Http请求处理器适配器,简单控制器处理器适配器,注解方法处理器适配器
initHandlerAdapters(context);
// 6. HandlerExceptionResolver:基于HandlerExceptionResolver接口的异常处理
initHandlerExceptionResolvers(context);
// 7. RequestToViewNameTranslator
// 当Controller没有返回View对象或逻辑视图名称,并且没有直接往Response输出流写数据时,Spring将会采用约定方式提供一个逻辑视图名称
initRequestToViewNameTranslator(context);
// 8. ViewResolver:将ModelAndView选择合适的视图进行渲染的处理器
initViewResolvers(context);
// 9. FlashMapManager:提供请求存储属性,供其他请求使用
initFlashMapManager(context);
}
/**
* Initialize the LocaleResolver used by this class.
* <p>If no bean is defined with the given name in the BeanFactory for this namespace,
* we default to AcceptHeaderLocaleResolver.
*/
private void initLocaleResolver(ApplicationContext context) {
try {
// 1. IOC中取localeResolver对象,取不到抛异常
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.localeResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
// 2.. 从DispatcherServlet.properties配置文件中获取
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
}
}
}
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
// 1..
List<T> strategies = getDefaultStrategies(context, strategyInterface);
if (strategies.size() != 1) {
throw new BeanInitializationException(
"DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
}
// 2.
return strategies.get(0);
}
@SuppressWarnings("unchecked")
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
// 1. 获得strategyInterface对应的value值
String key = strategyInterface.getName();
// 2. 创建value对应的对象们,并返回
String value = defaultStrategies.getProperty(key);
if (value != null) {
// 基于","分隔,创建classNames数组
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
// 创建strategyInterface集合
List<T> strategies = new ArrayList<>(classNames.length);
// 遍历classNames数组,创建对应的类,添加到strategyInterface中
for (String className : classNames) {
try {
// 3. 获得className类
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
// 4.. 创建className对应的类,并添加到strategies中
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
return strategies;
}
else {
return new LinkedList<>();
}
}
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
// 1... AbstractAutowireCapableBeanFactory => DefaultListableBeanFactory
return context.getAutowireCapableBeanFactory().createBean(clazz);
}
}
1. DispatcherServlet.properties
- 源码环境properties会覆盖。
jar
形式不会覆盖
spring-webmvc/org.springframework.web.servlet/resources/DispatcherServlet.properties
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
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
/**
* Initialize the MultipartResolver used by this class.
* <p>If no bean is defined with the given name in the BeanFactory for this namespace,
* no multipart handling is provided.
*/
private void initMultipartResolver(ApplicationContext context) {
try {
// IOC中获取MultipartResolver的Bean
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.multipartResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
this.multipartResolver = null;
if (logger.isTraceEnabled()) {
logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
}
}
}
/**
* Initialize the LocaleResolver used by this class.
* <p>If no bean is defined with the given name in the BeanFactory for this namespace,
* we default to AcceptHeaderLocaleResolver.
*/
private void initLocaleResolver(ApplicationContext context) {
try {
// 1. IOC中取localeResolver对象,取不到抛异常
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.localeResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
// 2.. 从DispatcherServlet.properties配置文件中获取
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
}
}
}
/**
* Initialize the ThemeResolver used by this class.
* <p>If no bean is defined with the given name in the BeanFactory for this namespace,
* we default to a FixedThemeResolver.
*/
private void initThemeResolver(ApplicationContext context) {
try {
this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.themeResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.themeResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
// 从配置文件中获取默认的FixedThemeResolver
this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No ThemeResolver '" + THEME_RESOLVER_BEAN_NAME +
"': using default [" + this.themeResolver.getClass().getSimpleName() + "]");
}
}
}
/**
* Initialize the HandlerMappings used by this class.
* <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
* we default to BeanNameUrlHandlerMapping.
*/
private void initHandlerMappings(ApplicationContext context) {
// 将handlerMappings置空
this.handlerMappings = null;
// 如果开启探测功能,则扫描已注册的HandlerMapping的bean,添加到handlerMappings中
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 扫描已注册的handlerMapping的bean
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
// 添加到handlerMappings中,并进行排序
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order. 可以定义多种视图,按照优先级进行排序操作
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
// 如果关闭探测功能,则获取Bean名称为handlerMapping对应的bean,将其添加到handlerMappings
else {
try {
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类
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");
}
}
}
/**
* Initialize the HandlerAdapters used by this class.
* <p>If no HandlerAdapter beans are defined in the BeanFactory for this namespace,
* we default to SimpleControllerHandlerAdapter.
*/
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}
// Ensure we have at least some HandlerAdapters, by registering
// default HandlerAdapters if no other adapters are found.
// 如果未获得到,则获得默认配置的HandlerAdapter类
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
/**
* Initialize the HandlerExceptionResolver used by this class.
* <p>If no bean is defined with the given name in the BeanFactory for this namespace,
* we default to no exception resolver.
*/
private void initHandlerExceptionResolvers(ApplicationContext context) {
// 置空 handlerExceptionResolver 处理
this.handlerExceptionResolvers = null;
// 自动扫描handlerExceptionResolver类型的bean
if (this.detectAllHandlerExceptionResolvers) {
// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
// We keep HandlerExceptionResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
// 获取名字为HANDLER_EXCEPTION_RESOLVER_BEAN_NAME的bean
else {
try {
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, no HandlerExceptionResolver is fine too.
}
}
// Ensure we have at least some HandlerExceptionResolvers, by registering
// default HandlerExceptionResolvers if no other resolvers are found.
// 如果未获得到,则获取默认配置的handlerExceptionResolver类
if (this.handlerExceptionResolvers == null) {
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
/**
* Initialize the RequestToViewNameTranslator used by this servlet instance.
* <p>If no implementation is configured then we default to DefaultRequestToViewNameTranslator.
*/
private void initRequestToViewNameTranslator(ApplicationContext context) {
try {
this.viewNameTranslator =
context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.viewNameTranslator.getClass().getSimpleName());
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.viewNameTranslator);
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
// 如果未找到,则获取默认的 RequestToViewNameTranslator 对象,DefaultRequestToViewNameTranslator
this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
if (logger.isTraceEnabled()) {
logger.trace("No RequestToViewNameTranslator '" + REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME +
"': using default [" + this.viewNameTranslator.getClass().getSimpleName() + "]");
}
}
}
/**
* Initialize the ViewResolvers used by this class.
* <p>If no ViewResolver beans are defined in the BeanFactory for this
* namespace, we default to InternalResourceViewResolver.
*/
private void initViewResolvers(ApplicationContext context) {
// 置空 viewResolvers 处理
this.viewResolvers = null;
/// 自动扫描 ViewResolver 类型的 Bean
if (this.detectAllViewResolvers) {
// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
Map<String, ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.viewResolvers = new ArrayList<>(matchingBeans.values());
// We keep ViewResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
}
// 获得名字为VIEW_RESOLVER_BEAN_NAME的bean
else {
try {
ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
this.viewResolvers = Collections.singletonList(vr);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default ViewResolver later.
}
}
// Ensure we have at least one ViewResolver, by registering
// a default ViewResolver if no other resolvers are found.
// 如果未获得到,则获取默认配置的ViewResolver对象
if (this.viewResolvers == null) {
this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No ViewResolvers declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
/**
* Initialize the {@link FlashMapManager} used by this servlet instance.
* <p>If no implementation is configured then we default to
* {@code org.springframework.web.servlet.support.DefaultFlashMapManager}.
*/
private void initFlashMapManager(ApplicationContext context) {
try {
this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.flashMapManager.getClass().getSimpleName());
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.flashMapManager);
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
// 未找到,则获取默认的 FlashMapManager 对象,SessionFlashMapManager
this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
if (logger.isTraceEnabled()) {
logger.trace("No FlashMapManager '" + FLASH_MAP_MANAGER_BEAN_NAME +
"': using default [" + this.flashMapManager.getClass().getSimpleName() + "]");
}
}
}
4. AbsAutowireCapableBF
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
@Override
@SuppressWarnings("unchecked")
public <T> T createBean(Class<T> beanClass) throws BeansException {
// Use prototype bean definition, to avoid registering bean as dependent bean.
// 封装RootBeanDefinition
RootBeanDefinition bd = new RootBeanDefinition(beanClass);
// 设置bean的作用域
bd.setScope(SCOPE_PROTOTYPE);
// 是否允许被缓存
bd.allowCaching = ClassUtils.isCacheSafe(beanClass, getBeanClassLoader());
return (T) createBean(beanClass.getName(), bd, null);
}
}
3. boot_MVC
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
1. WebMvcAutoConfiguration
package org.springframework.boot.autoconfigure.web.servlet;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link EnableWebMvc Web MVC}.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
public static final String DEFAULT_PREFIX = "";
public static final String DEFAULT_SUFFIX = "";
private static final String[] SERVLET_LOCATIONS = { "/" };
// ---------------------------------------------------------------------------------------------
// Defined as a nested config to ensure WebMvcConfigurer is not read when not
// on the classpath
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);
private final ResourceProperties resourceProperties;
private final WebMvcProperties mvcProperties;
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
@Bean
@ConditionalOnBean(View.class)
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return resolver;
}
@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
// ContentNegotiatingViewResolver uses all the other view resolvers to locate
// a view so it should have a high precedence
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
}
// ---------------------------------------------------------------------------------------------
/**
* Configuration equivalent to {@code @EnableWebMvc}.
*/
@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
private final ResourceProperties resourceProperties;
private final WebMvcProperties mvcProperties;
private final ListableBeanFactory beanFactory;
private final WebMvcRegistrations mvcRegistrations;
private ResourceLoader resourceLoader;
public EnableWebMvcConfiguration(ResourceProperties resourceProperties,
ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider, ListableBeanFactory beanFactory) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
this.beanFactory = beanFactory;
}
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager,
conversionService, validator);
adapter.setIgnoreDefaultModelOnRedirect(
this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}
}
}
2. WebMvcConfigurationSupport
package org.springframework.web.servlet.config.annotation;
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
private static final boolean romePresent;
private static final boolean jaxb2Present;
private static final boolean jackson2Present;
private static final boolean jackson2XmlPresent;
private static final boolean jackson2SmilePresent;
private static final boolean jackson2CborPresent;
private static final boolean gsonPresent;
private static final boolean jsonbPresent;
static {
ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();
romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
}
@Nullable
private ApplicationContext applicationContext;
@Nullable
private ServletContext servletContext;
@Nullable
private List<Object> interceptors;
@Nullable
private PathMatchConfigurer pathMatchConfigurer;
@Nullable
private ContentNegotiationManager contentNegotiationManager;
@Nullable
private List<HandlerMethodArgumentResolver> argumentResolvers;
@Nullable
private List<HandlerMethodReturnValueHandler> returnValueHandlers;
@Nullable
private List<HttpMessageConverter<?>> messageConverters;
@Nullable
private Map<String, CorsConfiguration> corsConfigurations;
/**
* Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping
* requests to annotated controllers.
*/
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setContentNegotiationManager(contentNegotiationManager);
mapping.setCorsConfigurations(getCorsConfigurations());
PathMatchConfigurer configurer = getPathMatchConfigurer();
Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}
UrlPathHelper pathHelper = configurer.getUrlPathHelper();
if (pathHelper != null) {
mapping.setUrlPathHelper(pathHelper);
}
PathMatcher pathMatcher = configurer.getPathMatcher();
if (pathMatcher != null) {
mapping.setPathMatcher(pathMatcher);
}
Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes();
if (pathPrefixes != null) {
mapping.setPathPrefixes(pathPrefixes);
}
return mapping;
}
/**
* Protected method for plugging in a custom subclass of
* {@link RequestMappingHandlerMapping}.
* @since 4.0
*/
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
/**
* Return a {@link BeanNameUrlHandlerMapping} ordered at 2 to map URL
* paths to controller bean names.
*/
@Bean
public BeanNameUrlHandlerMapping beanNameHandlerMapping(
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
mapping.setOrder(2);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setCorsConfigurations(getCorsConfigurations());
return mapping;
}
/**
* Returns a {@link RequestMappingHandlerAdapter} for processing requests
* through annotated controller methods. Consider overriding one of these
* other more fine-grained methods:
* <ul>
* <li>{@link #addArgumentResolvers} for adding custom argument resolvers.
* <li>{@link #addReturnValueHandlers} for adding custom return value handlers.
* <li>{@link #configureMessageConverters} for adding custom message converters.
* </ul>
*/
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(contentNegotiationManager);
adapter.setMessageConverters(getMessageConverters());
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
adapter.setCustomArgumentResolvers(getArgumentResolvers());
adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
if (jackson2Present) {
adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
}
AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
configureAsyncSupport(configurer);
if (configurer.getTaskExecutor() != null) {
adapter.setTaskExecutor(configurer.getTaskExecutor());
}
if (configurer.getTimeout() != null) {
adapter.setAsyncRequestTimeout(configurer.getTimeout());
}
adapter.setCallableInterceptors(configurer.getCallableInterceptors());
adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
return adapter;
}
/**
* Protected method for plugging in a custom subclass of
* {@link RequestMappingHandlerAdapter}.
* @since 4.3
*/
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
return new RequestMappingHandlerAdapter();
}
/**
* Returns a {@link HandlerExceptionResolverComposite} containing a list of exception
* resolvers obtained either through {@link #configureHandlerExceptionResolvers} or
* through {@link #addDefaultHandlerExceptionResolvers}.
* <p><strong>Note:</strong> This method cannot be made final due to CGLIB constraints.
* Rather than overriding it, consider overriding {@link #configureHandlerExceptionResolvers}
* which allows for providing a list of resolvers.
*/
@Bean
public HandlerExceptionResolver handlerExceptionResolver(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
configureHandlerExceptionResolvers(exceptionResolvers);
if (exceptionResolvers.isEmpty()) {
addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
}
extendHandlerExceptionResolvers(exceptionResolvers);
HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
composite.setOrder(0);
composite.setExceptionResolvers(exceptionResolvers);
return composite;
}
/**
* A method available to subclasses for adding default
* {@link HandlerExceptionResolver HandlerExceptionResolvers}.
* <p>Adds the following exception resolvers:
* <ul>
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
* {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with
* {@link org.springframework.web.bind.annotation.ResponseStatus}.
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring exception types
* </ul>
*/
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,
ContentNegotiationManager mvcContentNegotiationManager) {
ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);
exceptionHandlerResolver.setMessageConverters(getMessageConverters());
exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
if (jackson2Present) {
exceptionHandlerResolver.setResponseBodyAdvice(
Collections.singletonList(new JsonViewResponseBodyAdvice()));
}
if (this.applicationContext != null) {
exceptionHandlerResolver.setApplicationContext(this.applicationContext);
}
exceptionHandlerResolver.afterPropertiesSet();
exceptionResolvers.add(exceptionHandlerResolver);
ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
responseStatusResolver.setMessageSource(this.applicationContext);
exceptionResolvers.add(responseStatusResolver);
exceptionResolvers.add(new DefaultHandlerExceptionResolver());
}
/**
* Protected method for plugging in a custom subclass of
* {@link ExceptionHandlerExceptionResolver}.
* @since 4.3
*/
protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() {
return new ExceptionHandlerExceptionResolver();
}
}
3. DispatcherServletAutoCfg
/**
* {@link EnableAutoConfiguration Auto-configuration} for the Spring
* {@link DispatcherServlet}. Should work for a standalone application where an embedded
* web server is already present and also for a deployable application using
* {@link SpringBootServletInitializer}.
*
* @author Phillip Webb
* @author Dave Syer
* @author Stephane Nicoll
* @author Brian Clozel
* @since 2.0.0
*/
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
/*
* The bean name for a DispatcherServlet that will be mapped to the root URL "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/*
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
}
}
4. BeanWrapper
@Test
public void beanWrapperImpl() {
Ooxx build = Ooxx.builder()
.id(1).build();
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(build);
// 1. 一个个setter()
beanWrapper.setPropertyValue("id", 2);
beanWrapper.setPropertyValue("name", 2);
// 2. map_setter()
Map<String, Integer> map = Map.of("id", 2, "name", 2);
beanWrapper.setPropertyValues(map);
// 3. PV_setter()
PropertyValue pv1 = new PropertyValue("id", 2);
PropertyValue pv2 = new PropertyValue("name", 2);
beanWrapper.setPropertyValue(pv1);
beanWrapper.setPropertyValue(pv2);
// 4. list_setter()
List<PropertyValue> pvList = List.of(pv1, pv2);
MutablePropertyValues pvs = new MutablePropertyValues(pvList);
beanWrapper.setPropertyValues(pvs);
System.out.println("build = " + build);
}
public class TestBeanWrapper {
public static void main(String[] args) {
User user = new User();
// BeanWrapper是Spring中的核心接口,是Spring中的一个包装类
// 具有单独或批量获取、设置属性值,获取属性描述符以及查询属性可读可写的能力,还可以完成类型的转换
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(user);
beanWrapper.setPropertyValue("name", "ooxx");
System.out.println("user.getName() = " + user.getName());
PropertyValue value = new PropertyValue("name", "xxoo");
beanWrapper.setPropertyValue(value);
System.out.println("user.getName() = " + user.getName());
}
}
class User {
private String name;
public String getName() {
return name;
}
// 必须有setter()
public void setName(String name) {
this.name = name;
}
}