分享

Servlet起步

 yespon 2017-03-13

什么是Servlet

Servlet是sun公司制定的用来扩展web服务器功能的组件规范,通俗理解为遵循Servlet规范开发的实现了某个功能的Java组件。该组件没有 main 方法,不能独立地运行,只能在Servlet容器中运行,容器管理其从创建到销毁的整个过程。

早期web服务器(Apache)不能处理动态页面,为了扩展该功能,web服务器将请求发送给帮助程序(tomcat)处理。tomcat就是Servlet容器, WEB-INF目录下的web.xml部署描述符文件是web应用的配置文件,容器根据该配置来指定Servlet处理具体请求。

Web请求的过程

  1. 浏览器依据ip、port与服务器建立连接
  2. 浏览器将相关数据(如请求参数)打包,然后发送请求
  3. web服务器的通信模块解析请求数据包,发送给Serlvet容器。容器将解析的数据封装到request(HttpServletRequest)对象中,同时创建一个response(HttpServletResponse)对象。
  4. 容器依据请求路径找到Servlet类,加载class文件并创建Servlet对象(如果已经存在则跳过)。然后调用该对象的service()方法,将request(可以获取请求中所有的数据)和response(可以封装服务器的响应数据)作为参数传递进去,执行业务逻辑。
  5. 容器读取response中的处理结果,然后将处理结果发送给通信模块,通信模块将数据打包发送给浏览器。
  6. 浏览器解析响应数据包,生成响应的页面。
  7. WEB应用程序停止时,Servlet容器将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。 

简单来说就是tomcat容器通过web.xml文件中的找到对应的servlet,然后调用service()方法处理浏览器请求。 

开发servlet步骤

  1.在web容器中配置url映射

复制代码
servlet> servlet-name>servletTestservlet-name> servlet-class>com.servlet.servletTestservlet-class>servlet>servlet-mapping> servlet-name>servletTestservlet-name> url-pattern>/servlet/testurl-pattern>servlet-mapping>
复制代码

  2.开发servlet类

定义一个servlet,继承HttpServlet(能够处理HTTP请求),然后覆写doGet/doPost或覆写service()方法。

抽象类Httpservlet同时实现了service、doGet和doPost方法,其中service方法会根据HTTP请求自动调用相应的doxxx方法(默认实现为向客户端返回一个错误)。所以实际开发中如果service方法不需要处理业务逻辑,则只需重写相应的doxxx方法(向客户端发送数据),不用重写service方法。如果需要service处理业务逻辑而重写了service方法,则里面必须包括相应的转发逻辑(转发到其他Servlet组件或调用doxxx)。

复制代码
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 Servlettest extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType('text/html'); PrintWriter out = response.getWriter(); ServletCofig config = this.getServletConfig(); //可以直接调用 out.println(''); out.println(''); out.println(' A Servlet'); out.println(' '); out.print(' This is '); out.print(this.getClass()); out.println(', using the GET method'); out.println(' '); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); }}
复制代码

 

一个servlet处理多个请求

此时servlet充当控制器的作用,将不同请求分发给不同资源,需要采用url-pattern的扩展名规则。

复制代码
String uri=request.getRequestURI(); //获取请求路径:appName/list.do,appName/add.doString action= uri.substring(uri.lastIndexOf(“/”)+1,uri.lastIndexOf(“.”)); //根据不同的路径,调用不同的分支处理if(action.equals('add')){ ...}else if(action.equals('list')){ ...}
复制代码

 Servlet生命周期

1.实例化:容器不管收到多少个请求,只会创建一个servlet对象。

  实例化时机:收到请求时,或设置1于容器启动时创建。

2. 初始化: 容器调用init(ServletConfig config)方法对Serlvet进行初始化,GernericServlet提供了init方法的实现(而且通过this.config=config将ServletConfig对象保存),不用开发者写。Servlet的整个生命周期内,该方法只被调用一次。在init方法内,通过config.getInitParameter('name')或静态方法ServletConfig.getInitParameter('name')可以获取servlet的初始化参数init-param。如果需要重写init方法执行别的操作,最后应调用 super.init() 以确保正确的初始化。

3.  就绪:容器收到请求后调用service()方法。

4.  销毁:只执行一次。

Servlet线程安全问题

重定向与转发

重定向:服务器向浏览器发送一个302状态码及一个Location消息头(值为重定向地址),浏览器收到后立即向重定向地址发送请求。

使用场景:当资源移动到新的位置,需要客户端向新地址发送请求时,或是为了负载均衡,或者只是为了简化用户的操作(提交信息后跳转)。

使用响应对象的API即可实现重定向,重定向过程中涉及到的web组件不共享同一个request和response对象。

复制代码
out.println('会被清空');response.sendRedirect('任意url');System.out.println('该处代码仍会执行,等整个service方法结束才发送响应数据');//重定向后会清空response对象中的数据,Content-Lenght:0
复制代码

转发:一个web组件(servlet/jsp)将未完成的请求处理通过容器转交给另外一个web组件继续完成,如servlet获取数据后转发给jsp展现。

转发前后涉及的web组件用的是同一个request和response对象。在服务器内部完成,和浏览器无关。

使用步骤:

复制代码
//1.绑定数据到request对象request.setAttribute(String name,Object obj);//Object request.getAttribute(String name); 转发目标组件内获取数据//2.获得转发器RequestDispatcher rd=request.getRequestDispatcher(String path);//path:转发目的地,必须是同一个应用内部的绝对路径或相对路径//3.转发rd.forward(request,response); other code ... //转发语句之后的代码依然会被执行完
复制代码

过滤器、监听器与SpringMVC的拦截器

过滤器:是Servlet2.3规范中定义的一种封装了一些功能的小型、可插入web组件,用来拦截Servlet容器的请求过程和响应过程,以便处理请求和响应数据。

使用场景:确认用户是否登陆过,提交的内容是否有敏感词,字符集转变,管理会话属性,将多个相同处理逻辑的模块集中到过滤器中方便代码的维护。

编写过滤器步骤:

  1.编写一个java类,实现filter接口,拦截处理逻辑在doFilter方法中实现

复制代码
/* 敏感词过滤器 */public class CommentFilter implements Filter{ private FilterConfig config; private illegalWord; //容器启动后创建Filter实例,调用init方法一次。 //容器会将创建好的FilterConfig对象作为参数传入init方法,借此可以获取初始化的配置信息 //可以将FilterConfig作为成员保存在对象中供后续使用 public void init(FilterConfig filterConfig)throws ServletException{ this.config=filterConfig; illegalWord=filterConfig.getInitParameter('illegalWord'); ... } //doFilter是主要方法,可以调用过滤器链的doFilter方法,也可以直接向客户端返回响应信息,或者利用HttpServletResponse的sendRedirect()方法将请求转向到其他资源 //参数chain是过滤器链对象,过滤器链的dofilter()方法会调用下一个过滤器的doFilter方法。若无则调用相应的Serlvet的service方法 public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)throws IOException,ServletException{ HttpServletRequest req=(HttpServletRequest)request; //强制转到子接口 HttpServletResponse res=(HttpServletResponse)response; req.setCharacterEncoding('UTF-8'); res.setContentType('text/html;charset=utf-8'); PrintWriter out=req.getWriter(); String comment=req.getParameter('coment'); if(coment.indexOf('shit')!=-1){ //res.sendRedirect('error/_error.jsp');//可以直接转发 out.print('

有敏感词

'); //也可以直接响应数据。 }else{ chain.doFilter(req,res); …//other code here will be executed when response comes back; } } //容器删除过滤器实例之前调用,只执行一次 public void destroy(){this.config = null;...}}
复制代码

  2.将过滤器配置到web.xml中

复制代码
filter> filter-name>setCharacterEncodingfilter-name> filter-class>com.company.strutstudy.web.servletstudy.filter.EncodingFilterfilter-class> init-param> param-name>encodingparam-name> param-value>utf-8param-value> init-param>filter>//存在多个过滤器时,会按照filter-mapping的先后顺序依次调用filter-mapping> filter-name>setCharacterEncodingfilter-name> url-pattern>/*url-pattern>filter-mapping>
复制代码

 

监听器:Servlet规范中定义的一种特殊的组件,用来监听Servlet容器产生的特定事件并进行相应的处理,包括容器创建/销毁request、session和ServletContext时的事件,调用reqeust、session和ServletContext的setAttribute、removeAttribute方法时产生的事件。

使用场景:统计在线用户数量/网站访问量,记录访问日志,系统启动时初始化等。

监听器的启动顺序:按照web.xml的配置顺序来启动

容器加载顺序:监听器>过滤器>Servlet

编写监听器步骤:

  1.编写一个java类,实现特定的监听器接口(ServletContextListener/ServletContextAttributeListener,HttpSessionListener/HttpSessionAttributeListene,ServletRequestListener/ServletRequestAttributeListener)

复制代码
//统计网站在线人数public class CountListener implements HttpSessionListener{ private int count=0; public void sessionCreated(HttpSessionEvent hse){ //其他接口对应的requestInitialized,contextInitialized方法 count++; //通过监听的事件对象获得session对象,然后通过session获得servlet上下文 HttpSession session=hse.getSession(); ServletContext ctx=session.getServletContext(); ctx.setAttribute('count',count)  } public void sessionDestroyed(HttpSessionEvent hse){ //requestDestroyed,contextDestroyed方法 //关闭时操作  }}
复制代码

  2.在web.xml中配置该监听器

listener> listener-class>web.CountListenerlistener-class>listener>

拦截器:Spring的HandlerMapping处理器支持拦截器应用。拦截器组件是SpringMVC特有的组件,可以在进入Controller之前拦截,也可以在执行Controller之后拦截,还可以在jsp解析完成后向浏览器输出前拦截。

使用场景:日志记录,权限检查,后台处理时间监控,通用逻辑共用(如读取cookie)。

拦截器接口及方法:

复制代码
public interface HandlerInterceptor { boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; void postHandle(HttpServletRequest request, HttpServletResponse response,Object handler, ModelAndView modelAndView) throws Exception; void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception;}
复制代码

继承HandlerInterceptor接口,三个方法必须同时实现。如果只需要实现某个的回调,可以继承HandlerInterceptorAdapter接口。

preHandle:处理器执行前被调用,第三个参数为处理请求的处理器。

返回值:true表示继续调调其他拦截器或处理器;false表示流程中断,不继续调用其他的拦截器或处理器,此时需要通过response重定向来产生响应,否则页面是白板。

postHandle:处理器执行后、视图处理前调用,可以通过modelAndView对模型数据进行处理或对视图进行处理。

afterCompletion整个请求处理完毕后调用。如性能监控中可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理。只要preHandle返回true,拦截器的afterCompletion就会被执行

如何配置:

复制代码
mvc:interceptors> bean class='com.test.AllInterceptor'/> mvc:interceptor> mvc:mapping path='/test/check.do'/> mvc:exclude-mapping path='/login/*'> bean class='com.test.SomeInterceptor'/> mvc:interceptor>mvc:interceptors>
复制代码

 

 

 SpringMVC拦截器与Filter的区别 

二者都是AOP编程思想的体现,都能实现权限检查、日志记录等。

不同之处:

Filter是Servlet规范规定的,由Servlet容器支持的,只能用于Web程序中,只在Servlet前后起作用。

拦截器是在Spring容器内,由Spring框架支持的,既可以用于Web程序,也可以用于Application、Swing程序中。拦截器是一个Spring的组件,能使用Spring里的任何资源。在Spring构架的程序中,拦截器的使用具有更大的弹性,优先使用。

同时配置过滤器和拦截器时的请求处理过程:

 

 

 

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约