ServletConfig与ServletContext
ServletConfig对象
web容器负责调用Servlet的init(ServletConfig config)方法,web容器会将该Servlet在web.xml中的配置信息拿出来,创建一个ServletConfig对象,封装这些信息,再作为参数传入该Servlet的init方法。init方法在GenericServlet中就相当于GenericServlet中成员属性config(该属性虽然引用了另一个对象,但它被transient关键字修饰,所以该属性引用的另一个对 象不会随着GenericServlet对象的序列化被序列化)的set方法。ServletConfig代表当前Servlet在web.xml中的配置信息,可以用来获取<servlet>中用<init-param>配置的初始化参数。例如:
<servlet>
<!-- 配置当前Servlet的初始化参数,只有当前Servlet能访问到 -->
<init-param>
<param-name>driver</param-name>
<param-value>com.mysql.jdbc.Driver</param-value>
</init-param>
</servlet>
ServletConfig有什么用呢?它涉及到我们软件的可维护性:
情景:如果将一个属性定义到一个Servlet实体类中,我们明天要修改这个driver的值。
解决方案一:你找到所有用到driver属性的实体类,将它们对应的driver属性的值改了,然后将编译后的class文件再重新发布到服务器上
解决方案二:你之前将这个driver属性以键值对的方式写到web.xml文件中,然后在Servlet实体类中,你通过this.getServletConfig()方法获取了一个ServletConfig对象,用这个对象的getInitParameter("driver")获取web.xml中配置的driver属性的值。所以你只修改了web.xml文件中的值,并且你也不用重新发布项目。
毫无疑问,解决方案二的可维护性更高。
*ServletContext对象
ServletContext这个类对web应用进行抽象,一个ServletContext对象与一个具体的web应用相对应(java面向对象思想:使用对象封装了与它对应的现实事物的各种属性值),所以ServletContext对象当中封装了web应用中的各种属性值。
生命周期
当服务器启动时会依次加载web应用,并在加载完成之后会创建一个ServletContext对象来唯一代表该web应用,它随着web应用的存在而一直存在,直到web应用移出容器或者服务器关闭时,随着web应用销毁,ServletContext对象跟着销毁。
注:加载web应用其实是把web应用中的各种对象:filter(过滤器对象)、listener(监听器对象)等加载到内存中,如果你想直接操作这些对象是找不到的,所以有了ServletContext对象来代表这个web应用,你想操作web应用中的组件,直接找它就行了。
获取方法(自定义Servlet的doGet方法中)
方法1:通过ServletConfig对象提供getServletContext()方法
// 获取ServletContext的方法1
ServletContext sc1 = this.getServletConfig().getServletContext();
方法2:在GenericServlet中提供了便捷的getServletContext()方法,我们写的Servlet继承自GenericServlet,所以可以通过this.getServletContext()来获取ServletContext对象。(其实底层也是用的ServletConfig对象获取的)
// 获取ServletContext的方法2
ServletContext sc1=this.getServletContext();
实际应用
1.读取web.xml中web应用的初始化参数(这个参数和ServletConfig的参数不太一样),在web应用中的所有组件都可以访问到。
以下这两段代码是在web.xml中将配给Servlet的编码集改为整个站点配置并通过ServletContext获取。(注意web.xml改完之后要重启一下服务器)注意:每个参数都是一组<context-param>标签
<!-- 配置当前web应用的初始化参数,在其所有组件有效 -->
<context-param>
<param-name>encode</param-name>
<param-value>utf-8</param-value>
</context-param>
// 获取web.xml中配置的初始化参数
String encode=sc1.getInitParameter("encode");
拓展:使用迭代器查看web应用中所有的初始化参数名
Enumeration<String> names= sc1.getInitParameterNames();
while(names.hasMoreElements()){
String name=names.nextElement();
System.out.println(name+":"+sc1.getInitParameter(name));
}
2.ServletContext对象作为域对象使用,也被称为Application作用域。利用这个对象上的map就可以在整个web应用内实现资源的共享
域:这个对象在一定范围内是可见的,利用这个对象身上的map,在这个范围内共享数据,这样的对象叫做域对象。
Javaweb开发中一共有四大域对象
ServletContext(Application)域:
作用范围:整个web应用中都可见
生命周期:ServletContext代表当前web应用,当服务器启动时会依次加载web应用,并在加载完成之后会创建一个ServletContext对象来唯一标识该web应用,它随着web应用的存在而一直存在,直到web应用移出容器或者服务器关闭时,随着web应用一起被销毁
操作域的方法:
setAttribute(String name,Object obj);//向域中设置数据
Object getAttribute(String name);//从域中获取数据
removeAttribute(String name);//从域中移除数据
Application作用域和Request作用域对比
生命周期:一个是整个web应用周期,一个是一次请求一次应答
作用范围:一个是整个web应用,一个是整个请求链
具体作用:一个是整个web应用内实现共享,一个是Servlet带数据给JSP
问题:理论上Request作用域能做的事儿,Application作用域都能做,但为什么还会有Request作用域呢?
答:核心思想是多用户。我们根据两种情景来解释。
使用Request作用域:用户1请求Servlet1,在Request1中存入属性name,值为张三,然后Servlet1将请求转发给Servlet2,Servlet2会从Request1中取出数据执行。用户2请求Servlet1,在Request2中存入属性name,值为李四,然后Servlet1将请求转发给Servlet2,Servlet2会从Request2中取出数据执行。不会产生错误。
使用Application作用域:用户1请求Servlet1,在Application中存入属性name,值为张三,然后Servlet1将请求转发给Servlet2,Servlet2还没来得及将数据从Application作用域中取出,用户2的请求到了,属性也是name,但是值为李四,由于Application中的map集合中又放了一个name,那么之前name的值会被覆盖成为李四。当用户1请求Servlet2时拿到的值就是李四了。
所以Application作用域的局限性在于它只有一个,碰到多线程访问时会产生并发安全问题。而Request作用域是为每个用户的每次请求设计的专属对象,用来存储用户当前请求的所有数据
3.