入口分析
tomcat启动的时候会加载web.xml,对于spring项目来说一个简单的web.xml通常为如下格式:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- Spring mvc分发servlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 欢迎页,默认进入index.jsp -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
文件中有一个Servlet,所以Servlet 即为程序的入口。
DispatcherServlet 分析
通过下图可以看出DispatcherServlet 继承了 FrameworkServlet,HttpServletBean,HttpServlet,GenericServlet
DispatcherServlet继承实现关系.png DispatcherServlet Diagrams.png
我们思考下一个Servlet是如何加载的?
Servlet Structure.png调用顺序 init() ---> service() ---> destory() , 由于DispatcherServlet方法是Servlet的实现类,所以其必然有对应的init方法,但是经过分析发现DispatcherServlet方法中以及其父类FrameworkServlet方法中都没有init方法,直在HttpServletBean中才发现init方法。代码如下:
/**
* 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 {
// Set bean properties from init parameters.
// 第一步:根据bean属性设置,设置初始化参数。getServletConfig为Tomcat帮我们实现,主要是Servlet的信息,如Servlet名字等,requiredProperties为new HashSet表示必然要出现的属性
// 这个步骤的主要目的是为了获取设置的init-param中的name 和 value, 并将其狗造成PropertyValue 放至MutablePropertyValues 的成员变量propertyValueList 中
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 第二步:创建一个BeanWrapper对象,里面包装了一个DispatcherServlet对象
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
// 第三步:获取资源加载对象
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
// 第四步:给bw注册一个客户自定义的属性编辑器,主要是做类型转换
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
// 第五步:initBeanWrapper,spring 默认没有实现
initBeanWrapper(bw);
// 第六步:为bean设置属性值,底层是利用的反射
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
// 第七步:调用子类去实现spring 容器初始化(关键步骤!)
initServletBean();
}
我们进行逐步分析:
第一步:根据bean属性设置,设置初始化参数。getServletConfig为Tomcat帮我们实现,主要是Servlet的信息,如Servlet名字等,requiredProperties为new HashSet
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
/**
* Create new ServletConfigPropertyValues.
* 创建一个新的ServletConfig属性值
* @param config the ServletConfig we'll use to take PropertyValues from
* @param requiredProperties set of property names we need, where
* we can't accept default values
* @throws ServletException if any required properties are missing
*/
public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
throws ServletException {
Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?
new HashSet<>(requiredProperties) : null);
// 获取到配置的初始化参数
/*
config 可以理解为,web.xml中的配置信息
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
此处就为获取到 <init-param> 下的信息 为tomcat实现,我们暂时不做分析。
*/
Enumeration<String> paramNames = config.getInitParameterNames();
while (paramNames.hasMoreElements()) {
// 获取参数名字
String property = paramNames.nextElement();
// 获取参数value
Object value = config.getInitParameter(property);
// 将name和value构成PropertyValue,放至MutablePropertyValues 的成员变量propertyValueList 中,但是并未使用其返回值
addPropertyValue(new PropertyValue(property, value));
if (missingProps != null) {
missingProps.remove(property);
}
}
// Fail if we are still missing properties.
// 如果丢失了必然出现的属性则抛出异常。
if (!CollectionUtils.isEmpty(missingProps)) {
throw new ServletException(
"Initialization from ServletConfig for servlet '" + config.getServletName() +
"' failed; the following required properties were missing: " +
StringUtils.collectionToDelimitedString(missingProps, ", "));
}
}
第二步:创建一个BeanWrapper对象,里面包装了一个DispatcherServlet对象
第三步:获取资源加载对象
第四步:给bw注册一个客户自定义的属性编辑器,主要是做类型转换
第五步:initBeanWrapper,spring 默认没有实现
第六步:为bean设置属性值,底层是利用的反射
第七步:调用子类去实现spring 容器初始化(关键步骤!)
网友评论