⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 day05.txt

📁 servlet上课内容5.
💻 TXT
字号:
Servlet_day05   langna  2007-9-27   星期四
回顾:
    session
    cookie
    将扩展名滤掉;
    path=path.subString(0, path.indexOf("."));
    
一, Fliter : 过滤器
    过滤器是用于过滤Servlet的请求和响应,
    过滤器是存在于请求和被请求资源之间的。
     双向的;
     客户端--------------->   过滤器------------------>  服务器(servlet)
     客户端 <---------------  过滤器 <-----------------   服务器(servlet)
     
     过滤器可以调用servlet的方法; servlet返回给过滤器; 
     
     用户真正感觉到的是资源,不需要知道过滤器的路径; 过滤器只关心和自己相关的一组路径;
     servlet/jsp/html等都是可以被过滤的; 在资源执行前和后都能执行;
     可以做一些安全和日志的操作;session的管理;
 1,     
     例子: 如TimeServlet: 
     假如:  没有servlet的源代码,只有字节码; 要求在页面中加上一行代码;
     汇编和反编译都可以;但很复杂;用过滤器即可;
     javax.servlet  Fliter接口
     
    Filter是一个接口,要写一个自己的Filter就只能实现Filter接口。
    Filter也有自己的生命周期,他的生命周期和Servlet比较相似,
    也是会先调用init()方法,然后再调用核心的处理过滤的方法doFilter(),
    这个方法中可定义了过滤规则,然后是destory()方法销毁Filter对象
    
     void  init(FliterConfig){}
     void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){}
     void destroy(){} 
     
     public  class  AppendFliter  extends Fliter{
             //覆盖三个方法
             
     }
2,  配置文件web.xml
     
        <filter>
        	<filter-name>AppendFliter</filter-name>
        	<filter-class>com.tarena.AppendFliter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>AppendFliter</filter-name>
		<url-pattern>/protected/*</url-pattern>
		<!--这里的url-pattern就是要过滤的Servlet的url-pattern-->
		<!--可以写servlet-name;写/* 表示过滤所有的servlet, session管理,计数器统计等用这个 -->
	</filter-mapping>
        
        FilterChain 代表被过滤的资源,要想资源执行,必须调用它的方法:
        chain.doFliter(reqest,response);
        
        启动服务器时就创建一个过滤器实例;init(); 
        请求过来时调用doFliter();销毁时调用destroy();
        
        PrinterWriter  out = response.getWriter();
        out.println("welcom to  my  page! ");//在前面追加一行;
        chain.doFliter(reqest,response);//表明调用被过滤的资源;
        out.println("after  my  page !");
        //在后面打不出来;因为在servlet中out被关了;将out.close()注释掉;
        response.getWriter();//得到的是一个引用,不是重新打开,状态只有一个,取到的是被关闭的out;
        
 3, 包装器:
    MyResponse 继承 HttpServletResponse 的包装类;大部分功能由后者完成;
    只是 getWriter方法 是我们需要的;
    
    public class MyResponse extends HttpServletResponseWrapper{
        private CharArrayWriter caw = new CharArrayWriter();
        public MyResponse(HttpServletResponse response)
        {
            super(response);
        }
        
        public PrintWriter getWriter()
        {
            return new PrintWriter(caw);
        }
        
        public String getServletOutput()
        {
            return caw.toString();
        }
}
    
    MyResponse  myResponse=new  MyResponse((HttpServletResponse)response);
    
    CharArrayWriter类,是一个将数据保存在字符数组中的输出流,
    我们可以使用它来构造一个PrintWriter对象,也就实现了向内存输出。
    CharArrayWriter类的toString()和toCharArray()方法就可以取得写入内存中的数据。
    注意:CharArrayWriter类是一个不会真正输出的类,他的write()方法
    只会将内容写入字符数组,而且这个字符数组是会自动增长的;
   
    servlet :
    doGet 中
    out.getWriter();//得到的是向内存中输出的流;
    out.close();//只是将向内存中输出关闭; 关闭该流, 此方法并不释放缓冲区;仍然可以调用其他方法;
    
    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
            MyResponse  myResponse=new  MyResponse((HttpServletResponse)response);
            chain.doFliter(reqest,myResponse);
            PrintWriter  out=response.getWriter();//在过滤器中得到的是向客户端输出的流;
            out.println(myResponse.getServletOutput());
            out.println(" after my page");
            out.close();
    }
 
  不要依赖于过滤器的顺序;相同的功能放到一个过滤器中即可;
  会看时序图;关心顺序;
  协作图, 能看出谁是核心对象; 
  画流程图:  visio/rose/pd(数据库设计)
  
  注:
  servlet2.4  之前请求不是由客户端直接发送的,forward();include();
                   过滤器只能过滤客户端的请求;
   2.4之后在web.xml 的 <filter-mapping> 标签中加上:
        <dispatcher>request</dispatcher>  <!--客户端的请求 -->
        <dispatcher>forward</dispatcher> <!--转发的请求 -->
        <dispatcher>include</dispatcher> 
        <dispatcher>error</dispatcher>          

4,  自己写一个HttpFliter; 抽象类;以后可以复用 ; 
    
     写一个init(){}
     在init(config){this.config=config; init();}     
     子类中如果覆盖了init()方法;也相当于覆盖了带参的方法;
    
     写一个 public  abstract  doFliter(HttpServletRequest , HttpServletResponse ,FliterChain  );
     
 
 二, 监听器:
 1,    什么是事件监听器;
       怎么实现和部署监听器;
     
     过滤器过滤哪些路径可以控制的;
     监听器监听的内容是不能控制的, 监听所有请求,会话,上下文;
     用来监听ServletContext ,  HttpSession ,  ServletRequest ; 
     都有公共方法:  getAttribute, setAttribute 
     
     他们的属性都有什么用?和数量:

                       上下文                           会 话                                       请 求
                    ServletContext               HttpSession                            ServletRequest
  数量:           一个应用一个;               一个用户一个;                         所有用户发送的请求的数量;
  
  可见范围:    全局,整个应用中;           当前用户访问到的资源;            当前请求的资源以及相关的资源(过滤或转发时);
     
  生命周期:   和应用一样                     一个会话周期;                         当前请求的响应生成完毕;
                    应用启动时自动创建
                    关闭时自动销毁;
  
  安全性:      必须考虑并发;                   必须考虑并发;                        线程安全的;     
                   需要加线程同步;                需要加线程同步;                       
                   对上下文属性加锁;            同一个用户访问相同的资源;
                   考虑是否会出现覆盖;         不会和另外用户发生冲突;
                   多个servlet访问;     
                   
 2, 监听上下文: (上下文对象代表当前的应用)
   ServletContextListener(监听生命周期); ServletContextAttributeListener(监听属性);    
   服务器启动的时候是单线程; 
   服务器是事件源; 事件对象 ServletContextEvent ;事件监听器就是ServletContextListener;
  
  tomcat根文件夹下有一个logs文件夹,专门用来记录日志信息的; 继承HttpServlet直接调用log(String);  
  public class MyContextListener implements ServletContextListener{
        public void contextInitialized(ServletContextEvent sce){
                sce.getServletContext().log("MyContextListener.contextInitialized()");
               // UserService  us
        }
        
        public void contextDestroyed(ServletContextEvent sce){
                sce.getServletContext().log("MyContextListener.contextDestroyed()");
        }
  } 

listener的配置:
    <listener>
    	<listener-class>com.tarena.listener.MyContextListener</listener-class>
            <!--listener-class也就是实现Listener接口的类-->
    </listener>
    
3 , HttpSessionListener: 
     写一个集合,放在session中;
     写一个辅助类:容器类;意义上更清楚;实现可序列化接口;
     可以转化成字节码网络传输;或保存在磁盘上;
     session 的活化,钝化;服务器都支持;
     
     钝化: 很长时间没有人访问,就先把它写到磁盘中,内存空间服务别的用户;
     活化(激活):如果再来访问,就把状态从磁盘中取出来; 
     java语言中的数据结构都是可序列化的;
 
     public  class  Product{}
     public  class  Item{}//覆盖equals和hashCode方法;
     
     public  class  ShoppingCart {
            private  Set items=new  HashSet();//一种商品只有一个记录,不能重复;
            public  void  add(Item  p){
                products.add(p);//首先判断一下是否已经有了这种商品;有的话取出来,数量加一;
            }
            public  void  remove(Item  p){
                products.remove(p);
            }
            public Iterator getProducts(){//返回迭代器比集合要好,只能迭代内容,不能再增加了;
            }
     }
     
     利用session的监听器,创建一个购物车放进去;
     销毁session 时, 将没有结算的商品放到数据库中保存起来;
     也可以在入口创建一个session时,创建一个购物车放进来;
 
 4, RequestListener: 不常用,但是可以统计点击数;
 
 三, 连接池: (看看大概思路就行,不做要求)
       数据库中没有连接池,还想提高性能?  自己把连接缓存起来用; 
       
       上下文监听器, 登陆, 创建一个数据库连接,放到上下文的属性中, 取出来用,
       当应用关闭时,将连接取出来,关闭; 
       
      Connection 线程不安全的; 所有servlet只有一个; 
       
      public  class  ConnectionPoolListener   implements   ServletContextListener{
       
       }
       
      public  class  ConnectionUsingServlet {
           //用完以后不能关闭连接; 但是又不符合编程习惯;写一个包装类覆盖close方法;
       }
       public  class  ConnectionWrapper  extends  Connection {
            private  Connection  con;
            private  boolean  isBusy;
            private  ConnectionPool  pool;
            
            public  ConnectionWrapper(Connection  con){
                    this.con=con;
            }
          // 实现所有的方法
          
          public void  close()throws SQLException{
              isBusy=false;  
              pool.notifyThreads();
          }
          
          //写一个方法来真正关闭连接
          public  void  realse(){
                con.close();
          }
          public boolean isBusy(){
                return  isBusy;
          }
       }
       
       在doGet(){
            //对数据库的所有操作都同步;对上下文加锁; 
            Connection  con;
            synchronized(getServletContext()){
                Statement  st=con.createStatement();
                ResultSet   rs=st.executeQuery("");
            }
       }
       
     把同步的代码提出来,放到ConnectionPool里; 对getConnection()方法同步;
     public class  ConnectionPool  {
            private ConnectionWrapper  cw;
            
            public  void  init(){//初始化连接池
                Connection con=JdbcUtil.getConnection();
                cw=new  ConnectionWrapper(con);
            }
            
            public  synchronized  notifyThreads(){
                    notifyAll();//必须用在同步块中;拥有对象的锁才能调用;
            }
            public  synchronized  Connection  getConnection(){
                while(cw.isBusy()){
                        wait();  //必须用在同步块中;拥有对象的锁才能调用;
                }
                cw.setBusy(true);
                return  cw;
            }
            public void destroy(){ 
            }
     }
     
     

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -