JavaWeb详解(第三篇)之Servlet基础简介-过滤器、监听器
JavaWeb的三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器。前面我们学习了Servlet ,接下来我们在了解一下Filter 过滤器和Listener 监听器。
1、Filter 过滤器
1.1、什么是Filter 过滤器
Filter 过滤器它是 JavaEE 的规范。也就是接口。可以对指定URL请求进行拦截,拦截之后在过滤器中进行相应的处理/判断。
Filter 过滤器它的作用是:拦截请求,过滤响应。
拦截请求常见的应用场景有: 1)权限检查;2)日记操作 ;3)事务管理...等等。
1.2、Filter 入门
案例描述:在首页,访问个人信息页面,如果没有登陆则不允许访问。
1)编写一个类去实现Filter 接口
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public class FilterDemo implements Filter{
/**
* 用于拦截请求
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpSession session = httpServletRequest.getSession();
Object user = session.getAttribute("user");
// 如果等于 null,说明还没有登录:则不允许访问私人页面
if (user == null) {
request.getRequestDispatcher("/login.jsp").forward(request, response);
return;
} else {
// 让程序继续往下访问用户的目标资源:如果没有其他的过滤器,将访问请求的资源;如果有过滤器则继续进入其他过滤器
chain.doFilter(request,response);
}
}
}
2)编写一个InfoServlet
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class InfoServlet extends HttpServlet{
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=gbk");
PrintWriter out = response.getWriter();
out.println("私人空间,请谨慎查看!");
}
}
3)修改index.jsp主页
<body>
<div>我是页头</div>
<a href="/persion/infoServlet">查看我的信息</a>
<div>我是页尾</div>
</body>
4)配置web.xml
<!--filter 标签用于配置一个 Filter 过滤器-->
<filter>
<filter-name>FilterDemo</filter-name>
<filter-class>com.cn.filterAndListener.FilterDemo</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo</filter-name>
<!-- 匹配规则同servlet,支持/*也支持*.do
.../persion/infoServlet 访问此链接会被拦截
-->
<url-pattern>/persion/infoServlet</url-pattern>
</filter-mapping>
案例效果:
成功拦截了,但是我们发现有一个问题。我之前的登陆页面转发过去,怎么验证码不显示了呢?一查,发现路径好像不对:
扩展:补充说明base标签的作用
public class FilterDemo implements Filter{
/**
* 用于拦截请求
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpSession session = httpServletRequest.getSession();
Object user = session.getAttribute("user");
// 如果等于 null,说明还没有登录:则不允许访问私人页面
if (user == null) {
//注:"/" 跟目录 request.getRequestDispatcher("/"),表示当前工程http://localhost:8080/
//当我使用/login.jsp,确实可以转发到登陆页面,但是此时的登陆页面的url:http://localhost:8080/persion/infoServlet
request.getRequestDispatcher("/login.jsp").forward(request, response);
return;
} else {
// 让程序继续往下访问用户的目标资源:如果没有其他的过滤器,将访问请求的资源;如果有过滤器则继续进入其他过滤器
chain.doFilter(request,response);
}
}
}
<!--把src目录复制下来-->
<img id="codeimg" style="width: 60px;height:20px;" src="ImageServlet?0.14947579772812647">
跳转的链接是:
http://localhost:8080/persion/ImageServlet?0.14947579772812647
我们没有在login.jsp中声明<base>这里的base路径默认是http://localhost:8080/persion/
<!--base 标签设置页面相对路径工作时参照地址的地址 href 属性就是参数的地址值 -->
所以我们得到的是上诉地址,导致验证码无法显示
把js中的代码改成如下:发现正常了,但是这样做并不好,业务我们根本不知道从第几层,这种做法不通用
window.document.getElementById("codeimg").src="../"+url+"?"+Math.random();
我们在login.jsp页面中加入base设置页面相对路径工作时参照地址
<base href="/">
最终我们看到:
1.3、Filter 应用
1.3.1、过滤器解决中文乱码问题
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
//全拦截先设置一波字符编码
request.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
2、Listener 监听器
2.1、什么是监听器
如果你属性JAVA的GUI图形界面,应该接触过很多监听器。比如:监听键盘事件、按钮的点击事件,然后触发某种动作。这就是监听器的作用:无需主动调用,自动触发。
监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。
在Servlet规范中定义了多种类型的监听器:
1)监听对象的创建和销毁
a)ServletRequestListener:可以在请求时获得通知
b)HttpSessionListener:可以用来收集在线者信息,或者更改会话列表时获得通知
c)ServletContextListener:可以获取web.xml里面的参数配置
2)监听对象属性变化
a)ServletContextAttributeListener
b)HttpSessionAttributeListener
c)ServletRequestAttributeListener
3)监听Session对象
a)HttpSessionBindingListener
b)HttpSessionActivationListener
2.2、监听器入门
1)新建一个ListenerDemo
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class ListenerDemo implements HttpSessionListener,HttpSessionAttributeListener{
// 新建一个session时触发此操作
public void sessionCreated(HttpSessionEvent se) {
System.out.println("新建一个session");
}
// 添加session时触发此操作
public void attributeAdded(HttpSessionBindingEvent e) {
HttpSession session = e.getSession();
String name = (String) session.getAttribute(session.getAttributeNames().nextElement());
System.out.println("我可以把session添加到对象中,值为:"+name);
}
public void attributeRemoved(HttpSessionBindingEvent e) {
System.out.println("我可以把session从对象中移除");
}
public void attributeReplaced(HttpSessionBindingEvent e) {
System.out.println("替换");
}
}
2)修改xml
<listener>
<!-- <display-name>ListenerDemo</display-name> -->
<listener-class>com.cn.filterAndListener.ListenerDemo</listener-class>
</listener>
3)使用我们之前的案例:SessionDemo
案例效果:
注:三者的执行顺序:listener->Filter->servlet。简单记为:理(Listener)发(Filter)师(servlet)
3、其他知识补充
3.1、错误页面
1)新建ExceptionServletDemo
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ExceptionServletDemo extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int i = 9/0;
System.out.println("大兄弟,代码出错了啊!!!");
}
}
2)web.xml
<servlet>
<servlet-name>ExceptionServletDemo</servlet-name>
<servlet-class>com.cn.filterAndListener.ExceptionServletDemo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ExceptionServletDemo</servlet-name>
<url-pattern>/ExceptionServletDemo</url-pattern>
</servlet-mapping>
<!--配置捕错误时进入的页面-->
<error-page>
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/error.jsp</location>
</error-page>
<!--也可以配置-->
<error-page>
<error-code>404</error-code>
<location>/error.jsp</location>
</error-page>
案例效果:
3.2、上传下载
3.2.1、上传
需要:(SmartUpload.jar)包
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.jspsmart.upload.File;
import com.jspsmart.upload.Files;
import com.jspsmart.upload.Request;
import com.jspsmart.upload.SmartUpload;
import com.jspsmart.upload.SmartUploadException;
public class UploadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
String dirPath = this.getServletContext().getRealPath("/images");
// 接收客户端上传的文件
// 创建 SmartUpload 对象用于接收文件的上传
SmartUpload su = new SmartUpload();
// 通过 su 对象初始化 request 中的数据
su.initialize(this.getServletConfig(), request, response);
// 设置上床文件的限制信息
su.setAllowedFilesList("jpg,png,bmp,gif");
// 设置单个上传文件为2M
su.setMaxFileSize(2 * 1024 * 1024);
// 执行上传:上传之后,页面提交的数据都在 su 对象中
su.upload();
// 从 su 对象中获取数据(文件和字段)
// 获取到 su 中上传的文件
Files files = su.getFiles();
// 获取到包含所有文本字段的 request 对象
Request r = su.getRequest();
// 遍历files获取到所有上传的文件(允许批量上传)
for (int i = 0; i < files.getCount(); i++) {
File file = files.getFile(i);
file.saveAs(dirPath + "/" + file.getFileName());
}
// 从 r 中获取到需要的字段
String name = r.getParameter("userName");
String pass = r.getParameter("userPass");
String[] fns = new java.io.File(dirPath).list();
ArrayList<String> fileNames = new ArrayList<String>();
for (String f : fns) {
fileNames.add(f);
}
request.setAttribute("fileNames", fileNames);
request.getRequestDispatcher("list.jsp").forward(request, response);
} catch (SmartUploadException e) {
e.printStackTrace();
}
}
}
3.2.2、下载
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 接受客户端需要下载的文件名
String fileName = request.getParameter("fname");
// 获取到文件存放的目录
String dirPath = this.getServletContext().getRealPath("/images");
// 要下载的文件是
File f = new File(dirPath + "/" + fileName);
// 再将图片信息发送给客户端之前,设置响应信息的格式
// 设置相应数据的格式为浏览器无法解析的格式
response.setContentType("application/pdf");
// 设置文件的头信息
response.setHeader("Content-Disposition", "attachment;filename="
+ fileName);
// 设置响应数据的大小
response.setContentLength((int) f.length());
// 开始下载
FileInputStream fis = new FileInputStream(f);
OutputStream out = response.getOutputStream();
byte[] buff = new byte[102400];
int len = -1;
while ((len = fis.read(buff)) != -1) {
out.write(buff, 0, len);
out.flush();
}
out.close();
}
}