欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JavaWeb三大组件(Servlet、Filter、Listener)

程序员文章站 2024-02-09 16:11:28
...

1.Servlet

Servlet简介

    运行在服务器端Servlet容器中的小程序(服务器将请求封装为对象HttpRequest交给Servlet小程序去执行,对数据进行操作,servlet是帮web服务器处理数的)Servlet就是一个接口,凡是实现这个接口的类我们都称为Servlet

Servlet作用

      接受请求-->处理请求-->返回响应

为什么要有Servlet

      ①服务器返回客户静态资源,例如:web页面,图片等,WEB服务器擅长提供静态页面不能提供动态即时页面,不能往数据库保存数据

       ②Servlet :服务器端小程序 -可以提供动态页面 -可以与后台交互-运行在servlet容器中,servlet容器运行在服务器中。比如用户输入用户名和密码,web服务器只会响应一些静态页面自己不会处理这些数据,所以服务器会将请求或响应封装为一个对象作为参数传递给java程序,java程序会去处理这些数据,如果用户名密码正确给客户返回一个页面,否则返回另一页面(所有的java程序(Servlet都会有一个方法service(HttpServletRequest request, HttpServletResponse response)方法,这个方法的参数就是请求和响应))

Servlet创建步骤

1.创建一个类实现Servlet接口
2.在web.xml文件中注册servlet(在web.xml文件中配置Servlet)

          <servlet>
              <servlet-name>别名</servlet-name>
              <servlet-class>全类名</servlet-class>
          </servlet>

          <servlet-mapping>
            <servlet-name>别名</servlet-name>        //对应的Servert
            <url-pattern>/requestadress</url-pattern>   //为Servlet映射一个请求地址   Servlet接受浏览器发给web服务器请求 要有请求地址
        </servlet-mapping>  

    客户通过浏览器发送请求给web服务器,如发送的请求为一个地址 如:/requestadress,在配置文件web.XML中会映射这个请求地址给 对应的Servlet(Servlet-name),web服务器通过Servlet-name对应的全类名创建一个Servlet实例 , 通过Servlet实例调用其service()方法完成数据的处理。

Servlet生命周期

- Servlet的生命周期是指Servlet的对象由被创建到被销毁的过程。
    - Servlet的生命周期方法:
            1.构造器
                - Servlet第一次处理请求的时候调用,用于创建Servlet的实例,只会调用一次。
                - Servlet是单例,同一个Servlet在服务器中只有一个对象,但是它是多线程的,效率高;
                - 它是以多线程的方式调用service()方法,Servlet不是线程安全的,在service()中尽量不要操作全局变量。
            2.init()
                - 构造器执行以后,init马上被调用,用来做初始化操作,只会调用一次
                - 注意如果我们继承的是HttpServlet,那么他的里面有两个init方法。
                - 如果要重写只能重写的无参的方法。
            3.service()
                - 每次处理请求时都会调用service,它用来处理用户发送的请求,会调用多次
                - 如果我们通过继承HttpServlet实现Servlet,
                    我们只需要根据要处理的请求类型,重写doGet或doPost
            4.destroy()
                - Servlet对象销毁之前调用,用来做一些收尾工作,只会调用一次

ServletConfig接口

     每一个Servlet都有其唯一对应的ServletConfig,ServletConfig代表Servlet的配置信息,具体来说就是servlet标签中的内容

   <servlet>
          <servlet-name>Servlet3</servlet-name>
          <servlet-class>com.atguigu.servlet.Servlet3</servlet-class>
          <init-param>(初始化参数)
                  <param-name>username</param-name>
                  <param-value>root</param-value>

          </init-param>
   </servlet>

web服务器对XML进行解析时将其封装成一个对象(就像我们利用dom4j对students.xml 进行解析,并封装成student一样) 获取:由服务器创建,最终作为参数传递到init()方法中,我们可以在init方法中直接使用如果是通过继承HttpServlet,则可以直接调用ServletConfig的方法,因为HttpServlet实现了ServletConfig接口。
        功能:1.获取当前Servlet的别名(没用)
              2.获取当前Servlet的初始化参数(XML中的)
              3.获取ServletContext对象

ServletContext接口

代表整个的WEB应用,每一个WEB应用都有其唯一的ServletContext对象,  ServletContext对象在服务器启动时创建,在服务器停止时销毁 
      获取:通过ServletConfig的getServletContext()方法获取
      功能:
           1.获取整个WEB应用的初始化参数           

        <context-param>
             <param-name>phone</param-name>
             <param-value>13777788899</param-value>
        </context-param>

            2.根据虚拟路径获取资源的真实路径(物理路径)(主要用来文件的上传和下载的)
            3.可以作为一个域对象在不同的WEB资源之间共享数据 

HttpServlet接口

 在创建Servlet都是继承HttpServlet类的方式直接创建,然后重写doGet()和doPost()方法】
(原因:因为HttpServlet继承了GenericServlet,而GenericServlet实现了Servlet接口;实际上当请求到达是还是会直接找原生的service(ServletResqust request,ServletResponse response);而自己的原生service方法在父类HttpServlet中;父类中还有一个重载的service(HttpServletRequest request,HttpServletResponse response)方法,请求到达直接找了原生的service方法,然后把ServletRequest和ServletResponse强转为HttpServletRequset和HttpServletResponse然后调用重载的service方法,在重载的setvice方法中会判断请求是get请求还是post请求,如果是get请求会找doGet方法,如果是post请求会直接找doPost方法,所以我们只需要根据前台是get请求还是post请求来重写doGet方法或doPost方法即可)
          当需要处理get请求时重写doGet()方法
          当需要处理post请求时重写doPost()方法
          需要初始化对象时,重写init()无参方法即可
          需要做收尾工作的时候,重写destroy()方法即可

HttpServletRequest接口

request代表浏览器发送给服务器的请求报文
获取:由服务器创建(服务器将请求报文封装为HttpServletRequest对象),最终作为参数传递到doGet或doPost方法中,我们可以在这两个方法中直接使用
功能:
         [1]获取到浏览器发送的请求参数    :如用户名,密码等参数                

         request.getParameter(String name)  //根据参数名获取参数值

[2]动态的获取项目的名字(主要用来设置绝对路径的)              

          request.getContextPath();   

 [3]作为一个域对象在不同的WEB资源之间共享数据
 [4]请求的转发              

          RequestDispatcher dispatcher =request.getRequestDispatcher(String path)
          dispatcher.forward(request, response);
          request.getServerName()    :    localhost ;
          request.getServerPort()    :    8080  ; 
          request.getContextPath()   :    /项目名 ;

HttpServletResponse接口

代表服务器发送给浏览器的响应报文
     获取:由服务器创建(服务器将响应报文封装为HttpServletRsponse对象),最终作为参数传递到doGet或doPost方法中,我们可以在这两个方法中直接使用
     功能:
          [1]可以向浏览器发送一个页面或者是页面片段        

       response.sendRedirect("<h1>欢迎登录</h1>")

           [2]做请求的重定向

       response.sendRedirect(String path)

          [3]将数据写到页面

     PrintWriter writer = response.getWriter(); 
     writer.write(new Date());
     writer.flush();
     writer ose();     

 

如何解决Servlet的单线程安全问题

1、实现 SingleThreadModel 接口
          该接口指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程被同时执行, 当然也就不存在线程安全的问题。
2、同步对共享数据的操作
          使用synchronized 关键字能保证一次只有一个线程可以访问被保护的区段,在本论文中的Servlet可以通过同步块操作来保证线程的安全。
3、避免使用实例变量
         本实例中的线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。   

    对上面的三种方法进行测试,可以表明用它们都能设计出线程安全的Servlet程序。但是,如果一个Servlet实现了SingleThreadModel接口,Servlet引擎将为每个新的请求创建一个单独的Servlet实例,这将引起大量的系统开销。SingleThreadModel在Servlet2.4中已不再提倡使用;同样如果在程序中使用同步来保护要使用的共享的数据,也会使系统的性能大大下降。这是因为被同步的代码块在同一时刻只能有一个线程执行它,使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态。另外为保证主存内容和线程的工作内存中的数据的一致性,要频繁地刷新缓存,这也会大大地影响系统的性能。所以在实际的开发中也应避免或最小化 Servlet 中的同步代码;在Serlet中避免使用实例变量是保证Servlet线程安全的最佳选择。Servlet的线程安全问题只有在大量的并发访问时才会显现出来,并且很难发现,因此在编写Servlet程序时要特别注意。线程安全问题主要是由实例变量造成的,因此在Servlet中应避免使用实例变量。如果应用程序设计无法避免使用实例变量,那么使用同步来保护要使用的实例变量,但为保证系统的最佳性能,应该同步可用性最小的代码路径

2.Filter

既然和Servlet如此相似,为什么有了Servlet还要Filter呢?

①. Servlet 通常用来servlet主要负责处理请求并且应答浏览器发出的请求. 有 HttpServlet 作为实现类,根据需要覆盖 doGet 或 doPost.
②. Filter:意为 "过滤器". 它除了可以完成 Servlet 的功能之外,它还可以实现资源拦截的效果.若把 Servlet 比喻为要拜访的客户,则 Filter 为访问该客户过程中需要经过的门岗. 它将起到过滤的作用.
③. Servlet 和 Filter 的功能谁更强大 ? Filter 的功能更强大些,因为他还有一个拦截的功能. 因为在其中多一个 API:FilterChain. 

Filter创建步骤

1.创建一个类,实现javax.servlet.Filter接口

public class ChartSetFilter implements Filter {

    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub  
    }

    public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain)throws IOException, ServletException {

        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        chain.doFilter(request, response);
    }

    public void destroy() {
        // TODO Auto-generated method stub  
    }   

}

2.在web.xml文件中配置Filter

    <filter>
        <display-name>ChartSetFilter</display-name>
        <filter-name>ChartSetFilter</filter-name>
        <filter-class>com.atguigu.survey.filter.ChartSetFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>ChartSetFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

3. 如果有多个过滤器都匹配该请求,顺序决定于web.xml filter-mapping的顺序,在前面的先执行,后面的后执行

Filter的生命周期

构造器:创建Filter实例时调用,Filter实例服务器一旦启动就会被创建
init():实例创建后马上被调用,用来对Filter做一些初始化的操作
doFilter():Filter的主要方法,用来完成过滤器主要功能的方法,每次访问目标资源时都会调用。
destroy():服务器停止时调用,用来释放资源。

3.Listener

监听器可以监听Application、Session、Request对象,当这些对象发生变化就会调用对应的监听方法。 

监听器的分类(三大类(八个监听器))

    1.用于监听域对象的创建和销毁的        

【重要】①ervletContextListener: 用于监听 ServletContext 对象被创建和销毁的监听器
             public void contextInitialized ( ServletContextEvent sce );
                       ServletContext创建时调用
             public void contextDestroyed ( ServletContextEvent sce );
                       ServletContext销毁时调用

        ②HttpSessionListener: 用于监听 HttpSession 对象被创建和销毁的监听器
             public void sessionCreated ( HttpSessionEvent se );
                       HttpSession对象创建时调用
             public void sessionDestroyed ( HttpSessionEvent se );
                       HttpSession对象销毁时调用

        ③ServletRequestListener:  用于监听 ServletRequest 对象被创建和销毁的监听器
             public void requestInitialized ( ServletRequestEvent sre );
                       ServletRequest对象创建时调用
              public void requestDestroyed ( ServletRequestEvent sre );
                       ServletRequest对象销毁时调用

      步骤:
      a.创建一个类实现实现ServletContextListener/HttpSessionListener/ServletRequestListener

   public class MyServletContextListener implements ServletContextListener {
          @Override
          public void contextInitialized(ServletContextEvent sce) {
            System.out.println("哈哈,我是ServletContext,我出生了");
          }

          @Override
          public void contextDestroyed(ServletContextEvent sce) {
            System.out.println("~~~~(>_<)~~~~,我是ServletContext,我要死了");
          }
    }

  b.在web.xml文件中注册监听器

<listener>
   <listener-class>com.atguigu.web.listener.MyServletContextListener</listener-class>
</listener>

2.用于监听域对象属性变化(添加属性、替换属性、移除属性... ...)       

     ④ServletContextAttributeListener
            public void attributeAdded(ServletContextAttributeEvent scab);
                   向ServletContext中添加属性时调用

            public void attributeRemoved(ServletContextAttributeEvent scab);
                   从ServletContext中移除属性时调用

            public void attributeReplaced(ServletContextAttributeEvent scab);
                   当ServletContext中的属性被修改时调用

      ⑤HttpSessionAttributeListener

      ⑥ervletRequestAttributeListener


     步骤:
        a.创建一个类实现实现ServletRequestAttributeListener/......Lsitener接口

     public class ReqAttrListener implements ServletRequestAttributeListener {
        @Override
        public void attributeAdded(ServletRequestAttributeEvent srae) {
           System.out.println("request域中添加一个属性" + srae.getName() + "=" + srae.getValue());
        }

        @Override
        public void attributeRemoved(ServletRequestAttributeEvent srae) {
          System.out.println("request域中移除一个属性" + srae.getName() + "=" + srae.getValue());
        }

        @Override
        public void attributeReplaced(ServletRequestAttributeEvent srae) {
           System.out.println("request域中一个属性被修改了" + srae.getName() + "=" + srae.getValue());
        }
     }  

       b.在web.xml文件中注册监听器     

  <listener>
          <listener-class>com.atguigu.web.listener.ReqAttrListener</listener-class>
  </listener>

3.感知session监听器       

  ⑦HttpSessionBindingListener监听 
        ⑴在需要监听的实体类实现HttpSessionBindingListener接口 
        ⑵重写valueBound()方法,这方法是在当该实体类被放到Session中时,触发该方法 
        ⑶重写valueUnbound()方法,这方法是在当该实体类从Session中被移除时,触发该方法 

  ⑧HttpSessionActivationListener监听 
        ⑴在需要监听的实体类实现HttpSessionActivationListener接口 
        ⑵重写sessionWillPassivate()方法,这方法是在当该实体类被序列化时,触发该方法 
        ⑶重写sessionDidActivate()方法,这方法是在当该实体类被反序列化时,触发该方法