Servlet是一个特殊的Java类, 是运行在 Web 服务器中的小型 Java 程序(即:服务器端的小应用程序)。servlet 通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。这个Java类必须继承HttpServlet。每个Servlet可以响应客户端的请求,Servlet提供不同的方法用于响应客户端请求,例如doGet,doPost,doPut等
Tomcat 是Web应用服务器,是一个Servlet/JSP容器. Tomcat 作为Servlet容器,负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传送回给客户.而Servlet是一种运行在支持Java语言的服务器上的组件.。
Servlet最常见的用途是扩展Java Web服务器功能,提供非常安全的,可移植的,易于使用的CGI替代品。 从http协议中的请求和响应可以得知,浏览器发出的请求是一个请求文本,而浏览器接收到的也应该是一个响应文本。
Java Servlet API 是Servlet容器(tomcat)和servlet之间的接口,它定义了serlvet的各种方法,还定义了Servlet容器传送给Servlet的对象类,其中最重要的就是ServletRequest和ServletResponse。所以说我们在编写servlet时,需要实现Servlet接口,按照其规范进行操作。
在浏览器的地址栏输入:http://ip:port/appNames/servlet
1)通过浏览器和ip:port和这个服务器建立连接。
2) 浏览器会生成一个请求数据包(路径appNames/servlet)向服务器发送请求。
3) 服务器收到请求数据包,分析请求资源路径做精准定位,通过请求的appName查找webapps文件下面的appName做匹配,匹配上了需要获取web.xml中的servlet(mapping)。
4) 服务器创建两个对象:
第一个对象:请求对象,该对象实现了HttpServletRequest接口,服务器会将请求数据包中的数据解析出来,存储在该对象里。这样做的好处是没有必要理解http协议,只需要读取request。
第二个对象:响应对象,实现了HttpServletResponse接口,作用是servlet处理完成后的结果可以存放到该对象上,然后服务器依据该对象的数据生成响应数据包。
5) servlet在执行servlet()方法时,可以通过request获取请求数据,也可以将处理结果存放到response上。然后服务器与响应对象直接形成一个默契,生成一个响应数据包给浏览器。
6)浏览器解析服务器返回的响应数据包,生成响应的结果。
Servlet访问的过程:
Http请求---->web.xml--------> url -pattern----->servlet-name----->servlet-class-----> QuickStratServlet(对应的Class文件)
Servlet生命周期可分为 5 个步骤
简单总结: 只要访问Servlet,service()就会被调用。init()只有第一次访问Servlet的时候才会被调用。 destroy()只有在Tomcat关闭的时候才会被调用。
public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.service(req, resp); } } 复制代码
@SpringBootApplication public class ServletApp { @Bean public ServletRegistrationBean MyServlet(){ return new ServletRegistrationBean(new MyServlet(),"/myserv/*"); } public static void main(String[] args){ SpringApplication.run(ServletApp.class, args); } } 复制代码
#Servlet细节
当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://localhost/hiway/user/aaa.html,我的应用上下文是hiway,容器会将http://localhost/hiway去掉,剩下的/user/aaa.html部分拿来做servlet的映射匹配。这个映射匹配过程是有顺序的,而且当有一个servlet匹配成功以后,就不会去理会剩下的servlet了。其匹配规则和顺序如下:
在Servlet规范中,对于Servlet单例与多例定义如下:
“Deployment Descriptor”, controls how the servlet container provides instances of the servlet.For a servlet not hosted in a distributed environment (the default), the servlet container must use only one instance per servlet declaration. However, for a servlet implementing the SingleThreadModel interface, the servlet container may instantiate multiple instances to handle a heavy request load and serialize requests to a particular instance.
上面规范提到: 如果一个Servlet没有被部署在分布式的环境中,一般web.xml中声明的一个Servlet只对应一个实例。 而如果一个Servlet实现了SingleThreadModel接口,就会被初始化多个实例。默认20个
所以个人理解Servlet不算单例,只是容器让它只实例化一次,变现出来的是单例的效果而已
对上面的三种方法进行测试,可以表明用它们都能设计出线程安全的Servlet程序。但是,如果一个Servlet实现了SingleThreadModel接口,Servlet引擎将为每个新的请求创建一个单独的Servlet实例,这将引起大量的系统开销。SingleThreadModel在Servlet2.4中已不再提倡使用;同样如果在程序中使用同步来保护要使用的共享的数据,也会使系统的性能大大下降。这是因为被同步的代码块在同一时刻只能有一个线程执行它,使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态。另外为保证主存内容和线程的工作内存中的数据的一致性,要频繁地刷新缓存,这也会大大地影响系统的性能。所以在实际的开发中也应避免或最小化 Servlet 中的同步代码;**在Serlet中避免使用实例变量是保证Servlet线程安全的最佳选择。**从Java 内存模型也可以知道,方法中的临时变量是在栈上分配空间,而且每个线程都有自己私有的栈空间,所以它们不会影响线程的安全
学习资料: lixh1986.iteye.com/blog/235569…
在servlet的配置当中,
<load-on-startup>1</load-on-startup> 复制代码
的含义是:
标记容器是否在启动的时候就加载这个servlet。当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。正数的值越小,启动该servlet的优先级越高。
配置load-on-startup后,servlet在startup后立即加载,但只是调用servlet的init()方法,用以初始化该servlet相关的资源。初始化成功后,该servlet可响应web请求;如未配置load-on-startup,容器一般在第一次响应web请求时,会先检测该servlet是否初始化,如未初始化,则调用servlet的init()先初始化,初始化成功后,再响应请求。