什么是Servlet?

Servlet实际是Server+Applet的缩写,表示一个服务器应用。Servlet是JavaEE规范的一部分。

在Servlet3.1中,它的结构图如下:
18mZLV.png

Servlet接口定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface Servlet{
/*这个方法在容器启动的时候被容器调用,
但是当load-on-startup设置为负数或者不设置的时候,
是在Servlet第一次被用到的时候调用*/
public void init(ServletConfig config) throws nServletException;

/*用于获取ServletConfig*/
public ServletConfig getServletConfig();
/*用于处理具体的请求*/
public void service(ServletRequest req,ServletResponse res) throws ServletException,IOException;
/*获取Servlet的相关信息,如作者等,默认实现返回空串*/
public String getServletInfo();

/*在Servlet销毁的时候(关闭服务器)调用,用于释放资源,只会调用一次*/
public void destroy();
}

init(ServletConfig config)调用的时候,需要一个ServletConfig对象,这个对象中存放了初始化Servlet的信息,其中一部分就来自于我们的配置文件中的<init-param>
标签下的内容。
18gtns.png

ServletConfig接口的定义如下:

1
2
3
4
5
6
7
8
9
10
11
public interface ServletConfig{
/*返回的就是我们配置的servlet-name*/
public String getServletName();
/*这个地方返回的ServletContext代表的就是我们这个应用本身*/
public ServletContext getServletContext();

/*用于获取init-param配置的参数*/
public String getInitParameter(String name);
public Enumeration<String> getInitParameterNames();

}

Tomcat的顶层结构

Tomcat最顶层的是Server,代表整个服务器,Server中至少包含一个Service,用于提供具体的服务。一个Service主要包含两个部分,分别是Connector和Container。 Connector用于处理连接相关的事情,并提供Socket与request、response的转化,Container用于封装和管理Servlet,以及剧吐处理request请求。一个Service中可以有多个connectors,但是只有一个Container。
18hYTA.png

Tomcat中的server由org.apache.catalina.startup.Catalina来管理,它是整个Tomcat的管理类,它里面包含了load,start,stop方法,分别用来管理整个服务器的生命周期。load方法会根据conf/server.xml创建Server并调用Server的init方法进行初始化,start方法用于启动服务器,stop方法用于停止服务器。
在启动的时候会逐层调用这些方法。
Tomcat虽然由Catalina管理,但是Tomcat的main却在org.qpache.catalina.startup.Bootstrap中,Bootstrap的作用类似一个CatalinaAdaptor,具体的处理过程还是使用Catalina来进行的,这样将启动类和管理类分开,可以更加方便地拓展启动方式。

Bootstrap的启动过程

启动tomcat首先就是调用Bootstrap的main方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public static void main(String args[]) {

synchronized (daemonLock) {
if (daemon == null) {
//初始化一个启动器
Bootstrap bootstrap = new Bootstrap();
try {
//创建了catalina实例,并且赋值给了catalinaDaemon变量
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to
// prevent a range of class not found exceptions.
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
}

//处理main方法传入的命令
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}

if (command.equals("startd")) {
args[args.length - 1] = "start";

daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) {
//处理启动命令
//这里的三个方法都调用了Catalina的相关方法
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null == daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
// Unwrap the Exception for clearer error reporting
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}

Catalina的启动过程

通过对Bootstrap启动类的分析,我们可以知道,启动Catalina主要是通过以下三个方法:

1
2
3
4
5
6
//设置Server启动完成后是否立即进入等待状态的标志
daemon.setAwait(true);
//加载配置文件,创建并初始化Server
daemon.load(args);
//启动服务器
daemon.start();

在Bootstrap中这三个方法的实现其实都是利用反射调用了Catalina中的相应的方法,我们直接查看Catalina中的实现。
下面我们依次看这三个方法的具体实现:

1
2
3
4
public void setAwait(boolean b) {
//这个方法仅仅是设置了一个标志位,表示启动后是否立即进入等待状态
await = b;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public void load() {

if (loaded) {
//如果已经load过了,就返回
return;
}
//设置标志位,表示已经加载过了
loaded = true;
//用于记录启动耗时
long t1 = System.nanoTime();

//下面的代码都是用来创建Server的
initDirs();

// Before digester - it may be needed
initNaming();

//使用Digester解析conf/server.xml文件创建了Server对象,
//并赋值给了Server属性
// Set configuration source
ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
File file = configFile();

// Create and execute our Digester
Digester digester = createStartDigester();

try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
InputStream inputStream = resource.getInputStream();
InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
} catch (Exception e) {
log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
if (file.exists() && !file.canRead()) {
log.warn(sm.getString("catalina.incorrectPermissions"));
}
return;
}

getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

// Stream redirection
initStreams();

// Start the new server
try {
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error(sm.getString("catalina.initError"), e);
}
}

long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info(sm.getString("catalina.init", Long.valueOf((t2 - t1) / 1000000)));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public void start() {

if (getServer() == null) {
load();
}

if (getServer() == null) {
log.fatal(sm.getString("catalina.noServer"));
return;
}

long t1 = System.nanoTime();

// Start the new server
try {
//调用Server的start方法,启动服务器
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}

long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info(sm.getString("catalina.startup", Long.valueOf((t2 - t1) / 1000000)));
}

// 注册关闭钩爪代码
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);

// If JULI is being used, disable JULI's shutdown hook since
// shutdown hooks run in parallel and log messages may be lost
// if JULI's hook completes before the CatalinaShutdownHook()
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}
//更加await的值确定是否进入等待状态
if (await) {
await();
stop();
}
}

整个Server的启动过程如下,首先设置启动后是否进入等待的标志位,然后调用load方法来加载配置文件,创建Server对象,最后调用server对象的start方法来启动服务器,最后再注册服务器关闭的钩爪函数,根据之前设置的标志位决定是否进入等待状态。

Server的启动过程

Server接口中提供了addServer(Server service)removeService(Service service)来添加和删除Service。Server的init方法和start方法,分配循环调用了每个Service的init方法和start方法以此来启动所有的Service。

Server的默认实现是org/apache/catalina/core/StandardServer.java
class StandardServer extends LifecycleMBeanBase implements Server

Server继承了LifecycleBeanBase,init和start方法就是在LifecycleBeanBase的父类LifecycleBase中定义的。
StandardServer中的initInternalstartInternal方法就是Tomcate生命周期的管理方式。
这两个方法的内部主要就是向下面一样,依次调用所有的Service的相应的方法。

1
2
3
4
5
6
7
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}


Service的启动过程

Service的默认实现是org.apache.catalina.core.StandardService,
class StandardService extends LifecycleMBeanBase implements Service
它同样也是继承自LifecycleMBeanBase类的,所以init和start方法最终就会调用initInternalstartInternal方法。
这两个方法的具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
protected void startInternal() throws LifecycleException {

if(log.isInfoEnabled())
log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING);

// Start our defined Container first
if (engine != null) {
synchronized (engine) {
//调用engine的start方法
engine.start();
}
}

synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}

mapperListener.start();

// Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
}
}
}

startInternal为例,startInternalinitInternal方法内部主要是调用container,executors,mapperListener,connectors的init和start方法。其中executors的作用主要是管理线程池。

整个Tomcat服务器的启动流程图如下:
1Gha80.png

Tomcat的生命周期管理

Tomcat的生命周期管理是由org.apache.catalina.lifecycle接口来定义的。
该接口主要完成3件事情:

  1. 定义了13个string类型的常量,用于LifecycleEvent事件的type属性,作用是区分组件发出的LifecycleEvent事件的状态。
  2. 定义了3个管理监听器的方法,addLifecycleListener,findLifecycleListeners和removeLifecycleListener.
  3. 定义了4个生命周期方法,init,start,stop,destory
  4. 定义了获取当前状态的两个方法getState和getStateName

LifecycleBase是LifecycleBase接口的默认实现。而监听器的管理是由LifecycleSupport类来完成的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public final synchronized void start() throws LifecycleException {

if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {

if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}

return;
}

//调整状态
if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}

try {

setStateInternal(LifecycleState.STARTING_PREP, null, false);
//调用相应的模板方法
startInternal();
if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}

这个方法的实现时,首先会判断当前状态和要处理的方法是否匹配,如果不匹配会执行相应的方法使其匹配,然后再调用相应的模板方法并设置状态。

Container分析

Container时tomcat容器的接口。Container一共有4个子接口Engine、Host、Context、Wrapper和一个默认实现类ContainerBase.每个子接口都是一个容器。

1GXJVx.png

Container的子容器Engine,Host,Context,Wrapper是逐层包含的关系。
1GXwxH.png

  • Engine:引擎,用于管理多个站点,一个Service最多只能有一个Engine。
  • Host:代表一个站点,也称为虚拟主机,通过配置host就可以添加站点。
  • Context:代表一个应用程序,对应着平时开发的一个程序,或WEB-INF下的一个web.xml文件
  • Wrapper:每个Wrapper封装着一个Servlet。

Connector分析

Connector用于接收请求并将请求封装成Request和Response来具体处理,最底层是使用Socket来进行连接的,Request和Response是按照HTTP协议来封装的,所以Connecter同时实现了TCP/IP协议和HTTP协议,Request和Response封装完之后交给Container进行处理。Container就是Servlet的容器,Container处理完之后返回给Connector,最后Connector使用Socker将结果返回给客户端,这样整个请求就处理完成了。
1YM9Zn.png

Connector具体是使用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型。ProtocolHandler里面有3个组件:Endpoint,Processor和Adapter。Endpoint用于处理底层的Socket网络连接,Processor用于将Endpoint接收到的Socket封装成为Request,Adapter负责将请求适配到Servlet容器进行具体的处理。

SpringMVC的启动过程

SpringMVC的结构

1NTGcj.png

spring部分的EnvironmentAware和ApplicationContextAware接口,都是继承自Aware接口的,在spring中XXXAware接口表示对XXX可感知的,继承相应的接口然后实现setXXX方法,就可以拿到相应的对象。比如继承EnvironmentAware接口并实现setEnvironment就可以拿到spring中的Environment对象了,spring会自动注入。

而EnvironmentCapable接口就是告诉spring自己可以提供Environment,实现它其中唯一的一个方法Environment getEnvironment()

而我们分析SpringMVC的启动过程主要就是分析HttpServletBean,FrameworkServlet,DispatcherServlet三个类。

HttpServletBean

通过之前的分析我们知道Servlet的创建首先会调用无参的init方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}

// Set bean properties from init parameters.
try {
//将Servlet中配置的信息封装到pvs变量中
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
//模板方法,可以在子类调用,做一些初始化的工作。
initBeanWrapper(bw);
//将配置的初始化值设置到DispatchcherServlet中
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}

// Let subclasses do whatever initialization they like.
//模板方法,子类初始化的入口方法
initServletBean();

if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}

在HttpServletBean的init方法中,首先将Serlvet中配置的参数(封装到了pvs中)使用BeanWrapper(Spring提供的用来操作javaBean属性的工具,使用它可以直接修改一个对象的属性)设置到DispatcherServle的相关属性,然后调用模板方法initServletBean,子类就通过这个方法初始化。

FrameworkSerlvet

从HttpServletBean的init流程,我们可以知道FrameworkServlet的初始化入口方法应该是initServletBean.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();

try {
//初始化WebApplicationContext
this.webApplicationContext = initWebApplicationContext();
//调用模板方法,初始化FrameworkServlet
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}

if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}

用于初始化WebApplicationContext的initWebApplicationContext();方法的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
protected WebApplicationContext initWebApplicationContext() {
//获取rootContext
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;

//如果已经通过构造方法设置了webApplicationContext
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) {
// 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
//当webApplicationContext已经存在ServletContext中时,
//通过配置在Servlet中的contextAttribute参数获取
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
//如果webApplicationContext还没看有创建,则创建一个
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.
//当contextRefreshedEvent事件没有触发时调用此模板方法
onRefresh(wac);
}

if (this.publishContext) {
// Publish the context as a servlet context attribute.
//将ApplicatinoContext保存到ServletContext中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}

return wac;
}

initWebApplicationContext()方法做了三件事情:

  1. 获取spring的根容器rootContext
  2. 设置webApplicationContext并根据情况调用onRefresh方法
  3. 将webApplicationContext设置到ServletContext中

DispatcherServlet

onRefresh方法时DispatcherServlet的入口方法。OnRefresh中简单的调用了initStrategies,在initStrategies中调用了9个初始化方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}