當柳上原的風吹向天際的時候...

          真正的快樂來源于創造

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            368 Posts :: 1 Stories :: 201 Comments :: 0 Trackbacks
          對一個成規模的系統來說,緩存總是不可或缺的一個環節,甚至會成為系統成功的重要因素。

          從原理來講,緩存并不神秘,它本質上只是一個哈希表,內部包含許多提取關鍵字和緩存內容的鍵值對,當有讀操作(如search)新的查詢到來時,系統先到 這個哈希表中看看是否有同樣的關鍵字存在,是則取出對應的值返回,否則進行查詢,并把新的查詢條件和結果存儲進哈希表,以便下次提取;當有寫操作(如 add,delete,update)來臨時,原則上說現有緩存的內容都存在了不確定性,那么簡單的處理就是清空現有緩存。

          緩存器的位置可以放在具體要執行的CRUD方法之前,當然我個人是不提倡這種耦合子系統的做法,利用Java的動態代理機制,我們可以把數據庫訪問和緩存 兩部分分離開來,而Spring提供的ProxyFactoryBean和Interceptor正好給我們提供了現成的便利,使我們不需要再重復的發明 車輪。這樣做的最大好處是解耦子系統,因為耦合是導致系統癱瘓的重大因素,所以我們必須盡量避免,隨時提防.

          請看具體來說是怎么實現緩存的,下面是需要為之提供緩存服務的TmpServiceImpl類及其接口:
          TmpServiceImpl類:
          public class TmpServiceImpl extends BaseService implements IService{
            
          /**
             * 添加一個Tmp對象到數據庫
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String add(String[] args) throws Exception{
              String name
          =args[0];
              
          int age=Integer.parseInt(args[1]);
              
          float salary=Float.parseFloat(args[2]);
              
              
          // 同名檢測
              if(hasSameName(name)){
                
          throw new BreakException("已經有和"+name+"同名的對象存在了.");
              }
              
              Tmp tmp
          =new Tmp(name,age,salary);
              dao.create(tmp);
              
              
          return tmp.toXML();
            }
            
            
          /**
             * 將TMP對象的信息組合成一個字符串返回
             * 
             * 說明:要修改此方法請與何楊商議,請勿自行修改!
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String getInfoById(String[] args) throws Exception{
              String id
          =args[0];
              
              Tmp tmp
          =(Tmp)getObjById(id);
              
              
          if(tmp==null){
                
          throw new BreakException("找不到Id'"+id+"'對應的Tmp對象.");
              }
              
              StringBuilder sb
          =new StringBuilder();
              
              sb.append(
          "姓名:"+tmp.getName()+""r"n");
              sb.append(
          "年齡:"+tmp.getAge()+""r"n");
              sb.append(
          "薪水:"+tmp.getSalary()+""r"n");
              sb.append(
          "添加時間:"+tmp.getAddTime()+""r"n");
              sb.append(
          "更新時間:"+tmp.getRefreshTime()+""r"n");
              
              
          return sb.toString();
            }
            
            
          /**
             * 按ID取得一個Tmp對象
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String getById(String[] args) throws Exception{
              String id
          =args[0];
              
              Tmp tmp
          =(Tmp)getObjById(id);
              
              
          if(tmp==null){
                
          throw new BreakException("找不到Id'"+id+"'對應的Tmp對象.");
              }
              
              
          return tmp.toXML();
            }
            
            
          /**
             * 按薪水來查詢Tmp對象
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String searchBySalary(String[] args)throws Exception{
              
          float salary=Float.parseFloat(args[0]);
              
              StringBuilder sb 
          = new StringBuilder();
              sb.append(
          " from " + domainClass + " d where d.valid=true and ");
              sb.append(
          " d.salary >='"+salary+"" );
              sb.append(
          " order by id asc ");
              
              String hql
          =sb.toString();
              
          return convertListToXml(dao.search(hql));
            }
            
            
          /**
             * 按ID來更新一個Tmp對象
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String update(String[] args)throws Exception{
              String id
          =args[0];
              String name
          =args[1];
              
          int age=Integer.parseInt(args[2]);
              
          float salary=Float.parseFloat(args[3]);
              
              Tmp tmp
          =(Tmp)getObjById(id);    
              
          if(tmp==null){
                
          throw new BreakException("找不到Id'"+id+"'對應的Tmp對象.");
              }
              
              tmp.setName(name);
              tmp.setAge(age);
              tmp.setSalary(salary);
              dao.update(tmp);
              
              
          return tmp.toXML();
            }
            
            
          /**
             * 刪除一個對象
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String delete(String[] args) throws Exception{
              String id
          =args[0];
              
              Tmp tmp
          =(Tmp)getObjById(id);    
              
          if(tmp==null){
                
          throw new BreakException("找不到Id'"+id+"'對應的Tmp對象.");
              }
              
              dao.delete(tmp);
              
              
          return tmp.toXML();
            }
            
            
          /**
             * 分頁搜索
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String pagedSearchBy(String[] args) throws Exception{
              
              
          int currentPage=Integer.parseInt(args[0]);  // 當前頁
              int pageSize=Integer.parseInt(args[1]);   // 頁面記錄數
              String name=args[2];            // 姓名
              String salaryFrom=args[3];          // 薪水起點
              String salaryTo=args[4];          // 薪水終點
              String ageFrom=args[5];           // 年齡起點
              String ageTo=args[6];           // 年齡終點
              
              
          // 組合Sql語句
              StringBuilder sb = new StringBuilder();
              sb.append(
          " from " + domainClass + " d where d.valid=true  ");
              
              
          if(StringUtils.isNotBlank(name)){
                sb.append(
          " and d.name like '%"+name+"%' " );
              }
              
              
          if(StringUtils.isNotBlank(salaryFrom)){
                sb.append(
          " and d.salary >='"+salaryFrom+"" );
              }
              
          if(StringUtils.isNotBlank(salaryTo)){
                sb.append(
          " and d.salary <'"+salaryTo+"" );
              }
              
              
          if(StringUtils.isNotBlank(ageFrom)){
                sb.append(
          " and d.age >='"+ageFrom+"" );
              }
              
          if(StringUtils.isNotBlank(ageTo)){
                sb.append(
          " and d.age <'"+ageTo+"" );
              }
              
              sb.append(
          " order by id asc ");   
              String hql
          =sb.toString();
              
              
          // 取得分頁查詢結果
              return getPagedSearchResultInXML(hql,currentPage,pageSize);
            }

            @Override
            
          public String search(String[] args) throws Exception {
              
          return null;
            }
          }

          IService接口:
          public interface IService{
            
          /**
             * 解析參數數組,組合成一個領域對象,然后添加到數據庫(寫方法)
             * 
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String add(String[] args) throws Exception;
            
            
          /**
             * 解析參數數組,更新領域對象的一個或多個屬性,然后更新數據庫中的對應記錄
             * 
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String update(String[] args)throws Exception;
            
            
          /**
             * 解析參數數組得到要刪除的領域對象的Id,然后根據它刪除數據庫中的對應記錄
             * 
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String delete(String[] args) throws Exception;
            
            
          /**
             * 解析參數數組得到要取得的領域對象的Id,然后根據它渠道數據庫中的對應記錄
             * 
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String getById(String[] args) throws Exception;
            
            
          /**
             * 按條件進行分頁查詢
             * 注意這里的條件最好寫全,最好根據數組內容走不同的分支,不要寫各種各樣的查詢函數,這樣不方便緩存的處理
             * 
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String pagedSearchBy(String[] args) throws Exception;
            
            
          /**
             * 按條件進行查詢,除了不分頁要求和上面函數(pagedSearchBy)一致
             * 
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String search(String[] args) throws Exception;
            
            
          /**
             * 按ID取得信息
             * 
             * 
          @param args
             * 
          @return
             * 
          @throws Exception
             
          */
            
          public String getInfoById(String[] args) throws Exception;
          }

          可以看出來,上面的服務類是直接走到數據庫操作記錄的,而我們需要在它的函數執行之前就讓緩存發揮左右,因此,我們需要引入 ProxyFactoryBean和Interepter的幫助,在TmpServiceImpl類的實際方法運行前檢索緩存。這需要進行一定的配置:
            <!-- Tmp對象服務實現類類方法攔截器(一) -->
              
          <bean id="tmpServiceMethodInterceptor" class="com.***.service.interceptor.ServiceMethodInterceptor"/>
              
              
          <!-- Tmp對象服務實現類(二) -->
              
          <bean id="TmpServiceImpl" class="com.***.service.TmpServiceImpl">
                  
          <property name="domainClass">
                      
          <value>Tmp</value>
                  
          </property>
                  
          <property name="dao">
                      
          <ref bean="dao"/>
                  
          </property>
                  
          <property name="transactionTemplate">
                      
          <ref bean="transactionTemplate"/>
                  
          </property>
              
          </bean>
              
              
          <!-- 對外的TmpService,實際上是TmpServiceImpl的代理(三) -->
              
          <bean id="TmpService" class="org.springframework.aop.framework.ProxyFactoryBean">
                  
          <property name="proxyInterfaces">
                      
          <value>com.***.service.base.IService</value>
                  
          </property>
                  
          <property name="interceptorNames">
                      
          <list>
                          
          <value>tmpServiceMethodInterceptor</value>
                      
          </list>
                  
          </property>
                  
          <property name="target">
                      
          <ref bean="TmpServiceImpl"/>
                  
          </property>
              
          </bean>

          這樣就可以了,下面是com.***.service.interceptor.ServiceMethodInterceptor類的代碼,應該很好理解:
          public class ServiceMethodInterceptor implements MethodInterceptor{
              
          // 日志記錄器
              private    static Logger logger = Logger.getLogger(ServiceMethodInterceptor.class);
              
              
          // 作為緩存的哈希表
              private Map<String,Object> cacheMap=new Hashtable<String,Object>();

              @Override
              
          public Object invoke(MethodInvocation invocation) throws Throwable {
                  String className
          =invocation.getClass().getName();
                  String mothodName
          =invocation.getMethod().getName();
                  logger.info(
          "類'"+className+"'的方法'"+mothodName+"'將得到調用!");
                  
                  
          if(mothodName.contains("add"|| mothodName.contains("update"|| mothodName.contains("delete") ){
                      
          // 寫方法來了,這意味著數據變更了,緩存可能不可靠,為安全起見需要重新來過
                      cacheMap.clear();
                      
                      Object result
          =invocation.proceed();
                      logger.info(
          "類'"+className+"'的方法'"+mothodName+"'調用完畢!");
                      
                      
          return result;
                  }
                  
          else{
                      
          // 來的是讀方法
                      
                      
          // 通過組合方法名和參數來得到key
                      StringBuffer sb=new StringBuffer();
                      sb.append(mothodName
          +";");
                      
                      Object[] arr
          =invocation.getArguments();
                      String[] arr2
          =(String[])arr[0];// 這一步的轉化是很重要的
                      for(Object obj:arr2){
                          sb.append(obj
          +",");
                      }
                      
                      String key
          =sb.toString();
                      
                      
          // 拿Key查看緩存中是否有內容,有則直接返回即可
                      if(cacheMap.containsKey(key)){
                          logger.info(
          "直接得到緩存中的結果!");
                          
          return cacheMap.get(key);
                      }
                      
          else{
                          Object result
          =invocation.proceed();
                          
                          cacheMap.put(key, result);
                          logger.info(
          "類'"+className+"'的方法'"+mothodName+"'調用完畢!");
                          
                          
          return result;
                      }
                  }
              }
          }

          之所以使用MethodInterceptor是因為在其中可以修改返回的結果,在上面出現的
          Object result=invocation.proceed();           
          return result;
          實際就是對TempServiceImpl函數執行的調用,result就是返回結果,它是可以改變的。因此,如果緩存中有對應內容,取出直接返回,沒有的話調用這兩句進行老實的數據庫操作即可。

          到這里,緩存已經可以使用了,當然,它還很不完善,在鍵的設計和簡化,如果數據過多時的硬盤暫存,數據過期,寫操作對緩存影響的精細化上都可以下一番工夫,這些我們日后再探討吧。
          posted on 2010-05-24 15:10 何楊 閱讀(638) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 恩施市| 宁城县| 金华市| 龙胜| 嘉义县| 大关县| 红原县| 温宿县| 酉阳| 尚志市| 百色市| 太谷县| 普定县| 潞西市| 江达县| 涟源市| 子洲县| 麻江县| 九龙县| 韶山市| 乐业县| 弥勒县| 宿州市| 高州市| 商河县| 晋州市| 铜山县| 连州市| 博罗县| 鹤岗市| 定结县| 嘉善县| 门源| 偃师市| 西平县| 浮山县| 曲麻莱县| 合阳县| 金秀| 唐海县| 陆河县|