监听器(listener)
介绍
- 监听器来自于servlet规范中
- 监听器专门用于监听【域对象生命周期变化】以及【域对象共享数据变化情况】
- 监听器接口实现类,必须由开发人员负责实现
上面提到监听器是监听域对象生命周期变化以及域对象共享数据变化情况,那么什么是域对象呢?
域对象
定义
在某一个范围之内,可以为servlet之间提供共享数据的对象。
分类
- ServletContext application:全局作用域对象,是在tomcat运行期间,可以为当前工程中所有的servlet提供共享数据
- HTTPSession session:会话作用域对象,在一次会话过程中,为本次参与会话的servlet提供共享数据
- HttpServletRequest request: 请求作用域对象,在一次请求处理过程中,比如【请求转发】,为参与本次请求的所有servlet提供共享数据
监听器实现步骤
- 根据监听的对象,选择一个合适的监听器接口来实现
- 重写监听器接口中的监听处理方法
- 在web.xml中注册监听器,通知tomcat
小案例(监听ServletContext的生命周期)
写一个类实现ServletContextListener接口,也就是监听ServletContext生命周期的变化(不同的接口有不同的作用),这里是重写了初始化和销毁的方法
@javax.servlet.annotation.WebListener()
public class OneListener implements ServletContextListener {
@Override
// 在application被初始化时被调用
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("全局作用域对象被初始化");
}
@Override
// 在application被销毁时被调用
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("全局作用域对象被销毁");
}
}
在上面的Listener中可以看到有一个@javax.servlet.annotation.WebListener(),这个注解就是声明该类是一个监听器,如果已经有了这个注解那么就不需要在下面的xml中进行配置,如果没有那个注解就按照下面的方式进行配置即可。 在web.xml中注册监听器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--监听器的注册,当我们程序启动时tomcat会帮我们创建监听器对象,创建的时机是在全局作用域被创建之前-->
<listener>
<listener-class>top.listener.OneListener</listener-class>
</listener>
</web-app>
小案例(监听ServletContext数据的变化)
按照上面监听ServletContext数据变化使用的接口就是ServletContextAttributeListener 如果是要看session的就是SessionAttributeListener
如果要看Request就是RequestAttributeListener
上面说监听器可以监听【域对象共享数据变化情况】,数据变化主要有三种情形:
新增共享数据
application.setAttribute("key", 100);
更新共享数据(key已经存在时就是更新)
application.setAttribute("key", 100);
删除共享数据
application.removeAttribute("key", 100);
新建一个类来实现ServletContextAttributeListener接口
package top.one.listener;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
@javax.servlet.annotation.WebListener()
public class TestListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
System.out.println("监听共享数据的新增");
}
@Override
public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
System.out.println("监听共享数据的移除");
}
@Override
public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
System.out.println("监听共享数据的更新");
}
}
由于上面的类上面有一个@javax.servlet.annotation.WebListener()注解所以可以不用再web.xml中注册监听器了
写一个servlet
package top.one.listener;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TestServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = req.getServletContext();
servletContext.setAttribute("key", 100);
servletContext.setAttribute("key", 200);
servletContext.removeAttribute("key");
}
}
在web.xml中配置servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>testServletContext</servlet-name>
<servlet-class>top.one.listener.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>testServletContext</servlet-name>
<url-pattern>/first.do</url-pattern>
</servlet-mapping>
</web-app>
启动项目访问 http://localhost:8080/ServletContext_war_exploded/first.do 即可看到下面的效果:
访问地址
上面的访问地址ip:端口以及后面的first.do(这个就是url-pattern里面配置的)都好说,那个ServletContext_war_exploded是怎么来的呢?其中这里是我们将项目部署到tomcat中时默认的访问路径名: 前提条件是我们已经在idea中配置好了tomcat,然后点击控制台那一块的application Servers
在左边有一个笔形的图标,那里是edit configuration,点击后出现下面的画面:
然后选择我们要部署的项目即可:
可以看到后面有一个ApplicationContext的路径,那里可以自己修改。
监听器的应用
jDBC的使用步骤(mysql版)
- 加载数据库驱动(好像在5点几之后这一步不用自己写,在jar中已经帮我们注册了)
Class.forName("com.mysql.jdbc.Driver");
- 获取连接
这里的url是告诉jdbc程序要连接哪一个数据库,常用的mysql和oracle的url分别如下:Connection conn = DriverManage.getConnection(url, username, password);
- mysql: jdbc:mysql://ip:端口/数据库名 ,如果是本地的url可能是 jdbc:mysql://localhost:3306/数据库名
- oracle: jdbc:oracle:thin@ip:端口:数据库名 ,如果是本地的url可能是jdbc:oracle:thin:@localhost:1521:数据库名
- sql预编译
这里不使用Statement对象是因为Statement不能防止sql注入问题。String sql = "select name, password from users where id=? "; PrepareStatement ps = conn.prepareStatement(sql);
- 执行sql语句
ps.setString(1, '1'); ps.executeQuery(); // 这里不需要传入sql
- 获取结果
ResultSet rs = ps.executeQuery(); while(rs.next()){ String name = rs.getString(2); // 这里的2表示name字段在数据库中是第二列 String name = rs.getString("name"); // 这种也是可以的,直接获取name字段的值 }
- 关闭连接 主要是有ResultSet的关闭,PrepareStatement的关闭以及Connection的关闭
具体应用
上面jdbc连接数据库的例子中,获取连接和关闭连接的操作时最耗时的,解决频繁的获取连接和关闭连接的方法是使用了连接池,也就是在项目启动时就创建好了一些连接,这样当需要使用的时候直接从连接池中去获取,而不用在使用的时候去创建连接,这样速度就提高了很多,但是这些连接总是需要在某一个时刻创建和销毁,上面说是在项目启动的时候创建,在项目关闭的时候关闭这些连接,那么怎么知道项目启动和项目关闭的时刻呢?这时候就需要使用ServletContextListener监听器了。因为该监听器可以监听项目的生命周期的变化,自然可以知道项目的启动和结束的时刻。