文章目录
过滤器和监听器是Servlet规范里面的两个高级特性,过滤器的作用就是通过对request、response的修改实现特定的功能,例如请求数据字符编码、IP地址过滤、异常过滤、用户身份认证等。监听器的作用就是监听Web程序中正在进行的程序,根据发生的时间做出特定的响应。
Servlet进阶API
在Web容器启动后,通过加载web.xml文件读取Servlet的配置信息,实例化Servlet类,并且为每个Servlet配置信息产生唯一的一个ServletConfig对象。在运行Servlet时,调用Servlet的接口init()方法,将产生的ServletConfig作为参数传入Servlet中。
应用程序事件与监听器
ServletContext事件、监听器
当Web应用程序启动时,会自动开始监听,首先调用的是contextInitialized()方法,并传入ServletContextEvent参数。在Web应用关闭时,会自动调用contextDestroyed()方法,同样传入ServletContextEvent参数。可以在contextInitialized()方法中实现Web应用的数据库连接、读取应用程序设置等;在contextDestroyed()中设置数据库资源的释放。
package com.shore;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;
import org.apache.log4j.Logger;
@WebListener
//给当前文件的路径设置为根目录下的GetReaderBody2
public class MyServletContextListener implements ServletContextListener{
private static Logger log = Logger.getLogger("MyServletContextListener");
public void contextInitialized(ServletContextEvent sce) {
//通过ServletContextEvent获得ServletContext对象
ServletContext context = sce.getServletContext();
String name = context.getInitParameter("user_name");
log.debug("初始化参数name的值"+name);
log.debug("Tomcat正在启动中......");
}
public void contextDestroyed(ServletContextEvent sce) {
log.debug("Tomcat正在关闭中......");
}
}
ServletRequestAttributeListener 被称为“ServletContext属性监听器”,可以用来监听Application属性的添加、移除和替换时相应的动作事件。
public void attributeAdded(ServletRequestAttributeEvent arg0) {
log.debug("加入一个request范围的属性,名称为:"+
arg0.getName()+",其值为:"+arg0.getValue());
}
public void attributeRemoved(ServletRequestAttributeEvent arg0) {
log.debug("移除一个request范围的属性,名称为:"+arg0.getName());
}
public void attributeReplaced(ServletRequestAttributeEvent arg0) {
log.debug("修改一个request范围的属性,名称为:"+
arg0.getName()+",修改前的值为:"+arg0.getValue());
HttpSession事件监控器
与HttpSession有关的监听器有5个:HttpSessionIdListener、HttpSessionListener、HttpSessionAttributeListener、HttpSessionBindingListener、HttpSessionActivationListener。
HttpSessionIdListener用来监听Session ID的变化。当Session ID发生变化时,会触发sessionIDChange()方法,并传入HttpSessionEvent和oldSessionId参数,可以使用HttpSessionEven中的getSession().getId()获取新的session ID,oldSessionId代表改变之前的session ID。
HttpSessionListener是“HttpSession生命周期监听器”,可以用来监听HttpSession对象初始化或者结束时相应的动作事件。当HttpSession对象初始化或者结束前,会自动调用sessionCreated()方法和sessionDestroyed()方法,并传入HttpSessionEven参数,可以通过HttpSessionEven的getSession()方法取得HttpSession对象。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML>
<html>
<head>
<title>用户登录</title>
</head>
<body>
<p>用户登录</p>
<form action="<%=path%>/<%--servletConfigDemo--%>Login.do" method="post">
<table border="1" width="250px;">
<tr>
<td width="75px;" >用户名:</td>
<td ><input name="userId"/></td>
</tr>
<tr>
<td width="75px;">密 码:</td>
<td ><input name="passwd" type="password"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交"/>
<input type="reset" value="重置"/>
</td>
</tr>
</table>
</form>
</body>
</html>
package com.shore;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet(
urlPatterns = {
"/Login.do" },
loadOnStartup = 0,
name = "Login",
displayName = "demo",
initParams = {
@WebInitParam(name = "success", value = "success.jsp")
}
)
public class Login extends HttpServlet {
Map<String, String> users;
public Login() {
users = new HashMap<String, String>();
users.put("zhangsan", "123456");
users.put("lisi", "123456");
users.put("wangwu", "123456");
users.put("zhaoliu", "123456");
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String userId = request.getParameter("userId");
String passwd = request.getParameter("passwd");
//匹配用户名与密码,如果一致记录加一
if (users.containsKey(userId) && users.get(userId).equals(passwd)) {
request.getSession().setAttribute("user", userId);
request.getSession().setAttribute("count",MyHttpSessionListener.getCount());
}
String success = getInitParameter("success");
response.sendRedirect(success);
}
}
package com.shore;
import jakarta.servlet.annotation.WebListener;
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;
@WebListener
public class MyHttpSessionListener implements HttpSessionListener{
private static int count;//统计数
public static int getCount() {
return count;
}
public void sessionCreated(HttpSessionEvent se) {
MyHttpSessionListener.count++;
}
public void sessionDestroyed(HttpSessionEvent se) {
MyHttpSessionListener.count--;
}
}
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
%>
<!DOCTYPE HTML>
<html>
<head>
<title>登录成功界面</title>
</head>
<body>
<h3>目前在线人数为:${sessionScope.count}</h3>
<h4>欢迎您:${sessionScope.user}</h4>
<a href="<%=path%>/logout.do?userId=${sessionScope.user}">注销</a>
</body>
</html>
HttpSessionAttributeListener是“HttpSection属性改变监听器”,可以用来监听HttpSession对象加入属性、移除属性或者替换属性时响应的动作事件。
HttpSessionBindingListener是“HttpSection对象绑定监听器”,可以用来监听HttpSession中设置成HttpSession属性或者从HttpSession中移除时得到session通知。
HttpSessionActivationListener是“HttpSection对象转移监听器”,可以用来实现它对同一会话在不同的JVM中转移。
HttpServletRequest事件、监听器
HttpServletRequest有关的监听器有两个:ServletRequestListener、
ServletRequestAttributeListener。
ServletRequestListener是“Request生命周期监听器”,可以用来监听Request对象初始化或者结束时响应的动作事件。在request对象初始化或者结束前,会自动调用requestInitialized()和requestDestroyed()方法。
ServletRequestAttributeListener是“Request属性改变监听器”,可以用来监听Request对象加入属性、移除属性或者替换属性时响应的动作事件。
过滤器
在Web应用程序中,它即可以拦截过滤浏览器的请求,也可以改变对浏览器的响应。
请求封装器:是指利用HttpServletRequestWrapper类将请求中的内容进行同意的修改,例如:修改请求字符编码、替换字符、权限验证等。
package com.shore;
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
@WebFilter(
asyncSupported = true,
description = "字符编码过滤器",
filterName = "encodingFilter",
urlPatterns = {
"/*" },
initParams = {
@WebInitParam(name = "encoding", value = "UTF-8")
}
)
public class EncodingFilter implements Filter{
private static Logger log = Logger.getLogger("EncodingFilter");
private String encoding=null;
private String filterName="";
public void init(FilterConfig filterConfig) throws ServletException {
//通过filterConfig获得初始化中的编码值
encoding = filterConfig.getInitParameter("encoding");
filterName = filterConfig.getFilterName();
if(encoding==null||"".equals(encoding)){
encoding="UTF-8";
}
log.debug("获得编码值");
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//分别对请求和响应进行编码设置
HttpServletRequest req = (HttpServletRequest)request;
log.debug("请求被"+filterName+"过滤");
//上述编码过滤器对于post请求是没有问题的,但是对于get请求获取中文参数时还是会出现乱码问题。基于这种情况
//可以到请求封装器HttpServletRequestWrapper包装请求,将字符转换的工作添加到getParameter()方法中
//这样就可以对请求的参数进行统一的转换
if("GET".equals(req.getMethod())){
req = new RequestEncodingWrapper(req,encoding);
}else{
req.setCharacterEncoding(encoding);
}
response.setContentType("text/html;charset="+encoding);
//传输给过滤器过滤
chain.doFilter(req, response);
log.debug("响应"+filterName+"过滤");
}
public void destroy() {
log.debug("请求销毁");
}
}
package com.shore;
import java.io.UnsupportedEncodingException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
public class RequestEncodingWrapper extends HttpServletRequestWrapper{
private String encoding="";
public RequestEncodingWrapper(HttpServletRequest request) {
//必须调用父类构造方法
super(request);
}
public RequestEncodingWrapper(HttpServletRequest request,String encoding) {
//必须调用父类构造方法
super(request);
this.encoding = encoding;
}
//重新定义getParameter方法
public String getParameter(String name){
String value = getRequest().getParameter(name);
try {
//将参数值进行编码
if(value!=null&&!"".equals(value))
value = new String(value.trim().getBytes("ISO-8859-1"), encoding);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return value;
}
}
响应封装器:响应封装器是指利用HttpServletResponseWrapper类将响应中的内容进行统一修改,例如压缩输出内容、替换输出内容等。
异步处理
对于每个请求,该Servlet会取得异步信息AsyncContext,同时释放占用内存、延迟响应,然后启动AsyncRequest对象定义的线程,在该线程中进行业务处理,等业务处理完成后,输出页面信息并调用AsyncContext的complete()方法表示异步完成。
package com.shore;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import jakarta.servlet.AsyncContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet(
asyncSupported = true,
urlPatterns = {
"/asyncdemo.do" },
name = "myAsyncServlet"
)
public class MyAsyncServlet extends HttpServlet {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("开始时间" + sdf.format(new Date()) + " <br/> ");
out.flush();
//在子线程中执行业务调用,并由其负责输出响应,主线程推出
AsyncContext asyncContext = request.startAsync(request,response);
asyncContext.setTimeout(900000000);//设置最大超时时间
new Thread(new Executor(asyncContext)).start();
out.println("结束时间" + sdf.format(new Date())+ " <br/> ");
out.flush();
}
//内部类
public class Executor implements Runnable {
private AsyncContext ctx = null;
public Executor(AsyncContext ctx){
this.ctx = ctx;
}
public void run(){
try {
//等待20秒以模拟业务方法的执行
Thread.sleep(20000);
PrintWriter out = ctx.getResponse().getWriter();
out.println("业务处理完毕的时间" + sdf.format(new Date()) + " <br/> ");
out.flush();
ctx.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}