API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。
当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来处理。在 ServletContextListener 接口中定义了处理ServletContextEvent 事件的两个方法。
Java代码
/**
* 当Servlet 容器启动Web 应用时调用该方法。在调用完该方法之后,容器再对Filter 初始化,
* 并且对那些在Web 应用启动时就需拆核要被初始化的Servlet 进行初始化。
*/
contextInitialized(ServletContextEvent sce)
/**
* 当Servlet 容器终止Web 应用时调用该方法。在调用该方法之前,容器会先销毁所有的Servlet 和Filter 过滤器。
*/
contextDestroyed(ServletContextEvent sce)
下面通过两个具体的例子来介绍 ServletContextListener 的用法。
例一:在服务启动时,将数据库中的数据加载进内存,并将其赋值给一个属性名,其它的 Servlet 就可以通过 getAttribute 进行属性值的访问。
有如下两个步骤:
1 : ServletContext 对象是一个为整个 web 应用提供共享的内存,任何请求都可以访问里面的内容
2 :如何实现在服务启动的时候就动态的加入到里面的内容:我旅模掘们需要做的有:
1 ) 实现 servletContextListerner 接口 并将要共享的通过 setAttribute ( name,data )方法提交到内存中去 ;
2 )应用项目通过 getAttribute(name) 将数据取到 。
Java代码
public class ServletContextLTest implements ServletContextListener{
// 实现其中的销码派毁函数
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("this is last destroyeed")
}
// 实现其中的初始化函数,当有事件发生时即触发
public void contextInitialized(ServletContextEvent sce) {
ServletContext sct=sce.getServletContext()
Map<Integer,String>depts=new HashMap<Integer,String>()
Connection connection=null
PreparedStatement pstm=null
ResultSet rs=null
try{
connection=ConnectTool.getConnection()
String sql="select deptNo,dname from dept"
pstm=connection.prepareStatement(sql)
rs=pstm.executeQuery()
while(rs.next()){
depts.put(rs.getInt(1), rs.getString(2))
}
// 将所取到的值存放到一个属性键值对中
sct.setAttribute("dept", depts)
System.out.println("======listener test is beginning=========")
}catch(Exception e){
e.printStackTrace()
}finally{
ConnectTool.releasersc(rs, pstm, connection)
}
}
}
在完成上述编码后,仍需在 web.xml 中进行如下配置,以使得该监听器可以起作用。
Xml代码
<listener>
<listener-class>ServletContextTest.ServletContextLTest</listener-class>
</listener>
在完成上述配置后, web 服务器在启动时,会直接加载该监听器,通过以下的应用程序就可以进行数据的访问。
Java代码
public class CreateEmployee extends HttpServlet{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletContext sct=getServletConfig().getServletContext()
// 从上下文环境中通过属性名获取属性值
Map<Integer,String>dept=(Map<Integer,String>)sct.getAttribute("dept")
Set<Integer>key=dept.keySet()
response.setContentType("text/htmlcharset=utf-8")
PrintWriter out=response.getWriter()
out.println("<html>")
out.println("<body>")
out.println("<form action='/register' action='post'>")
out.println("<table alignb='center'>")
out.println("<tr>")
out.println("<td>")
out.println("username:")
out.println("</td>")
out.println("<td>")
out.println("<input type='text' name='username'")
out.println("</tr>")
out.println("<tr>")
out.println("<td>")
out.println("city:")
out.println("</td>")
out.println("<td>")
out.println("<select name='dept'")
for(Integer i:key){
out.println("<option value='"+i+"'>"+dept.get(i)+"</option>")
}
out.println("</select>")
out.println("</td>")
out.println("<tr>")
out.println("</table>")
out.println("</form>")
out.println("</body>")
out.println("</html>")
out.flush()
}
}
例二:书写一个类用于统计当Web 应用启动后,网页被客户端访问的次数。如果重新启动Web 应用,计数器不会重新从1 开始统计访问次数,而是从上次统计的结果上进行累加。
在实际应用中,往往需要统计自Web 应用被发布后网页被客户端访问的次数,这就要求当Web 应用被终止时,计数器的数值被永久存储在一个文件中或者数据库中,等到Web 应用重新启动时,先从文件或数据库中读取计数器的初始值,然后在此基础上继续计数。
向文件中写入或读取计数器的数值的功能可以由自定义的 MyServletContextListener 类来完成,它具有以下功能:
1 、在 Web 应用启动时从文件中读取计数器的数值,并把表示计数器的 Counter 对象存放到 Web应用范围内。存放计数器的文件的路径为helloapp/count/count.txt 。
2 、在Web 应用终止时把Web 应用范围内的计数器的数值保存到count.txt 文件中。
Java代码
public class MyServletContextListener implements ServletContextListener{
public void contextInitialized(ServletContextEvent sce){
System.out.println("helloapp application is Initialized.")
// 获取 ServletContext 对象
ServletContext context=sce.getServletContext()
try{
// 从文件中读取计数器的数值
BufferedReader reader=new BufferedReader(
new InputStreamReader(context.
getResourceAsStream("/count/count.txt")))
int count=Integer.parseInt(reader.readLine())
reader.close()
// 创建计数器对象
Counter counter=new Counter(count)
// 把计数器对象保存到 Web 应用范围
context.setAttribute("counter",counter)
} catch(IOException e) {
e.printStackTrace()
}
}
public void contextDestroyed(ServletContextEvent sce){
System.out.println("helloapp application is Destroyed.")
// 获取 ServletContext 对象
ServletContext context=sce.getServletContext()
// 从 Web 应用范围获得计数器对象
Counter counter=(Counter)context.getAttribute("counter")
if(counter!=null){
try{
// 把计数器的数值写到 count.txt 文件中
String filepath=context.getRealPath("/count")
filepath=filepath+"/count.txt"
PrintWriter pw=new PrintWriter(filepath)
pw.println(counter.getCount())
pw.close()
} catch(IOException e) {
e.printStackTrace()
}
}
}
}
将用户自定义的 MyServletContextListener 监听器在 Servlet 容器进行注册, Servlet 容器会在启动或终止 Web 应用时,会调用该监听器的相关方法。在 web.xml 文件中, <listener>元素用于向容器注册监听器:
Xml代码
<listener>
<listener-class>ServletContextTest .MyServletContextListener<listener-class />
</listener>
通过上述两个例子,即可以非常清楚的了解到 ServletContextListener 接口的使用方法及技巧。
在Container 加载Web 应用程序时(例如启动 Container 之后),会呼叫contextInitialized() ,而当容器移除Web 应用程序时,会呼叫contextDestroyed
() 方法。
通过 Tomcat 控制台的打印结果的先后顺序,会发现当 Web 应用启动时,Servlet 容器先调用contextInitialized() 方法,再调用lifeInit 的init() 方法;
当Web 应用终止时,Servlet 容器先调用lifeInit 的destroy() 方法,再调用contextDestroyed() 方法。
由此可见,在Web 应用的生命周期中,ServletContext 对象最早被创建,最晚被销毁。
org.springframeword.web.cotext.ContextLoaderlistener
spring的监听器,并不是下载的。是在你自卜闷己下汪行的jar包型陵弯中copy到的路径的
在Java Web项目中,经常要在项目开始运行时启动一个线程,每隔一定的时间就老让敬运行一定的代码,比如扫描数据库的变化等等。要实现这个功能,可以现在侍慎web.xml文件中定义滑唤一个Listener,然后在这个Listener中启动一个线程,在线程里面实现功能。1. 自定义Listener
在Struts+Spring+Hibernate的Web项目中,web.xml里面一般都会有这样的代码:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
这几句代码使得Web项目的容器(也就是Web服务器,比如Tomcat)在项目启动时实例化了一个org.springframework.web.context.ContextLoaderListener类。
类似的,我们也可以在web.xml里面自己定义一个Listener,让Web服务器去实例化:
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>com.XXX.listener.WSListener</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
在以上的web.xml文件中,我们就让Web服务器在启动时实例化我们自己定义的com.XXX.listener.WSListener类(一般自己定义的Listener类要写在org.springframework.web.context.ContextLoaderListener的后面),然后在该类中去启动线程:
public class WSListener implements ServletContextListener{
private WSThread wsThread
@Overridepublic void contextDestroyed(ServletContextEvent event) {// TODO Auto-generated method stub
if (wsThread != null &&wsThread.isRunning){
wsThread.stopThread()
}
}
@Overridepublic void contextInitialized(ServletContextEvent event) {// TODO Auto-generated method stub
if (wsThread == null){
wsThread = new WSThread(event)
wsThread.start()
}
}
}
Listener类是由Web服务器管理的,当Web服务器启动时,将Listener类实例化并调用其contextInitialized(ServletContextEvent event)方法,当Web服务器关闭时,调用其contextDestroyed(ServletContextEvent event)方法,因此我们可以分别在这两个方法里面实现线程的启动和结束。
2. 在Spring容器以外获得其内部的Bean的实例的引用
被启动的线程用于间隔一定的时间扫描一次数据库,找出新增加的数据。在一般的Struts+Spring+Hibernate的Web项目中,Spring容器中的Bean是由Spring容器管理的,而我们这里启动的线程并不在Spring容器中,那么怎样获得Spring容器中Bean的实例的引用进而访问数据库呢?可以使用Spring的WebApplicationContextUtils工具类,该工具类获得Spring容器的引用,再获得其内部的Bean的实例的引用。
线程的代码:
public class WSThread extends Thread{public volatile boolean isRunning = true // 两次扫描之间休眠的时间
public long s_time private WebApplicationContext context private PropService propService private Prop prop private Temp2Service temp2Service private Temp2 temp2
private TempService tempService
ServletContextEvent event public WSThread(ServletContextEvent e){this.event = e this.context = WebApplicationContextUtils.getRequiredWebApplicationContext(event.getServletContext()) this.propService = (PropService) context.getBean("propService") this.temp2Service = (Temp2Service) context.getBean("temp2Service")
}
public void run(){while (isRunning){try {this.prop = propService.findByName("scan_time") this.s_time = Integer.parseInt(prop.getValue())*1000
sleep(s_time)
System.out.println("Run!!!!!!")
} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace()
}
}
}
public void stopThread(){
isRunning = false
}
public boolean isRunning(){return isRunning
}
}
在该线程的构造函数中,使用了从Listener传过来的ServletContextEvent变量,用该变量的getServletContext()方法,获取Web项目的servletContext,然后再以这个ServletContext作为参数,使用WebApplicationContextUtils的getRequiredWebApplicationContext()方法获取ApplicationContext对象,最后再通过ApplicationContext的getBean()方法获取到Bean的实例,实现对数据库的访问。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)