本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到个人仓库里查看html
https://github.com/h2pl/Java-Tutorial前端
喜欢的话麻烦点下Star哈java
文章首发于个人我的博客:python
www.how2playlife.comandroid
本文是微信公众号【Java技术江湖】的《走进JavaWeb技术世界》其中一篇,本文部份内容来源于网络,为了把本文主题讲得清晰透彻,也整合了不少我认为不错的技术博客内容,引用其中了一些比较好的博客文章,若有侵权,请联系做者。git
该系列博文会告诉你如何从入门到进阶,从servlet到框架,从ssm再到SpringBoot,一步步地学习JavaWeb基础知识,并上手进行实战,接着了解JavaWeb项目中常常要使用的技术和组件,包括日志组件、Maven、Junit,等等内容,以便让你更完整地了解整个Java Web技术体系,造成本身的知识框架。程序员
为了更好地总结和检验你的学习成果,本系列文章也会提供每一个知识点对应的面试题以及参考答案。github
若是对本系列文章有什么建议,或者是有什么疑问的话,也能够关注公众号【Java技术江湖】联系做者,欢迎你参与本系列博文的创做和修订。web
文末赠送8000G的Java架构师学习资料,须要的朋友能够到文末了解领取方式,资料包括Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送做者原创的Java学习指南、Java程序员面试指南等干货资源)
面试
Servlet的做用是为Java程序提供一个统一的web应用的规范,方便程序员统一的使用这种规范来编写程序,应用容器可使用提供的规范来实现本身的特性。好比tomcat的代码和jetty的代码就不同,但做为程序员你只须要了解servlet规范就能够从request中取值,你能够操做session等等。不用在乎应用服务器底层的实现的差异而影响你的开发。
HTTP 协议只是一个规范,定义服务请求和响应的大体式样。Java servlet 类将HTTP中那些低层的结构包装在 Java 类中,这些类所包含的便利方法使其在 Java 语言环境中更易于处理。
正如您正使用的特定 servlet 容器的配置文件中所定义的,当用户经过 URL 发出一个请求时,这些 Java servlet 类就将之转换成一个 HttpServletRequest,并发送给 URL 所指向的目标。当服务器端完成其工做时,Java 运行时环境(Java Runtime Environment)就将结果包装在一个 HttpServletResponse 中,而后将原 HTTP 响应送回给发出该请求的客户机。在与 Web 应用程序进行交互时,一般会发出多个请求并得到多个响应。全部这些都是在一个会话语境中,Java 语言将之包装在一个 HttpSession 对象中。在处理响应时,您能够访问该对象,并在建立响应时向其添加事件。它提供了一些跨请求的语境。
容器(如 Tomcat)将为 servlet 管理运行时环境。您能够配置该容器,定制 J2EE 服务器的工做方式,以便将 servlet 暴露给外部世界。正如咱们将看到的,经过该容器中的各类配置文件,您在 URL(由用户在浏览器中输入)与服务器端组件之间搭建了一座桥梁,这些组件将处理您须要该 URL 转换的请求。在运行应用程序时,该容器将加载并初始化 servlet,管理其生命周期。
Servlet顶级类关联图
Servlet
Servlet的框架是由两个Java包组成的:javax.servlet与javax.servlet.http。在javax.servlet包中定义了全部的Servlet类都必须实现或者扩展的通用接口和类。在javax.servlet.http包中定义了采用Http协议通讯的HttpServlet类。Servlet的框架的核心是javax.servlet.Servlet接口,全部的Servlet都必须实现这个接口。
Servlet接口
在Servlet接口中定义了5个方法:
1\. init(ServletConfig)方法:负责初始化Servlet对象,在Servlet的生命周期中,该方法执行一次;该方法执行在单线程的环境下,所以开发者不用考虑线程安全的问题; 2\. service(ServletRequest req,ServletResponse res)方法:负责响应客户的请求;为了提升效率,Servlet规范要求一个Servlet实例必须可以同时服务于多个客户端请求,即service()方法运行在多线程的环境下,Servlet开发者必须保证该方法的线程安全性; 3\. destroy()方法:当Servlet对象退出生命周期时,负责释放占用的资源; 4\. getServletInfo:就是字面意思,返回Servlet的描述; 5\. getServletConfig:这个方法返回由Servlet容器传给init方法的ServletConfig。
ServletRequest & ServletResponse
对于每个HTTP请求,servlet容器会建立一个封装了HTTP请求的ServletRequest实例传递给servlet的service方法,ServletResponse则表示一个Servlet响应,其隐藏了将响应发给浏览器的复杂性。经过ServletRequest的方法你能够获取一些请求相关的参数,而ServletResponse则能够将设置一些返回参数信息,而且设置返回内容。
ServletConfig
ServletConfig封装能够经过@WebServlet或者web.xml传给一个Servlet的配置信息,以这种方式传递的每一条信息都称作初始化信息,初始化信息就是一个个K-V键值对。为了从一个Servlet内部获取某个初始参数的值,init方法中调用ServletConfig的getinitParameter方法或getinitParameterNames方法获取,除此以外,还能够经过getServletContext获取ServletContext对象。
ServletContext
ServletContext是表明了Servlet应用程序。每一个Web应用程序只有一个context。在分布式环境中,一个应用程序同时部署到多个容器中,而且每台Java虚拟机都有一个ServletContext对象。有了ServletContext对象后,就能够共享能经过应用程序的全部资源访问的信息,促进Web对象的动态注册,共享的信息经过一个内部Map中的对象保存在ServiceContext中来实现。保存在ServletContext中的对象称做属性。操做属性的方法:
GenericServlet
前面编写的Servlet应用中经过实现Servlet接口来编写Servlet,可是咱们每次都必须为Servlet中的全部方法都提供实现,还须要将ServletConfig对象保存到一个类级别的变量中,GenericServlet抽象类就是为了为咱们省略一些模板代码,实现了Servlet和ServletConfig,完成了一下几个工做:
将init方法中的ServletConfig赋给一个类级变量,使的能够经过getServletConfig来获取。
public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); }
同时为避免覆盖init方法后在子类中必须调用super.init(servletConfig),GenericServlet还提供了一个不带参数的init方法,当ServletConfig赋值完成就会被第带参数的init方法调用。这样就能够经过覆盖不带参数的init方法编写初始化代码,而ServletConfig实例依然得以保存
为Servlet接口中的全部方法提供默认实现。
提供方法来包装ServletConfig中的方法。
HTTPServlet
在编写Servlet应用程序时,大多数都要用到HTTP,也就是说能够利用HTTP提供的特性,javax.servlet.http包含了编写Servlet应用程序的类和接口,其中不少覆盖了javax.servlet中的类型,咱们本身在编写应用时大多时候也是继承的HttpServlet。
当Web服务器接收到一个HTTP请求时,它会先判断请求内容——若是是静态网页数据,Web服务器将会自行处理,而后产生响应信息;若是牵涉到动态数据,Web服务器会将请求转交给Servlet容器。此时Servlet容器会找到对应的处理该请求的Servlet实例来处理,结果会送回Web服务器,再由Web服务器传回用户端。
针对同一个Servlet,Servlet容器会在第一次收到http请求时创建一个Servlet实例,而后启动一个线程。第二次收到http请求时,Servlet容器无须创建相同的Servlet实例,而是启动第二个线程来服务客户端请求。因此多线程方式不但能够提升Web应用程序的执行效率,也能够下降Web服务器的系统负担。
Web服务器工做流程
接着咱们描述一下Tomcat与Servlet是如何工做的,首先看下面的时序图:
Servlet工做原理时序图
Web Client 向Servlet容器(Tomcat)发出Http请求;
Servlet容器接收Web Client的请求;
Servlet容器建立一个HttpRequest对象,将Web Client请求的信息封装到这个对象中;
Servlet容器建立一个HttpResponse对象;
Servlet容器调用HttpServlet对象的service方法,把HttpRequest对象与HttpResponse对象做为参数传给 HttpServlet对象;
HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息;
HttpServlet调用HttpResponse对象的有关方法,生成响应数据;
Servlet容器把HttpServlet的响应结果传给Web Client;
在Servlet接口中定义了5个方法,其中3个方法表明了Servlet的生命周期:
1\. init(ServletConfig)方法:负责初始化Servlet对象,在Servlet的生命周期中,该方法执行一次;该方法执行在单线程的环境下,所以开发者不用考虑线程安全的问题; 2\. service(ServletRequest req,ServletResponse res)方法:负责响应客户的请求;为了提升效率,Servlet规范要求一个Servlet实例必须可以同时服务于多个客户端请求,即service()方法运行在多线程的环境下,Servlet开发者必须保证该方法的线程安全性; 3\. destroy()方法:当Servlet对象退出生命周期时,负责释放占用的资源;
编程注意事项说明:
1\. 若是service()方法没有访问Servlet的成员变量也没有访问全局的资源好比静态变量、文件、数据库链接等,而是只使用了当前线程本身的资源,好比非指向全局资源的临时变量、request和response对象等。该方法自己就是线程安全的,没必要进行任何的同步控制。 2\. 若是service()方法访问了Servlet的成员变量,可是对该变量的操做是只读操做,该方法自己就是线程安全的,没必要进行任何的同步控制。 3\. 若是service()方法访问了Servlet的成员变量,而且对该变量的操做既有读又有写,一般须要加上同步控制语句。 4\. 若是service()方法访问了全局的静态变量,若是同一时刻系统中也可能有其它线程访问该静态变量,若是既有读也有写的操做,一般须要加上同步控制语句。 5\. 若是service()方法访问了全局的资源,好比文件、数据库链接等,一般须要加上同步控制语句。
在建立一个 Java servlet 时,通常须要子类 HttpServlet。该类中的方法容许您访问请求和响应包装器(wrapper),您能够用这个包装器来处理请求和建立响应。Servlet的生命周期,简单的归纳这就分为四步:
Servlet类加载--->实例化--->服务--->销毁;
Servlet生命周期
建立Servlet对象的时机:
注意:在web.xml文件中,某些Servlet只有
元素,没有 元素,这样咱们没法经过url的方式访问这些Servlet,这种Servlet一般会在 元素中配置一个 子元素,让容器在启动的时候自动加载这些Servlet并调用init(ServletConfig config)方法来初始化该Servlet。其中方法参数config中包含了Servlet的配置信息,好比初始化参数,该对象由服务器建立。
销毁Servlet对象的时机:
Servlet容器中止或者从新启动:Servlet容器调用Servlet对象的destroy方法来释放资源。以上所讲的就是Servlet对象的生命周期。那么Servlet容器如何知道建立哪个Servlet对象?Servlet对象如何配置?实际上这些信息是经过读取web.xml配置文件来实现的。
<servlet> <!-- Servlet对象的名称 --> <servlet-name>action<servlet-name> <!-- 建立Servlet对象所要调用的类 --> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <!-- 参数名称 --> <param-name>config</param-name> <!-- 参数值 --> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>detail</param-name> <param-value>2</param-value> </init-param> <init-param> <param-name>debug</param-name> <param-value>2</param-value> </init-param> <!-- Servlet容器启动时加载Servlet对象的顺序 --> <load-on-startup>2</load-on-startup> </servlet> <!-- 要与servlet中的servlet-name配置节内容对应 --> <servlet-mapping> <servlet-name>action</servlet-name> <!-- 客户访问的Servlet的相对URL路径 --> <url-pattern>*.do</url-pattern> </servlet-mapping>
当Servlet容器启动的时候读取
配置节信息,根据 配置节信息建立Servlet对象,同时根据 配置节信息建立HttpServletConfig对象,而后执行Servlet对象的init方法,而且根据 配置节信息来决定建立Servlet对象的顺序,若是此配置节信息为负数或者没有配置,那么在Servlet容器启动时,将不加载此Servlet对象。当客户访问Servlet容器时,Servlet容器根据客户访问的URL地址,经过 配置节中的 配置节信息找到指定的Servlet对象,并调用此Servlet对象的service方法。
在整个Servlet的生命周期过程当中,建立Servlet实例、调用实例的init()和destroy()方法都只进行一次,当初始化完成后,Servlet容器会将该实例保存在内存中,经过调用它的service()方法,为接收到的请求服务。下面给出Servlet整个生命周期过程的UML序列图,如图所示:
Servlet生命周期
若是须要让Servlet容器在启动时即加载Servlet,能够在web.xml文件中配置
元素。
Listener 使用的很是普遍,它是基于观察者模式设计的,Listener 的设计对开发 Servlet 应用程序提供了一种快捷的手段,可以方便的从另外一个纵向维度控制程序和数据。目前 Servlet 中提供了 5 种两类事件的观察者接口,它们分别是:4 个 EventListeners 类型的,ServletContextAttributeListener、ServletRequestAttributeListener、ServletRequestListener、HttpSessionAttributeListener 和 2 个 LifecycleListeners 类型的,ServletContextListener、HttpSessionListener。以下图所示:
Servlet中的Listener
它们基本上涵盖了整个 Servlet 生命周期中,你感兴趣的每种事件。这些 Listener 的实现类能够配置在 web.xml 中的
Servlet 可以给咱们提供两部分数据,一个是在 Servlet 初始化时调用 init 方法时设置的 ServletConfig,这个类基本上含有了 Servlet 自己和 Servlet 所运行的 Servlet 容器中的基本信息。还有一部分数据是由 ServletRequest 类提供,从提供的方法中发现主要是描述此次请求的 HTTP 协议的信息。关于这一块还有一个让不少人迷惑的 Session 与 Cookie。
Session 与 Cookie 的做用都是为了保持访问用户与后端服务器的交互状态。它们有各自的优势也有各自的缺陷。然而具备讽刺意味的是它们优势和它们的使用场景又是矛盾的,例如使用 Cookie 来传递信息时,随着 Cookie 个数的增多和访问量的增长,它占用的网络带宽也也会愈来愈大。因此大访问量的时候但愿用 Session,可是 Session 的致命弱点是不容易在多台服务器之间共享,因此这也限制了 Session 的使用。
无论 Session 和 Cookie 有什么不足,咱们仍是要用它们。下面详细讲一下,Session 如何基于 Cookie 来工做。实际上有三种方式能可让 Session 正常工做:
第一种状况下,当浏览器不支持 Cookie 功能时,浏览器会将用户的 SessionCookieName 重写到用户请求的 URL 参数中,它的传递格式如:
/path/Servlet?name=value&name2=value2&JSESSIONID=value3
接着 Request 根据这个 JSESSIONID 参数拿到 Session ID 并设置到 request.setRequestedSessionId 中。
请注意若是客户端也支持 Cookie 的话,Tomcat 仍然会解析 Cookie 中的 Session ID,并会覆盖 URL 中的 Session ID。
若是是第三种状况的话将会根据 javax.servlet.request.ssl_session 属性值设置 Session ID。
有了 Session ID 服务器端就能够建立 HttpSession 对象了,第一次触发是经过 request. getSession() 方法,若是当前的 Session ID 尚未对应的 HttpSession 对象那么就建立一个新的,并将这个对象加到 org.apache.catalina. Manager 的 sessions 容器中保存,Manager 类将管理全部 Session 的生命周期,Session 过时将被回收,服务器关闭,Session 将被序列化到磁盘等。只要这个 HttpSession 对象存在,用户就能够根据 Session ID 来获取到这个对象,也就达到了状态的保持。
Session相关类图
上从图中能够看出从 request.getSession 中获取的 HttpSession 对象其实是 StandardSession 对象的门面对象,这与前面的 Request 和 Servlet 是同样的原理。下图是 Session 工做的时序图:
Session工做的时序图
还有一点与 Session 关联的 Cookie 与其它 Cookie 没有什么不一样,这个配置的配置能够经过 web.xml 中的 session-config 配置项来指定。
http://www.javashuo.com/article/p-ohvhntlc-ke.html
http://www.javashuo.com/article/p-tfbleich-bp.html
http://c.biancheng.net/view/939.html
https://blog.csdn.net/android_hl/article/details/53228348
黄小斜是跨考软件工程的 985 硕士,自学 Java 两年,拿到了 BAT 等近十家大厂 offer,从技术小白成长为阿里工程师。
做者专一于 JAVA 后端技术栈,热衷于分享程序员干货、学习经验、求职心得和程序人生,目前黄小斜的CSDN博客有百万+访问量,知乎粉丝2W+,全网已有10W+读者。
黄小斜是一个斜杠青年,坚持学习和写做,相信终身学习的力量,但愿和更多的程序员交朋友,一块儿进步和成长!
原创电子书:
关注危险公众号【黄小斜】后回复【原创电子书】便可领取我原创的电子书《菜鸟程序员修炼手册:从技术小白到阿里巴巴Java工程师》这份电子书总结了我2年的Java学习之路,包括学习方法、技术总结、求职经验和面试技巧等内容,已经帮助不少的程序员拿到了心仪的offer!
程序员3T技术学习资源: 一些程序员学习技术的资源大礼包,关注公众号后,后台回复关键字 “资料” 便可免费无套路获取,包括Java、python、C++、大数据、机器学习、前端、移动端等方向的技术资料。
若是你们想要实时关注我更新的文章以及分享的干货的话,能够关注个人微信公众号【Java技术江湖】
这是一位阿里 Java 工程师的技术小站。做者黄小斜,专一 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中间件、集群、Linux、网络、多线程,偶尔讲点Docker、ELK,同时也分享技术干货和学习经验,致力于Java全栈开发!
(关注公众号后回复”Java“便可领取 Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送做者原创的Java学习指南、Java程序员面试指南等干货资源)
Java工程师必备学习资源: 一些Java工程师经常使用学习资源,关注公众号后,后台回复关键字 “Java” 便可免费无套路获取。