Servlet、Filter
加载顺序是:context-param -> listener -> filter -> servlet ,而同个类型之间的实际程序调用的时候的顺序是根据对应的 mapping 的顺序进行调用的。
下面从几个方面阐述一下题目中四个概念的区别与联系:
1、概念
2、生命周期
3、职责
4、执行过程
一、概念:
1、servlet:servlet是一种运行服务器端的Java应用程序,具有独立于平台和协议的特性,并且可以动态的生成web页面,它工作在客户端请求与服务器响应的中间层。
2、filter:filter是一个可以复用的代码片段,可以用来转换HTTP请求、响应和头信息。
Filter不像Servlet,它不能产生一个请求或者响应,它只是修改对某一资源的请求,或者修改从某一的响应。
3、listener:监听器,从字面上可以看出listener主要用来监听只用。通过listener可以监听web服务器中某一个执行动作,并根据其要求作出相应的响应。通俗的语言说就是在application,session,request三个对象创建消亡或者往其中添加修改删除属性时自动执行代码的功能组件。
4、interceptor:是在面向切面编程的,就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法,比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
5、servlet、filter、listener是配置到web.xml中,interceptor不配置到web.xml中,struts的拦截器配置到struts.xml中。spring的拦截器配置到spring.xml中。
二、生命周期:
1、servlet:servlet的生命周期始于它被装入web服务器的内存时,并在web服务器终止或重新装入servlet时结束。servlet一旦被装入web服务器,一般不会从web服务器内存中删除,直至web服务器关闭或重新结束。
(1)、装入:启动服务器时加载Servlet的实例;
(2)、初始化:web服务器启动时或web服务器接收到请求时,或者两者之间的某个时刻启动。初始化工作有init()方法负责执行完成;
(3)、调用:从第一次到以后的多次访问,都是只调用doGet()或doPost()方法;
(4)、销毁:停止服务器时调用destroy()方法,销毁实例。
2、filter:(一定要实现javax.servlet包的Filter接口的三个方法init()、doFilter()、destroy(),空实现也行)
(1)、启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;
(2)、每一次请求时都只调用方法doFilter()进行处理;
(3)、停止服务器时调用destroy()方法,销毁实例。
3、listener:类似于servlet和filter
web.xml 的加载顺序是:context- param -> listener -> filter -> servlet
4、interceptor:以struts的拦截器为例,加载了struts.xml以后,初始化相应拦截器。当action请求来时调用intercept方法,服务器停止销毁interceptor。
三、职责
1、servlet:
创建并返回一个包含基于客户请求性质的动态内容的完整的html页面;
创建可嵌入到现有的html页面中的一部分html页面(html片段);
读取客户端发来的隐藏数据;
读取客户端发来的显示数据;
与其他服务器资源(包括数据库和java的应用程序)进行通信;
通过状态代码和响应头向客户端发送隐藏数据。
2、filter:
filter能够在一个请求到达servlet之前预处理用户请求,也可以在离开servlet时处理http响应:
在执行servlet之前,首先执行filter程序,并为之做一些预处理工作;
根据程序需要修改请求和响应;
在servlet被调用之后截获servlet的执行
3、listener:职责如概念。
servlet2.4规范中提供了8个listener接口,可以将其分为三类,分别如下:
第一类:与servletContext有关的listner接口。包括:ServletContextListener、ServletContextAttributeListener
第二类:与HttpSession有关的Listner接口。包括:HttpSessionListner、HttpSessionAttributeListener、HttpSessionBindingListener、 HttpSessionActivationListener;
第三类:与ServletRequest有关的Listener接口,包括:ServletRequestListner、ServletRequestAttributeListener
4、interceptor:与过滤器十分相似,通过层层拦截,处理用户的请求和响应。
备注:web.xml 的加载顺序是:context-param -> listener -> filter -> servlet 。了解了这几个概念的区别以后,不难理论这个加载顺序了。
四、几个区别:
1,servlet 流程是短的,url传来之后,就对其进行处理,之后返回或转向到某一自己指定的页面。它主要用来在 业务处理之前进行控制.
2,filter 流程是线性的, url传来之后,检查之后,可保持原来的流程继续向下执行,被下一个filter, servlet接收等,而servlet 处理之后,不会继续向下传递。
filter功能可用来保持流程继续按照原来的方式进行下去,或者主导流程,而servlet的功能主要用来主导流程。
filter可用来进行字符编码的过滤,检测用户是否登陆的过滤,禁止页面缓存等
3, servlet,filter都是针对url之类的,而listener是针对对象的操作的,如session的创建,session.setAttribute的发生,在这样的事件发生时做一些事情。
service返回JSON
/** * 在Servlet返回JSON数据 */ @WebServlet("/json.do") public class JsonServlet extends HttpServlet { private static final long serialVersionUID = 7500835936131982864L; /** * 返回json格式数据 */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Map<String, Object> data = new HashMap<String, Object>(); data.put("date", new Date()); data.put("email", "accountwcx@qq.com"); data.put("age", 30); data.put("name", "csdn"); data.put("array", new int[]{1,2,3,4}); ResponseJsonUtils.json(response, data); } }
/** * Servlet返回JSONP格式数据 */ @WebServlet("/jsonp.do") public class JsonpServlet extends HttpServlet { private static final long serialVersionUID = -8343408864035108293L; /** * 请求会发送callback参数作为回调函数,如果没有发送callback参数则使用默认回调函数 */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //客户端发送的回调函数 String callback = request.getParameter("callback"); Map<String, Object> data = new HashMap<String, Object>(); data.put("date", new Date()); data.put("email", "accountwcx@qq.com"); data.put("age", 30); data.put("name", "csdn"); data.put("array", new int[]{1,2,3,4}); if(callback == null || callback.length() == 0){ //如果客户端没有发送回调函数,则使用默认的回调函数 ResponseJsonUtils.jsonp(response, data); }else{ //使用客户端的回调函数 ResponseJsonUtils.jsonp(response, callback, data); } } }
import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServletResponse; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; /** * * Web服务端返回JSON工具类 * 工具类依赖FastJSON * 工具类支持返回JSON和JSONP格式数据 * @author accountwcx@qq.com * */ public class ResponseJsonUtils { /** * 默认字符编码 */ private static String encoding = "UTF-8"; /** * JSONP默认的回调函数 */ private static String callback = "callback"; /** * FastJSON的序列化设置 */ private static SerializerFeature[] features = new SerializerFeature[]{ //输出Map中为Null的值 SerializerFeature.WriteMapNullValue, //如果Boolean对象为Null,则输出为false SerializerFeature.WriteNullBooleanAsFalse, //如果List为Null,则输出为[] SerializerFeature.WriteNullListAsEmpty, //如果Number为Null,则输出为0 SerializerFeature.WriteNullNumberAsZero, //输出Null字符串 SerializerFeature.WriteNullStringAsEmpty, //格式化输出日期 SerializerFeature.WriteDateUseDateFormat }; /** * 把Java对象JSON序列化 * @param obj 需要JSON序列化的Java对象 * @return JSON字符串 */ private static String toJSONString(Object obj){ return JSON.toJSONString(obj, features); } /** * 返回JSON格式数据 * @param response * @param data 待返回的Java对象 * @param encoding 返回JSON字符串的编码格式 */ public static void json(HttpServletResponse response, Object data, String encoding){ //设置编码格式 response.setContentType("text/plain;charset=" + encoding); response.setCharacterEncoding(encoding); PrintWriter out = null; try{ out = response.getWriter(); out.write(toJSONString(data)); out.flush(); }catch(IOException e){ e.printStackTrace(); } } /** * 返回JSON格式数据,使用默认编码 * @param response * @param data 待返回的Java对象 */ public static void json(HttpServletResponse response, Object data){ json(response, data, encoding); } /** * 返回JSONP数据,使用默认编码和默认回调函数 * @param response * @param data JSONP数据 */ public static void jsonp(HttpServletResponse response, Object data){ jsonp(response, callback, data, encoding); } /** * 返回JSONP数据,使用默认编码 * @param response * @param callback JSONP回调函数名称 * @param data JSONP数据 */ public static void jsonp(HttpServletResponse response, String callback, Object data){ jsonp(response, callback, data, encoding); } /** * 返回JSONP数据 * @param response * @param callback JSONP回调函数名称 * @param data JSONP数据 * @param encoding JSONP数据编码 */ public static void jsonp(HttpServletResponse response, String callback, Object data, String encoding){ StringBuffer sb = new StringBuffer(callback); sb.append("("); sb.append(toJSONString(data)); sb.append(");"); // 设置编码格式 response.setContentType("text/plain;charset=" + encoding); response.setCharacterEncoding(encoding); PrintWriter out = null; try { out = response.getWriter(); out.write(sb.toString()); out.flush(); } catch (IOException e) { e.printStackTrace(); } } public static String getEncoding() { return encoding; } public static void setEncoding(String encoding) { ResponseJsonUtils.encoding = encoding; } public static String getCallback() { return callback; } public static void setCallback(String callback) { ResponseJsonUtils.callback = callback; } }
import com.alibaba.fastjson.JSON; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { try { ...//业务逻辑 filterChain.doFilter(httpServletRequest, httpServletResponse); } catch (BadCredentialsException e) { httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value()); httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); ResultData<Object> result = new ResultData<>(GlobalCode.INVALID_IDENTITY); try (PrintWriter out = httpServletResponse.getWriter()) { out.write(JSON.toJSONString(result)); out.flush(); } }
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * Spring MVC返回JSON和JSONP数据 */ @Controller @RequestMapping("/json") public class JsonController { /** * 返回JSON数据 * @param request * @param response */ @RequestMapping("/json.do") public void json(HttpServletRequest request, HttpServletResponse response){ Map<String, Object> data = new HashMap<String, Object>(); data.put("date", new Date()); data.put("email", "accountwcx@qq.com"); data.put("age", 30); data.put("name", "csdn"); data.put("array", new int[]{1,2,3,4}); ResponseJsonUtils.json(response, data); } /** * 返回JSONP数据 * @param callback JSONP的回调函数 * @param request * @param response */ @RequestMapping("/jsonp.do") public void json(String callback, HttpServletRequest request, HttpServletResponse response){ Map<String, Object> data = new HashMap<String, Object>(); data.put("date", new Date()); data.put("email", "accountwcx@qq.com"); data.put("age", 30); data.put("name", "csdn"); data.put("array", new int[]{1,2,3,4}); if(callback == null || callback.length() == 0){ //如果客户端没有发送回调函数,则使用默认的回调函数 ResponseJsonUtils.jsonp(response, data); }else{ //使用客户端的回调函数 ResponseJsonUtils.jsonp(response, callback, data); } } }
import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ajun.exception.BsException; /** * 业务异常过滤器 * @author ajun * @http://blog.csdn.net/ajun_studio */ public class ExceptionFiler implements Filter { private String errorPage;//跳转的错误信息页面 public void destroy() { } public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; //捕获你抛出的业务异常 try { chain.doFilter(req, res); } catch (RuntimeException e) { if(e instanceof BsException){//如果是你定义的业务异常 request.setAttribute("BsException", e);//存储业务异常信息类 request.getRequestDispatcher(errorPage).forward(request, response);//跳转到信息提示页面!! } e.printStackTrace(); } } //初始化读取你配置的提示页面路径 public void init(FilterConfig config) throws ServletException { //读取错误信息提示页面路径 errorPage = config.getInitParameter("errorPage"); if(null==errorPage || "".equals(errorPage)){ throw new RuntimeException("没有配置错误信息跳转页面,请再web.xml中进行配置\n<init-param>\n<param-name>errorPage</param-name>\n<param-value>/error.jsp</param-value>\n </init-param>\n路径可以是你自己设定的任何有效路径页面!!"); //System.out.println("没有配置错误信息跳转页面"); } } }
Filter在web.xml中的配置如下:
<filter> <filter-name>ExceptionFilter</filter-name> <filter-class>com.ajun.filter.ExceptionFiler</filter-class> <init-param> <param-name>errorPage</param-name> <param-value>/error.jsp</param-value> </init-param> </filter> <filter-mapping> <filter-name>ExceptionFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping>