戳上方蓝字 “程序猿杂货铺” 关注我 并 置顶星标!
你的关注意义重大!
阅读本文大概需要 15 分钟
本文首发地址 | http://t.cn/E9a8mIx
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。
Servlet生命周期客户端请求该 Servlet;
加载 Servlet 类到内存;
实例化并调用init()方法初始化该 Servlet;
service()(根据请求方法不同调用 doGet() 或者 doPost(),此外还有 doHead()、doPut()、doTrace()、doDelete()、doOptions()、destroy())。
加载和实例化 Servlet。这项操作一般是动态执行的。然而,Server 通常会提供一个管理的选项,用于在 Server 启动时强制装载和初始化特定的 Servlet。
Servlet默认实现
Servlet 容器默认是采用单实例多线程的方式处理多个请求的:
当 web 服务器启动的时候(或客户端发送请求到服务器时),Servlet 就被加载并实例化(只存在一个Servlet实例);
容器初始化化 Servlet 主要就是读取配置文件(例如 tomcat ,可以通过 servlet.xml 的设置线程池中线程数目,初始化线程池通过web.xml,初始化每个参数值等等。
当请求到达时,Servlet 容器通过调度线程(Dispatchaer Thread) 调度它管理下线程池中等待执行的线程(Worker Thread)给请求者;
线程执行 Servlet 的 service 方法;
请求结束,放回线程池,等待被调用;
分析结果
Servlet 单实例,减少了产生servlet的开销;
通过线程池来响应多个请求,提高了请求的响应时间;
Servlet 容器并不关心到达的 Servlet 请求访问的是否是同一个 Servlet 还是另一个 Servlet,直接分配给它一个新的线程;如果是同一个 Servlet 的多个请求,那么 Servlet 的 service 方法将在多线程中并发的执行;
每一个请求由 ServletRequest 对象来接受请求,由 ServletResponse 对象来响应该请求;
加载流程
本次分享采用的是 Spring Boot 2.0.2.RELEASE 内嵌 Tomcat 实现的方式
Spring Boot启动加载流程
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();// 开始启动stopWatch.start();ConfigurableApplicationContext context = null;// 添加失败分析器Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();// 属性配置configureHeadlessProperty();// 添加监听,包括(Spring监听和Spring Boot监听)SpringApplicationRunListeners listeners = getRunListeners(args);// 启动监听listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 准备应用环境参数ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);// 创建容器context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);// 参数准备,监听器添加prepareContext(context, environment, listeners, applicationArguments,printedBanner);// 刷新容器refreshContext(context);// 容器启动完成后的处理afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// 开启监听listeners.started(context);// 开始执行callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {// 执行监听listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;}
如何启动 Tomcat
public void refresh() throws BeansException, IllegalStateException {// 加锁实容器启动时的线程安全synchronized (this.startupShutdownMonitor) {// 为容器刷新做准备prepareRefresh();// 告诉子类启动refreshFactory,Bean定义源文件载入从子类的refreshBeanFactory中加载ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 为BeanFactory配置容器特性,如:类加载器,事件处理器prepareBeanFactory(beanFactory);try {// 为容器的某些子类指定特殊的BeanPost事件处理postProcessBeanFactory(beanFactory);// 调用所有注册BeanFactoryPostProcessor的BeaninvokeBeanFactoryPostProcessors(beanFactory);// 为BeanFactory注册BeanPost事件处理器registerBeanPostProcessors(beanFactory);// 初始化信息源,和国际化相关initMessageSource();// 初始化容器事件传播器initApplicationEventMulticaster();// 调用子类的某些特殊Bean初始化方法onRefresh();// 为时间传播器注册事件监听器registerListeners();// 初始化所有所有剩余的单例BeanfinishBeanFactoryInitialization(beanFactory);// 初始化容器的生命周期事件处理器,并发布容器的生命周期事件finishRefresh();}catch (BeansException ex) {// 销毁以前创建的单态BeandestroyBeans();// 取消refresh操作,重置容器的同步标识cancelRefresh(ex);// 异常处理throw ex;}finally {// 重置resetCommonCaches();}}}
在 finishRefresh()中启动 web服务器
内嵌的Tomcat如何创建Servlet
protected void finishRefresh() {super.finishRefresh();// 启动web服务器WebServer webServer = startWebServer();if (webServer != null) {publishEvent(new ServletWebServerInitializedEvent(webServer, this));}}
具体实现
private WebServer startWebServer() {WebServer webServer = this.webServer;if (webServer != null) {webServer.start();}return webServer;}
startWebServer

开始启动 Tomcat
public void start() throws WebServerException {// 此处实现了线程安全synchronized (this.monitor) {if (this.started) {return;}try {addPreviouslyRemovedConnectors();Connector connector = this.tomcat.getConnector();if (connector != null && this.autoStart) {// 当连接建立,getPort() >= 0;时启动TomcatperformDeferredLoadOnStartup();}
加载并且初始化所有的 servlets(被 load on startup 标记的)
public boolean loadOnStartup(Container children[]) {for (ArrayList<Wrapper> list : map.values()) {for (Wrapper wrapper : list) {try {wrapper.load();...}
初始化应用中已经描述,但没有被初始化的 servlet
public synchronized void load() throws ServletException {instance = loadServlet();// 如果没有被初始化,则初始化servletif (!instanceInitialized) {// 此处可以初始化 DefaultServlet 和 HttpServletBean,详见下文initServlet(instance);}}
public synchronized Servlet loadServlet() throws ServletException {try {servlet = (Servlet) instanceManager.newInstance(servletClass);...// 初始化servletinitServlet(servlet);//发送加载完成事件fireContainerEvent("load", this);}
private synchronized void initServlet(Servlet servlet)throws ServletException {// 如果已经加载成功或者是实现了SingleThreadModel,直接返回if (instanceInitialized && !singleThreadModel) return;// 开始初始化servlet.init(facade);}
初始化 HttpServletBean 及相关组件
public final void init() throws ServletException {// 让子类实现initServletBean();}
protected final void initServletBean() throws ServletException {try {// 初始化WebApplicationContext,此处采用事件监听实现this.webApplicationContext = initWebApplicationContext();initFrameworkServlet();}}
protected WebApplicationContext initWebApplicationContext() {WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (!this.refreshEventReceived) {// refresh刷新容器onRefresh(wac);}if (this.publishContext) {//发布容器作为servlet的attributeString attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;}
DispatcherServlet 中 onRefresh 方法执行
protected void onRefresh(ApplicationContext context) {initStrategies(context);}
初始化 DispatcherServlet 相关组件
protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);}
至此 servlet 相关初始化工作已经完成
Servlet使用HttpServlet 对 service 的实现
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String method = req.getMethod();long lastModified;if (method.equals("GET")) {lastModified = this.getLastModified(req);if (lastModified == -1L) {this.doGet(req, resp);} else {long ifModifiedSince;try {ifModifiedSince = req.getDateHeader("If-Modified-Since");} catch (IllegalArgumentException var9) {ifModifiedSince = -1L;}if (ifModifiedSince < lastModified / 1000L * 1000L) {this.maybeSetLastModified(resp, lastModified);this.doGet(req, resp);} else {resp.setStatus(304);}}} else if (method.equals("HEAD")) {lastModified = this.getLastModified(req);this.maybeSetLastModified(resp, lastModified);this.doHead(req, resp);} else if (method.equals("POST")) {this.doPost(req, resp);} else if (method.equals("PUT")) {this.doPut(req, resp);} else if (method.equals("DELETE")) {this.doDelete(req, resp);} else if (method.equals("OPTIONS")) {this.doOptions(req, resp);} else if (method.equals("TRACE")) {this.doTrace(req, resp);} else {String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[]{method};errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(501, errMsg);}}
处理请求并发布事件(不考虑结果)
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());initContextHolders(request, localeContext, requestAttributes);try {// 执行 DispatcherServlet 的 doService方法doService(request, response);}publishRequestHandledEvent(request, response, startTime, failureCause);}
执行 DispatcherServlet 的 doDispatch 方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;//在Spring3.2之后,支持WebAsync异步处理WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {//转换请求为多个部分并获得相应的处理器processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 找到当前请求对应的handlermappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 找到当前请求对应的handler适配器HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//执行拦截器preHandleif (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 反射调用实际执行的handler,返回ModelAndViewmv = ha.handle(processedRequest, response, mappedHandler.getHandler());applyDefaultViewName(processedRequest, mv);//执行拦截器postHandlemappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {//此处主要是使@ExceptionHandler处理起作用dispatchException = new NestedServletException("Handler dispatch failed", err);}//执行拦截器processDispatchResult,当程序正常结束时执行processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// 相当于postHandle 和 afterCompletionif (mappedHandler != null) {//该实现在HandlerInterceptorAdapter中也有定义mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.//清除所有请求的处理所对应的资源占用if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}
至此 servlet 初始化和使用的所有的代码已经完成
服务停止// 销毁资源并关闭服务public void destroy() {close();}
public void close() {synchronized (this.startupShutdownMonitor) {doClose();// 注册 shutdownHookif (this.shutdownHook != null) {try {Runtime.getRuntime().removeShutdownHook(this.shutdownHook);...}
实际执行销毁动作
protected void doClose() {// 发布 shutdown 事件.publishEvent(new ContextClosedEvent(this));// 销毁所有的单例BeandestroyBeans();// 关闭容器closeBeanFactory();// 让子类执行清除onClose();}
如何创建单例多线程Servlet
在 Web 应用程序中,一个 Servlet 在一个时刻可能被多个用户同时访问。这时 Web 容器将为每个用户创建一个线程来执行 Servlet。如果 Servlet 不涉及共享资源的问题,不必关心多线程问题。但如果 Servlet 需要共享资源,需要保证 Servlet 是线程安全的。
下面是编写线程安全的 Servlet 的一些建议:
用方法的局部变量保存请求中的专有数据。对方法中定义的局部变量,进入方法的每个线程都有自己的一份方法变量拷贝。任何线程都不会修改其他线程的局部变量。如果要在不同的请求之间共享数据,应该使用会话来共享这类数据。
只用 Servlet的成员变量来存放那些不会改变的数据。有些数据在 Servlet 生命周期中不发生任何变化,通常是在初始时确定的,这些数据可以使用成员变量保存,如数据库连接名称、其他资源的路径等。
对可能被请求修改的成员变量同步。有时数据成员变量或者环境属性可能被请求修改。当访问这些数据时应该对它们同步,以避免多个线程同时修改这些数据。
如果 Servlet 访问外部资源,那么需要同步访问这些资源。例如,假设 Servlet 要从文件中读写数据。当一个线程读写一个文件时,其他线程也可能正在读写这个文件。文件访问本身不是线程安全的,所以必须编写同步访问这些资源的代码。在编写线程安全的 Servlet 时,
下面两种方法是不应该使用的:
在 Servlet API 中提供了一个 SingleThreadModel 接口,实现这个接口的 Servlet 在被多个客户请求时一个时刻只有一个线程运行。这个接口已被标记不推荐使用。
对 doGet() 或doPost() 方法同步。如果必须在 Servlet 中使用同步代码,应尽量在最小的代码块范围上进行同步。同步代码越小,Servlet 执行得才越好。)
送书活动马上开奖了,未参加的小伙伴抓紧了(点击文字直接跳转
)
往期精彩回顾


3990

被折叠的 条评论
为什么被折叠?



