zoukankan      html  css  js  c++  java
  • Servlet起步

    什么是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文件中的<url-patten>找到对应的servlet,然后调用service()方法处理浏览器请求。 

    开发servlet步骤

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

    <servlet>
        <servlet-name>servletTest</servlet-name>
        <servlet-class>com.servlet.servletTest</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletTest</servlet-name>
        <url-pattern>/servlet/test</url-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("<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">");
            out.println("<HTML>");
            out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
            out.println(" <BODY>");
            out.print(" This is ");
            out.print(this.getClass());
            out.println(", using the GET method");
            out.println(" </BODY></HTML>");
        }
    
        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.do
    String action= uri.substring(uri.lastIndexOf(“/”)+1,uri.lastIndexOf(“.”));
        //根据不同的路径,调用不同的分支处理
    if(action.equals("add")){
        ...
    }else if(action.equals("list")){
        ...
    }

     Servlet生命周期

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

      实例化时机:收到请求时,或设置<load-on-startup>1</load-on-startup>于容器启动时创建。

    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("<h3>有敏感词</h3>"); //也可以直接响应数据。
            }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>setCharacterEncoding</filter-name>
      <filter-class>com.company.strutstudy.web.servletstudy.filter.EncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    //存在多个过滤器时,会按照filter-mapping的先后顺序依次调用
    <filter-mapping>   
        <filter-name>setCharacterEncoding</filter-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.CountListener</listener-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>
        <!-- 定义在mvc:interceptors元素下面的Interceptor将拦截所有的请求 -->
        <bean class="com.test.AllInterceptor"/>
        <mvc:interceptor>
            <!-- 定义在mvc:interceptor下面的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构架的程序中,拦截器的使用具有更大的弹性,优先使用。

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

     

     

    以上皆为个人理解,如有错误之处,欢迎留言指正。
  • 相关阅读:
    this
    git使用整个过程整理
    javaScript事件知识点
    charles-web端开发者实用功能点
    react native 添加第三方插件react-native-orientation(横竖屏设置功能 android)
    react native 运行项目下载gradle慢的解决办法
    移动端 滑动组件 slip
    高逼格 分页列表 bootstrap-table
    reactjs 接入数据模型以及markdown语法的支持
    react-native 环境搭建以及项目创建打包
  • 原文地址:https://www.cnblogs.com/kevin2chen/p/6540052.html
Copyright © 2011-2022 走看看