其他更多 java 基础文章:
java 基础学习(目录)
什么是 Servlet
Servlet 是一个特殊的 Java 类, 是运行在 web 服务器中的小型 Java 程序 (即: 服务器端的小应用程序).servlet 通常通过 HTTP(超文本传输协议) 接收和响应来自 Web 客户端的请求. 这个 Java 类必须继承 HttpServlet. 每个 Servlet 可以响应客户端的请求, Servlet 提供不同的方法用于响应客户端请求, 例如 doGet,doPost,doPut 等
Tomcat 与 Servlet 的关系
Tomcat 是 Web 应用服务器, 是一个 Servlet/JSP 容器. Tomcat 作为 Servlet 容器, 负责处理客户请求, 把请求传送给 Servlet, 并将 Servlet 的响应传送回给客户. 而 Servlet 是一种运行在支持 Java 语言的服务器上的组件..
Servlet 最常见的用途是扩展 Java Web 服务器功能, 提供非常安全的, 可移植的, 易于使用的 CGI 替代品. 从 http 协议中的请求和响应可以得知, 浏览器发出的请求是一个请求文本, 而浏览器接收到的也应该是一个响应文本.
Tomcat 将 http 请求文本接收并解析, 然后封装成 HttpServletRequest 类型的 request 对象, 所有的 HTTP 头数据读可以通过 request 对象调用对应的方法查询到.
Tomcat 同时会要响应的信息封装为 HttpServletResponse 类型的 response 对象, 通过设置 response 属性就可以控制要输出到浏览器的内容, 然后将 response 交给 tomcat,tomcat 就会将其变成响应文本的格式发送给浏览器.
Java Servlet API 是 Servlet 容器 (tomcat) 和 servlet 之间的接口, 它定义了 serlvet 的各种方法, 还定义了 Servlet 容器传送给 Servlet 的对象类, 其中最重要的就是 ServletRequest 和 ServletResponse. 所以说我们在编写 servlet 时, 需要实现 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 的生命周期
Servlet 生命周期可分为 5 个步骤
加载 Servlet. 当 Tomcat 第一次访问 Servlet 的时候, Tomcat 会负责创建 Servlet 的实例
初始化. 当 Servlet 被实例化后, Tomcat 会调用 init()方法初始化这个对象
处理服务. 当浏览器访问 Servlet 的时候, Servlet 会调用 service()方法处理请求
销毁. 当 Tomcat 关闭时或者检测到 Servlet 要从 Tomcat 删除的时候会自动调用 destroy()方法, 让该实例释放掉所占的资源. 一个 Servlet 如果长时间不被使用的话, 也会被 Tomcat 自动销毁
卸载. 当 Servlet 调用完 destroy()方法后, 等待垃圾回收. 如果有需要再次使用这个 Servlet, 会重新调用 init()方法进行初始化操作.
简单总结: 只要访问 Servlet,service()就会被调用. init()只有第一次访问 Servlet 的时候才会被调用. destroy()只有在 Tomcat 关闭的时候才会被调用.
Servlet 的配置
使用配置文件
写 Servlet 类
- 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);
- }
- }
在 Web.xml 中配置 Servlet
###### 使用 springboot
写 servlet 类
在 springboot 启动类配置
- @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 减去当前应用上下文的路径作为 servlet 的映射 url, 比如我访问的是 http://localhost/hiway/user/aaa.html, 我的应用上下文是 hiway, 容器会将 http://localhost/hiway 去掉, 剩下的 / user/aaa.HTML 部分拿来做 servlet 的映射匹配. 这个映射匹配过程是有顺序的, 而且当有一个 servlet 匹配成功以后, 就不会去理会剩下的 servlet 了. 其匹配规则和顺序如下:
精确路径匹配. 例子: 比如 servletA 的 url-pattern 为 /test,servletB 的 url-pattern 为 /* , 这个时候, 如果我访问的 url 为 http://localhost/test , 这个时候容器就会先进行精确路径匹配, 发现 / test 正好被 servletA 精确匹配, 那么就去调用 servletA, 也不会去理会其他的 servlet 了.
最长路径匹配. 例子: servletA 的 url-pattern 为 / test/, 而 servletB 的 url-pattern 为 / test/a/, 此时访问 http://localhost/test/a 时, 容器会选择路径最长的 servlet 来匹配, 也就是这里的 servletB.
扩展匹配. 如果 url 最后一段包含扩展, 容器将会根据扩展选择合适的 servlet. 例子: servletA 的 url-pattern:*.action
最后, 如果前面三条规则都没有找到一个 servlet, 容器会根据 url 选择对应的请求资源. 如果应用定义了一个 default servlet, 则容器会将请求丢给 default 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
实现 SingleThreadModel 接口
使用 synchronized 同步对共享数据的操作
避免使用实例变量
对上面的三种方法进行测试, 可以表明用它们都能设计出线程安全的 Servlet 程序. 但是, 如果一个 Servlet 实现了 SingleThreadModel 接口, Servlet 引擎将为每个新的请求创建一个单独的 Servlet 实例, 这将引起大量的系统开销. SingleThreadModel 在 Servlet2.4 中已不再提倡使用; 同样如果在程序中使用同步来保护要使用的共享的数据, 也会使系统的性能大大下降. 这是因为被同步的代码块在同一时刻只能有一个线程执行它, 使得其同时处理客户请求的吞吐量降低, 而且很多客户处于阻塞状态. 另外为保证主存内容和线程的工作内存中的数据的一致性, 要频繁地刷新缓存, 这也会大大地影响系统的性能. 所以在实际的开发中也应避免或最小化 Servlet 中的同步代码;** 在 Serlet 中避免使用实例变量是保证 Servlet 线程安全的最佳选择.** 从 Java 内存模型也可以知道, 方法中的临时变量是在栈上分配空间, 而且每个线程都有自己私有的栈空间, 所以它们不会影响线程的安全
学习资料: http://lixh1986.iteye.com/blog/2355692
Servlet 的 < load-on-startup>
在 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()先初始化, 初始化成功后, 再响应请求.
缺省 default Servlet
可以将 url-pattern 配置一个 /, 代表该 servlet 是缺省的 servlet. 什么是缺省的 servlet? 当你访问资源地址所有的 servlet 都不匹配时, 缺省的 servlet 赋值处理. 其实, Web 应用中所有的资源的响应都是 servlet 负责, 包括静态资源(HTML 页面).(有配置缺省的 servlet, 无法访问到静态资源.)
学习资料: https://www.cnblogs.com/zhangyinhua/p/7625851.html
来源: https://juejin.im/post/5c160422e51d45453c1cdc30