概念
servlet
servlet是一种运行在服务器端的java应用程序,它工作在服务器端和客户端的中间层,可动态生成web页面。
它的工作过程如下:
客户端发送请求到服务器端。
服务器将请求信息转发给servlet
servlet根据客户端的请求动态生成web
服务器端将响应返回给客户端
在 Web 应用程序中,一个 Servlet 在一个时刻可能被多个用户同时访问。这时 Web 容器将为每个用户创建一个线程来执行 Servlet。如果 Servlet 不涉及共享资源的问题,不必关心多线程问题。但如果 Servlet 需要共享资源,需要保证 Servlet 是线程安全的。
实例
拦截
package test;
import com.alibaba.fastjson.JSONObject;
import com.gcyl.pro.result.Message;
import com.gcyl.pro.result.Result;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ServletTest extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Result result = new Result();
result.setData(null);
result.setCode(Message.O3036);
result.setSuccess(false);
result.setMsg("处理失败:查看我的店铺订单详情店铺Id和订单Id必啊啊啊啊");
PrintWriter writer = resp.getWriter();
writer.append(JSONObject.toJSONString(result));
}
}
<servlet>
<servlet-name>serletTest</servlet-name>
<servlet-class>test.ServletTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>serletTest</servlet-name>
<url-pattern>/myEntityShopOrder/buyOrderIsPayDetail</url-pattern>
</servlet-mapping>
在调用Controller之前调用了它,并直接返回结果。
重定向
package test;
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 ServletTest extends HttpServlet {
// @Override
// protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// resp.sendRedirect("/myEntityShopOrder/buyOrderIsPayDetail?shopId=155&orderId=100");
// }
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/myEntityShopOrder/test?shopId=1");
}
}
@RequestMapping("/test")
@ResponseBody
public Result test(Long shopId) {
Result result = new Result();
result.setData(shopId);
result.setCode(Message.M0001);
result.setSuccess(true);
logger.debug("处理完成");
return result;
}
<servlet>
<servlet-name>serletTest</servlet-name>
<servlet-class>test.ServletTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>serletTest</servlet-name>
<url-pattern>/myEntityShopOrder/buyOrderIsPayDetail</url-pattern>
</servlet-mapping>
filter
filter是一个可以复用的代码,可以用来转换http请求、响应和头信息。过滤器filter是实现了javax.servlet.filter接口的服务端程序,主要是过滤字符编码、做一些业务逻辑判断等。
它的工作原理是,只要你在web.xml里面配置好,它就会根据你的要求拦截请求,
此时你就可以对请求或者响应(request、response)统一设置编码,简化操作;同时可以进行逻辑判断,如是否已经登录、有没有权限访问该页面等等工作。它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁。
它与Servlet的区别在于:它不能直接向用户生成响应。完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
实现filter只需两个步骤:
- 实现javax.servlet.filter接口,创建filter类
- 在web.xml里面配置好
实例
下面先介绍一个简单的记录日志的Filter,这个Filter负责拦截所有的用户请求,并将请求的信息记录在日志中。
public class LogFilter implements Filter
{
//FilterConfig可用于访问Filter的配置信息
private FilterConfig config;
//实现初始化方法
public void init(FilterConfig config)
{
this.config = config;
}
//实现销毁方法
public void destroy()
{
this.config = null;
}
//执行过滤的核心方法
public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain)throws IOException,ServletException
{
//---------下面代码用于对用户请求执行预处理---------
//获取ServletContext对象,用于记录日志
ServletContext context = this.config.getServletContext();
long before = System.currentTimeMillis();
System.out.println("开始过滤...");
//将请求转换成HttpServletRequest请求
HttpServletRequest hrequest = (HttpServletRequest)request;
//记录日志
context.log("Filter已经截获到用户的请求地址: " + hrequest.getServletPath());
//Filter只是链式处理,请求依然放行到目的地址
chain.doFilter(request, response);
//---------下面代码用于对服务器响应执行后处理---------
long after = System.currentTimeMillis();
//记录日志
context.log("过滤结束");
//再次记录日志
context.log("请求被定位到" + hrequest.getRequestURI() + "所花的时间为: " + (after - before));
}
}
在web.xml文件中我们需要对其需要拦截的请求配置监听范围,或者说过滤哪些url。
<filter>
<filter-name>logfilter</filter-name>
<filter-class>com.mine.test.LogFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>logfilter</filter-name>
<url-pattern>/*</url-pattern> <!--配置过滤的范围 后缀符合即过滤 此处为全部过滤-->
</filter-mapping>
listener
listener是Servlet的监听器,监听客户端的请求、服务端的响应等。就是在application,session,request三个对象创建消亡或者往其中添加修改删除属性时自动执行代码的功能组件。比如当增加一个HttpSession时,就激发sessionCreated(HttpSessionEvent se)方法,这样就给在线人数加一。
实例
下面我们开发一个具体的例子,这个监听器能够统计在线的人数。在ServletContext初始化和销毁时,在服务器控制台打印对应的信息。当ServletContext里的属性增加、改变、删除时,在服务器控制台打印对应的信息。
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class OnlineUserListener implements HttpSessionListener,
ServletContextListener, ServletContextAttributeListener {
private long onlineUserCount = 0;
public long getOnlineUserCount() {
return onlineUserCount;
}
@Override
public void attributeAdded(ServletContextAttributeEvent arg0) {
}
@Override
public void attributeRemoved(ServletContextAttributeEvent arg0) {
}
@Override
public void attributeReplaced(ServletContextAttributeEvent attributeEvent) {
System.err.println("...attributeReplaced...");
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
}
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
onlineUserCount ++;
toUpdateCount(httpSessionEvent);
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
onlineUserCount --;
toUpdateCount(httpSessionEvent);
}
private void toUpdateCount(HttpSessionEvent httpSessionEvent){
httpSessionEvent.getSession().setAttribute("onlineUserCount", onlineUserCount);
}
}
<listener>
<listener-class>com.ee.listener.OnlineUserListener</listener-class>
</listener>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>主页</title>
</head>
<body>
<h4>你好!</h4>
在线人数:<h1><%=request.getSession().getAttribute("onlineUserCount") %></h1>
</body>
</html>
生命周期
启动关闭后看控制台输出结果得出:
- 在Tomcat(Servlet容器)启动时,Listener和 ServletContext 最先初始化。因为最先输出的日志是:
18-Jun-2017 20:22:14.271 INFO [RMI TCP Connection(3)-127.0.0.1] edu.shao.webapp.sample.listener.MyServletContextListener.contextInitialized Servlet context initialized.
- Filter在Tomcat(Servlet容器)启动时初始化。
18-Jun-2017 20:22:14.274 INFO [RMI TCP Connection(3)-127.0.0.1] edu.shao.webapp.sample.filter.LogFilter.init Log Filter initialized.
18-Jun-2017 20:22:14.275 INFO [RMI TCP Connection(3)-127.0.0.1] edu.shao.webapp.sample.filter.ResponseFilter.init Response Filter initialized.
- 如果某个Servlet配置了 1 ,该Servlet也是在Tomcat(Servlet容器)启动时初始化。(如果Servlet没有配置1 ,该Servlet不会初始化)例如 EarlyServlet:
18-Jun-2017 20:22:14.281 INFO [RMI TCP Connection(3)-127.0.0.1] edu.shao.webapp.sample.EarlyServlet.init Early Servlet initialized.
- 每次请求, Request都会被初始化,响应请求后,请求被销毁,filter在其间做一些操作(filter只需一次初始化):
18-Jun-2017 20:22:14.902 INFO [http-apr-8888-exec-2] null.null Servlet request initialized.
18-Jun-2017 20:22:14.915 INFO [http-apr-8888-exec-2] edu.shao.webapp.sample.filter.LogFilter.doFilter Intercept Url=/
18-Jun-2017 20:22:16.077 INFO [http-apr-8888-exec-2] edu.shao.webapp.sample.filter.LogFilter.doFilter 127.0.0.1: time used :1160
18-Jun-2017 20:22:16.077 INFO [http-apr-8888-exec-2] edu.shao.webapp.sample.listener.MyServletRequestListener.requestDestroyed Servlet request destroyed.
- 如果Servlet没有配置1 ,该Servlet不会在Tomcat启动时初始化,而是在请求到来时初始化。例如 HelloServlet:
2013-03-28 18:14:34,714 DEBUG - [HelloServlet - init] Hello Servlet initialized.
- 关闭Tomcat时,Servlet、Filter、Listener、ServletContext依次被注销。
18-Jun-2017 23:07:44.767 INFO [localhost-startStop-1] null.null Early Servlet destroyed.
18-Jun-2017 23:07:44.768 INFO [localhost-startStop-1] edu.shao.webapp.sample.filter.LogFilter.destroy Log Filter destroyed.
18-Jun-2017 23:07:44.768 INFO [localhost-startStop-1] edu.shao.webapp.sample.filter.ResponseFilter.destroy Response Filter destroyed.
18-Jun-2017 23:07:44.783 INFO [localhost-startStop-1] edu.shao.webapp.sample.listener.MyServletContextListener.contextDestroyed Servlet context destroyed.
区别
- servlet 流程是短的,url传来之后,就对其进行处理,之后返回或转向到某一自己指定的页面。它主要用来在 业务处理之前进行控制.
filter 流程是线性的, url传来之后,检查之后,可保持原来的流程继续向下执行,被下一个filter, servlet接收等,而servlet 处理之后,不会继续向下传递。filter功能可用来保持流程继续按照原来的方式进行下去,或者主导流程,而servlet的功能主要用来主导流程。
filter可用来进行字符编码的过滤,检测用户是否登陆的过滤,禁止页面缓存等
servlet,filter都是针对url之类的,而listener是针对对象的操作的,如session的创建,session.setAttribute的发生,在这样的事件发生时做一些事情。可用来进行:Spring整合Struts,为Struts的action注入属性,web应用定时任务的实现,在线人数的统计等