Servlet的调用图
前面我们已经学过了Servlet的生命周期了,我们根据Servlet的生命周期画出Servlet的调用图加深理解
![4c3c8662fc3b9fe367da4770b0aaf53d.png](https://i-blog.csdnimg.cn/blog_migrate/14ee9d05c9ba31740893983b7d5fb5a4.jpeg)
Servlet的细节
一个已经注册的Servlet可以被多次映射
同一个Servlet可以被映射到多个URL上。
![34e487fadaf565da01dd878e7ea893ae.png](https://i-blog.csdnimg.cn/blog_migrate/567fd9675f72308355fa89ca7c73af01.jpeg)
无论我访问的是http://localhost:8080/Demo1还是http://localhost:8080/ouzicheng。我访问的都是Demo1。
![c3a6a23f0d8dd31e9fe9a437e9033b12.png](https://i-blog.csdnimg.cn/blog_migrate/c01d99e574d7eeafc76e02fb7e9b61e4.jpeg)
![a1aab969460e33e31b907c40b5e56ff8.png](https://i-blog.csdnimg.cn/blog_migrate/82ce2d0aa1242b6f98bcf5394ded184b.jpeg)
Servlet映射的URL可以使用通配符
通配符有两种格式:
- *.扩展名
- 正斜杠(/)开头并以“/*”结尾。
匹配所有
![4898b1f533bed254ac52de905936768a.png](https://i-blog.csdnimg.cn/blog_migrate/f6dec7ec790b9f7e499a423cd6346d6b.jpeg)
匹配扩展名为.jsp的
![b2028c8c625a8ec5b875740cd1b90524.png](https://i-blog.csdnimg.cn/blog_migrate/50019d86b8c7904ec8b8ff7214c35d15.jpeg)
如果.扩展名和正斜杠(/)开头并以“/”结尾两种通配符同时出现,匹配的是哪一个呢?
- 看谁的匹配度高,谁就被选择
- *.扩展名的优先级最低
Servlet映射的URL可以使用通配符和Servlet可以被映射到多个URL上的作用:
- 隐藏网站是用什么编程语言写的【.php,.net,.asp实际上访问的都是同一个资源】
- 用特定的后缀声明版权【公司缩写】
![3a175d8c208a1c3aee5fd327262e200c.png](https://i-blog.csdnimg.cn/blog_migrate/e608a63f0f3f022ef7ea8aa52696af09.jpeg)
Servlet是单例的
为什么Servlet是单例的
浏览器多次对Servlet的请求,一般情况下,服务器只创建一个Servlet对象,也就是说,Servlet对象一旦创建了,就会驻留在内存中,为后续的请求做服务,直到服务器关闭。
每次访问请求对象和响应对象都是新的
对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。
线程安全问题
当多个用户访问Servlet的时候,服务器会为每个用户创建一个线程。当多个用户并发访问Servlet共享资源的时候就会出现线程安全问题。
原则:
- 如果一个变量需要多个用户共享,则应当在访问该变量的时候,加同步机制synchronized (对象){}
- 如果一个变量不需要共享,则直接在 doGet() 或者 doPost()定义.这样不会存在线程安全问题
load-on-startup
如果在元素中配置了一个元素,那么WEB应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init()方法。
![4cee19f08b62f04413fe815a042818a6.png](https://i-blog.csdnimg.cn/blog_migrate/9319354d41fd23ba907ee318adc94e93.jpeg)
![b8081516deeadb1d6bff1e4daf986d72.png](https://i-blog.csdnimg.cn/blog_migrate/4ab6999efdfd3811ddbcede65edeb491.jpeg)
作用:
- 为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据
- 完成一些定时的任务【定时写日志,定时备份数据】
在web访问任何资源都是在访问Servlet
当你启动Tomcat,你在网址上输入http://localhost:8080。为什么会出现Tomcat小猫的页面?
这是由缺省Servlet为你服务的!
- 我们先看一下web.xml文件中的配置,web.xml文件配置了一个缺省Servlet
![d00941a47226ddd2b184cedde56467cc.png](https://i-blog.csdnimg.cn/blog_migrate/2f45c01473b8e50cf98e67f4c37985bf.jpeg)
- 什么叫做缺省Servlet?凡是在web.xml文件中找不到匹配的元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求
- 既然我说了在web访问任何资源都是在访问Servlet,那么我访问静态资源【本地图片,本地HTML文件】也是在访问这个缺省Servlet【DefaultServlet】
- 证实一下:当我没有手工配置缺省Servlet的时候,访问本地图片是可以访问得到的
![9f1a7beebeec060b83467be28ab665d7.png](https://i-blog.csdnimg.cn/blog_migrate/d0eacb6da92ba8d2dfabeaa317a37622.jpeg)
- 现在我自己配置一个缺省Servlet,Demo1就是我手工配置的缺省Servlet,覆盖掉web.xml配置的缺省Servlet
![b48309075bb25d5ceec3359995af012f.png](https://i-blog.csdnimg.cn/blog_migrate/dfa87a87053b8519fa209fdcfe37e46e.jpeg)
- 下面我继续访问一下刚才的图片,此时输出的是Demo1这个Servlet写上的内容了
![05b28967563d003fe80ad38e43fadbb5.png](https://i-blog.csdnimg.cn/blog_migrate/0aa15cb47beed60653c3660a4681e610.jpeg)
- 总结:无论在web中访问什么资源【包括JSP】,都是在访问Servlet。没有手工配置缺省Servlet的时候,你访问静态图片,静态网页,缺省Servlet会在你web站点中寻找该图片或网页,如果有就返回给浏览器,没有就报404错误
ServletConfig对象
ServletConfig对象有什么用?
通过此对象可以读取web.xml中配置的初始化参数。
现在问题来了,为什么我们要把参数信息放到web.xml文件中呢?我们可以直接在程序中都可以定义参数信息,搞到web.xml文件中又有什么好处呢?
好处就是:能够让你的程序更加灵活【更换需求,更改配置文件web.xml即可,程序代码不用改】
获取web.xml文件配置的参数信息
- 为Demo1这个Servlet配置一个参数,参数名是name,值是zhongfucheng
![6b2f2bb10abbc549c8f93f9668637c0d.png](https://i-blog.csdnimg.cn/blog_migrate/778bc0ce69d4d315b785bd0860dbf334.jpeg)
在Servlet中获取ServletConfig对象,通过ServletConfig对象获取在web.xml文件配置的参数
![a13f9ad169a130106be2625a8cf232e2.png](https://i-blog.csdnimg.cn/blog_migrate/79530ff4e596af830e18397eac70be98.jpeg)
ServletContext对象
什么是ServletContext对象?
当Tomcat启动的时候,就会创建一个ServletContext对象。它代表着当前web站点
ServletContext有什么用?
- ServletContext既然代表着当前web站点,那么所有Servlet都共享着一个ServletContext对象,所以Servlet之间可以通过ServletContext实现通讯。
- ServletConfig获取的是配置的是单个Servlet的参数信息,ServletContext可以获取的是配置整个web站点的参数信息
- 利用ServletContext读取web站点的资源文件
- 实现Servlet的转发【用ServletContext转发不多,主要用request转发】
Servlet之间实现通讯
ServletContext对象可以被称之为域对象
到这里可能有一个疑问,域对象是什么呢?其实域对象可以简单理解成一个容器【类似于Map集合】
实现Servlet之间通讯就要用到ServletContext的setAttribute(String name,Object obj)方法, 第一个参数是关键字,第二个参数是你要存储的对象
- 这是Demo2的代码
![c9970743495488ae79e9fe9d74be3814.png](https://i-blog.csdnimg.cn/blog_migrate/bf0710c56c02b527ce01e6e71bf5c16b.jpeg)
- 这是Demo3的代码
![ba36559de0c90fcf3d0f7580f525f493.png](https://i-blog.csdnimg.cn/blog_migrate/29923faf5308ac7914e256e8e0da5561.jpeg)
- 访问Demo3可以获取Demo2存储的信息,从而实现多个Servlet之间通讯
![a3aba1a451729e233977a75a7d6cd83f.png](https://i-blog.csdnimg.cn/blog_migrate/5fd0c760f48cf76e528f71bc69067479.jpeg)
获取web站点配置的信息
如果我想要让所有的Servlet都能够获取到连接数据库的信息,不可能在web.xml文件中每个Servlet中都配置一下,这样代码量太大了!并且会显得非常啰嗦冗余。
- web.xml文件支持对整个站点进行配置参数信息【所有Servlet都可以取到该参数信息】
![18ed2922d75e01e94ba2b145782988ae.png](https://i-blog.csdnimg.cn/blog_migrate/113cd09d2b164420093797eb60dd373e.jpeg)
- Demo4代码
![33b4037b55a54de1bf2eec58e1b07b4e.png](https://i-blog.csdnimg.cn/blog_migrate/b921e7d13e71784be0296605b3a25270.jpeg)
![df37147aa14cb0a7bd75c95bba17794e.png](https://i-blog.csdnimg.cn/blog_migrate/5b3b399b88b8ae7304a33ceed7684592.jpeg)
- 试一下Demo3是否能拿到,相同的代码
![70d358731dcdb4c3af9df7ae3a92eacf.png](https://i-blog.csdnimg.cn/blog_migrate/cacb15c11a6f5c0d505c4d71ffdbed0b.jpeg)
![dcfb0a7eda0a01dd43b5238ef6efa2c4.png](https://i-blog.csdnimg.cn/blog_migrate/fddcf7bc41041a2ec65cec4f222ab74e.jpeg)
读取资源文件
第一种方式:
- 现在我要通过Servlet111读取1.png图片
![d41b1bea80d12507a433e47291eed70f.png](https://i-blog.csdnimg.cn/blog_migrate/5833fd1c1ec5bbd8231a6f90f6b9710e.jpeg)
- 按我们以前的方式,代码应该是这样的。
![c662196b0be4dc452339f9689a2ec627.png](https://i-blog.csdnimg.cn/blog_migrate/118d36a746863032e42057a12558b921.jpeg)
- 当我们访问的时候,却出错了!说找不到1.png文件
![c17b455ac973ea1797e902f8a804407b.png](https://i-blog.csdnimg.cn/blog_migrate/da388f01422c437a5940c1239afe05b3.jpeg)
- 这是为什么呢?我们以前读取文件的时候,如果程序和文件在同一包名,可以直接通过文件名称获取得到的!,原因很简单,以前我们写的程序都是通过JVM来运行的,而现在,我们是通过Tomcat来运行的
- 根据web的目录规范,Servlet编译后的class文件是存放在WEB-INFclasses文件夹中的
![a8fb7980f908dcf28683ce6eccf85be1.png](https://i-blog.csdnimg.cn/blog_migrate/9142caa5259a393432d308e03c493e6b.jpeg)
- 看到这里,我们知道了要进入classes目录中读取文件,所以我们将代码改成以下方式
![490928f75ed6106cd33d716561128284.png](https://i-blog.csdnimg.cn/blog_migrate/4613da77d671d233141439a14fb2b39a.jpeg)
- 再去读取时,就发现可以获取到文件了。
- 但是现在问题又来了,我读取文件的时候都要写上绝对路径,这样太不灵活了。试想一下,如果我将该读取文件的模块移到其他的web站点上,我的代码就又要修改了【因为web站点的名字不一样】。
- 我们通过ServletContext读取就可以避免修改代码的情况,因为ServletContext对象是根据当前web站点而生成的
- 代码如下所示:
![00cf51fd553ebd24d342bee8a036a611.png](https://i-blog.csdnimg.cn/blog_migrate/81f5757b67119a38737f6c158c3e25c0.jpeg)
![c2af6c88a855fdc790b8e15e651d96a2.png](https://i-blog.csdnimg.cn/blog_migrate/e73646ec3b605e7f277c1768537585ee.jpeg)
第二种方式:
- 如果我的文件放在web目录下,那么就简单得多了!,直接通过文件名称就能获取
![39398f07ae6cb07ce5c0b73a917df991.png](https://i-blog.csdnimg.cn/blog_migrate/f5ed8f8e6224ef53f78bb258db168aba.jpeg)
- 代码如下所示
![d068ba57072db51a13f5bcf68e80674e.png](https://i-blog.csdnimg.cn/blog_migrate/9fe37747f685a496c2ad1dfe8ad7b0fe.jpeg)
![bc1d1f49c95360b4eaee71752d50cb57.png](https://i-blog.csdnimg.cn/blog_migrate/4fa09934d7d748a406acba9f75bee567.jpeg)
第三种方式:
通过类装载器读取资源文件。
- 我的文件放在了src目录下【也叫做类目录】
![1f1235230922d6132820e813029b7881.png](https://i-blog.csdnimg.cn/blog_migrate/44869032e373c2855bec3697093115d5.jpeg)
- 代码如下所示
![07213ec3401a86ddfb6f26c7dd6a3348.png](https://i-blog.csdnimg.cn/blog_migrate/434219089f60e0b73325800c4318979e.jpeg)
![695d171e8b68ca91833eea3f6b3b5af7.png](https://i-blog.csdnimg.cn/blog_migrate/ebf4e166579de7bf05ff4dbcbb1f4e60.jpeg)
- 我的文件放在了src目录下的包下
![1981734a981ce8e461f6aafca9670af6.png](https://i-blog.csdnimg.cn/blog_migrate/09866f0c0193b9cf791749e1bac6737e.jpeg)
- 代码如下,添加包名路径即可。
![03dd5123de41695dff6a39a985cc6bbd.png](https://i-blog.csdnimg.cn/blog_migrate/3c6196fd5e25884cca864209c3b55b82.jpeg)
原则:如果文件太大,就不能用类装载器的方式去读取,会导致内存溢出
原文地址:https://dwz.cn/cQtQEKw2作者: Java3y