Commit 2574ac2c by SunWei峰

初识Spring MVC

parent 8b079d43
# Spring源码分析
***
> 说明: 个人学习 Spring 源码,仅供自己学习使用,其中不以任何方式谋取利益,
> 有需要的同学们可以自行下载。
> 学习过程可能引用到某些博客中的信息,书籍中的信息。
> 如果涉嫌侵权,请联系本人立即删除。
> 说明: <br>
> 个人学习 Spring 源码,仅供自己学习使用,其中不以任何方式谋取利益。<br>
> 未对源码做任何修改,只是在学习时加入自己理解的注解(部分参照网络博客)。<br>
> 有需要的同学们可以自行拉取。<br>
> 学习过程可能引用到某些博客中的信息,书籍中的信息。<br>
> 如果涉嫌侵权,请联系本人立即删除。<br>
> 如有需要某些资源(不违反任何规定情况下)可联系我给大家分享。<br>
`联系邮箱``osfung@163.com`
......@@ -12,6 +14,8 @@
源码获取方式:github 官方获取
源码地址:https://github.com/spring-projects/spring-framework
Spring版本:6.0.0
Java版本:JDK17
\ No newline at end of file
Java版本:JDK 17
\ No newline at end of file
......@@ -257,6 +257,8 @@ public class ContextLoader {
* @see #CONFIG_LOCATION_PARAM
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// 初始化完成的 webApplicationContext 会被保存到 servletContext 的属性中,key 为 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
// 所以这里是判断是否已经初始化了webApplicationContext,就抛出异常(web.xml 中声明了多次ContextLoader 的定义),不可重复初始化。
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 - " +
......@@ -289,6 +291,7 @@ public class ContextLoader {
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
// 映射当前的类加载器 与 创建的实例到全局变量 currentContextPerThread 中。
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
......@@ -341,6 +344,7 @@ public class ContextLoader {
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
protected Class<?> determineContextClass(ServletContext servletContext) {
// 获取 contextClassName。第一次初始化自然是没有缓存的
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
......@@ -352,6 +356,8 @@ public class ContextLoader {
}
}
else {
// 默认策略获取 contextClassName。通过上面的静态代码块我们可以知道defaultStrategies 读取的是 ContextLoader.properties 文件中的内容
// 默认是 XmlWebApplicationContext
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
......
......@@ -94,6 +94,8 @@ public class ContextLoaderListener extends ContextLoader implements ServletConte
}
// 容器启动时会调用
// TODO: 2022/10/5 contextInitialized 在这儿
/**
* Initialize the root web application context.
*/
......
......@@ -160,6 +160,7 @@ import org.springframework.web.util.WebUtils;
* @see org.springframework.web.servlet.mvc.Controller
* @see org.springframework.web.context.ContextLoaderListener
*/
// DispatcherServlet 在这儿,init() 方法在父类 HttpServletBean 中
@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {
......@@ -492,14 +493,23 @@ public class DispatcherServlet extends FrameworkServlet {
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
// 初始化多文件上传解析器
initMultipartResolver(context);
// 初始化国际化解析器
initLocaleResolver(context);
// 初始化主题解析器
initThemeResolver(context);
// 初始化 HandlerMapping
initHandlerMappings(context);
// 初始化 HandlerAdapter
initHandlerAdapters(context);
// 初始化 Handler异常解析器
initHandlerExceptionResolvers(context);
// 初始化RequestToViewNameTranslator
initRequestToViewNameTranslator(context);
// 初始化 视图解析器
initViewResolvers(context);
// 初始化 FlashMapManager
initFlashMapManager(context);
}
......@@ -509,6 +519,10 @@ public class DispatcherServlet extends FrameworkServlet {
* no multipart handling is provided.
*/
private void initMultipartResolver(ApplicationContext context) {
// 在 Spring 中,MultipartResolver 主要用来处理文件上传。默认情况下,Spring是没有 Multipart 处理的。
// 如果想使用 Spring 的 Multipart,则需要手动注入 Multipart 解析器,即MultipartResolver。
// 这样Spring 会检查每个请求是否包含 Multipart,如果包含,那么 MultipartResolver 就会解析它。
// 一般我们可以注入 CommonsMultipartResolver。
try {
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isTraceEnabled()) {
......@@ -533,6 +547,7 @@ public class DispatcherServlet extends FrameworkServlet {
* we default to AcceptHeaderLocaleResolver.
*/
private void initLocaleResolver(ApplicationContext context) {
// 一共有三种实现类,一种使用 URL 后缀类型,一种基于 cookie 实现,一种基于 session 来实现
try {
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isTraceEnabled()) {
......@@ -558,6 +573,7 @@ public class DispatcherServlet extends FrameworkServlet {
* we default to a FixedThemeResolver.
*/
private void initThemeResolver(ApplicationContext context) {
// 通过主题来改变用户体验,三种实现类:固定主题、主题保存在 session 中、主题保存在 cookie 中。
try {
this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
if (logger.isTraceEnabled()) {
......@@ -583,19 +599,24 @@ public class DispatcherServlet extends FrameworkServlet {
* we default to BeanNameUrlHandlerMapping.
*/
private void initHandlerMappings(ApplicationContext context) {
// 映射器可以有多个,在选用时会进行优先级排序,依次调用映射器链,找到就会直接返回处理器路径。
this.handlerMappings = null;
// 如果启用所有的HandlerMapping。可以通过这个参数来控制是使用指定的HandlerMapping,还是检索所有的
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 寻找所有的 HandlerMapping 类型的类
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
// 按照优先级进行排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
// 如果使用指定的参数,从容器中获取 beanName 为 "handlerMapping" 的 HandlerMapping
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
......@@ -607,6 +628,7 @@ public class DispatcherServlet extends FrameworkServlet {
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
// 如果 handlerMappings 为null。则使用默认策略指定的HandlerMapping
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
......@@ -870,8 +892,11 @@ public class DispatcherServlet extends FrameworkServlet {
}
}
// 获取Name
String key = strategyInterface.getName();
// 从配置文件中获取 value
String value = defaultStrategies.getProperty(key);
// 获取到 value 之后就是对 value 的处理,添加返回。
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
......
......@@ -528,7 +528,9 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
long startTime = System.currentTimeMillis();
try {
// 委托给 initWebApplicationContext 方法来完成进一步的初始化
this.webApplicationContext = initWebApplicationContext();
// 留给子类覆盖操作
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
......@@ -563,6 +565,7 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// 1. 如果 webApplicationContext 在构造函数的时候被注入,则 wac != null, 则可以直接使用
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
......@@ -574,9 +577,11 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
// 2.刷新上下文环境
configureAndRefreshWebApplicationContext(cwac);
}
}
// 3. 如果构造函数并没有注入,则wac为null,根据 contextAttribute 属性加载 WebApplicationContext
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
......@@ -584,8 +589,10 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
// 4. 如果上面两个方式都没有加载到 WebApplicationContext,则尝试自己加载
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
// 自己尝试创建WebApplicationContext
wac = createWebApplicationContext(rootContext);
}
......@@ -594,6 +601,7 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
// 5.刷新
onRefresh(wac);
}
}
......@@ -659,15 +667,18 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
// 获取web.xml 配置的 DispatcherServlet init-params contextConfigLocation 属性
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
// 配置 WebApplicationContext 实例并刷新
configureAndRefreshWebApplicationContext(wac);
return wac;
}
// 已经创建的 WebApplicationContext 实例进行配置及刷新。其实就是配置并刷新Spring。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
......@@ -682,6 +693,7 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
}
}
// 设置一些属性,ServletContext、ServletConfig等
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
......
......@@ -148,12 +148,16 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
public final void init() throws ServletException {
// Set bean properties from init parameters.
// 1. 封装及验证初始化参数
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 2. 将当前 Servlet 实例转化为 BeanWrapper 实例
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
// 3. 注册于相对于 Resource 的属性编辑器
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
// 4. 属性注入(支持 Spring 的自动注入)
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
......@@ -166,6 +170,7 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
}
// Let subclasses do whatever initialization they like.
// 5. servletBean的初始化
initServletBean();
}
......@@ -220,10 +225,12 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?
new HashSet<>(requiredProperties) : null);
// 获取 web.xml 中 DispatcherServlet 配置的 init-params 属性内容
Enumeration<String> paramNames = config.getInitParameterNames();
while (paramNames.hasMoreElements()) {
String property = paramNames.nextElement();
Object value = config.getInitParameter(property);
// 保存 init-params 属性
addPropertyValue(new PropertyValue(property, value));
if (missingProps != null) {
missingProps.remove(property);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment