Head First Servlets&JSP 读书笔记_5
如何将某些参数写在DD中,而非写死在类中
初版解决办法(以Email地址为例):
1 | //DD中: |
同时
1 | //servlet中: |
- 一般
Servlet
都是继承自HttpServlet
,而HttpServlet
是GenericServlet
的子类。getInitParameter()
方法来自于GenericServlet
,所以Servlet可以调用此方法去获取web.xml配置文件中的配置信息。 - 容器初始化一个servlet时,会为这个servlet建一个唯一的ServletConfig。其出场顺序为:先由容器加载servlet类,然后init()初始化,初始化完成前得到了ServletConfig对象,同时可以调用getInitParameter方法,从DD中读取初始化参数,经由ServletConfig传递给init()方法。
- 划重点:先
init()
,后得到ServletConfig
,同时使用初始化参数(只能读一次)
!可参考Head First Servlets&JSP 读书笔记_3中关于init()方法的讲解 - 以上的代码只是专属于此Servlet的初始化参数,无法全局使用;若采取保存下来的方式,又必须使此Servlet最早先运行,这么一来可维护性极差
除了该Servlet,如何让Web中其他部分也得到此参数?
升级版解决办法:使用ServletContext
,对整个Web应用中的所有servlet和JSP都可用
1 | //DD中: |
同时
1 | //servlet中: |
说明:
- 每个servlet有一个ServletConfig,每个Web应用有一个ServletContext
- 如果应用是分布式应用,那么每个JVM有一个ServletContext
- ServletContext也是在Servlet发生初始化时,对初始化参数完成设置
- 初始化参数=部署时常量;运行时可以得到,但无法设置
- 一般所说初始化参数,默认指“Servlet初始化参数”
- ServletContext还可以得到:有关服务器/容器的信息;写到服务器的日志文件,RequestDispatcher等
- 以上的初始化参数只能是String,其他参数怎么玩?使用对象来初始化怎么玩?见下文
使用对象来初始化Web应用的办法:listener
理论
- 需求:使用非String类型的参数,对Web进行初始化。
- 问题:1.初始化参数只能是String类型,
2.想把对象转成String,但转换代码无法塞到servlet或JSP之前运行 - 解决办法:建立一个ServletContextListener类,用以监听ServletContext的创建和撤销。
interface ServletContextListener |
---|
contextInitialized(ServletContextEvent) contextDestroyed(ServletContextEvent) |
以下以DataSource举例,若需要传递对象,可见下文对Dog的举例。两者原理步骤相通
1 | //ServletContextListener类 |
举例Dog之步骤陈述
把Dog类放到ServletContext中
新建一个监听类放在
WEB-INF/classes
目录(或其他目录)下,此类实现ServletContextListener接口,并在DD中放一个告知容器此处有监听者 1
2
3
4
5
6<listener>
<listener-class>
<!--监听类的全路径-->
com.example.MyServletContextListener
</listener-class>
</listener>新建Dog类,普通java类,由
ServletContextListener
初始化,设置在SerletContext
,由servlet
获取新建Servlet类,扩展
HttpServlet
,验证此监听类。此Servlet从ServletContext中得到Dog属性,然后调用Dog的另外的方法,打印到响应中
举例Dog之代码实现
1 | //MyServletContextListener类中的contextInitialized()方法: |
1 | //Dog类,平平无奇 |
1 | //Servlet类,测试类 |
完整的故事
- 容器读DD,包括
listener
和context-param
元素,容器为Web创建新的ServletContext - 容器为每个
ServletContext初始化参数
创建一个String名/值对
,容器将该名/值对(举例为breed)的引用交给ServletContext - 容器创建MyServletContextListener类的实例对象
- 容器调用监听者的
contextInitialized()
方法,传入新的ServletContextEvent
。注:这个event有ServletContext的一个引用,所以事件处理代码可以得到上下文,即event.getServletContext()
,进而得到上下文初始化参数 - 按照4的event,监听者向
ServletContextEvent
请求ServletContext
的一个引用,再向ServletContext
请求上下文初始化参数“breed” - 监听者使用该初始化参数来构造一个新的Dog对象
- 监听者将Dog设置为
ServletContext
的一个属性:sc.setAttribute("dog",d)
;正式调用Servlet前的准备工作就此完毕 - 容器建立一个新的Servlet,利用上面的ServletConfig->ServletContext->init()方法,新建Servlet完毕
- Servlet获得请求,并向ServletContext请求属性dog:
getServletContext().getAttribute("dog")
,Servlet在Dog上调用getBreed()
,并将结果打印到HttpResponse中
上下文作用域的非线程安全
- Web应用的每个部分都可以访问上下文属性,多个servlet说明可能有多个线程,线程并发会引起非线程安全
- 对doGet进行同步服务
synchronized
,意味着servlet一次仅能处理一个客户,但并不能阻止不同servlet或JSP访问上下文属性,所以需要对上下文加锁,而非servlet
1 | //doGet()方法 |
已完成全书第5章197页,读书笔记未完待续。。