zyskm用夢想丈量人生,用奔跑丈量激情

          #

          How tomcat works閱讀筆記

          這幾天抽空把How tomcat works看了一遍。

          這本書寫得很好,把tomcat這么一個牛B的大家伙拆成一堆零件,然后告訴你怎么組裝,真是做到了掰開了揉碎了講。
          簡單記一下
          第一章講web服務器,如何接受和響應http請求,還舉了個sokcet的例子。算是入門,從很底層的技術講起。
          第二章講servlet容器,javax.servlet.Servle接口,接受到http請求后如何查找servlet,執行servlet。
          第三章講連接器的概念,前兩章看下來你會覺得把http請求接受響應跟容器放在一起太亂了,這章就講如何把http操作提出來作為一個連接器。
          第四章講tomcat默認連接器,http協議操作講得很詳細,不過我沒怎么看哈,用的時候直接把tomcat這段代碼拿過來就是了。
          第五章講容器,在第三章的基礎上對容器進行分層分類,事情復雜了就分成幾部分,“治眾如治寡,分數是也”這個我們都知道。
              tomcat講容器分成這幾個概念:
          Engine:表示整個Catalina的servlet引擎
          Host:表示一個擁有數個上下文的虛擬主機
          Context:表示一個Web應用,一個context包含一個或多個wrapper
          Wrapper:表示一個獨立的servlet

          類型復雜了,要做的事情也復雜了。
          不僅僅是執行service()方法,還要前邊執行一堆,后邊再來一堆。引入了流水線任務Pipelining Tasks的概念,在流水線上可以執行多個Valve(有翻譯成閥),類似于攔截器的概念。

          第六章講生命周期,人多了要講究個步調統一,引入了Lifecycle接口的概念,方法包括啟動之前干什么、啟動之后干什么、啟動后把子容器也啟動了。
          包括引入監聽接口,都是些java常見實現方式,沒什么特殊。

          第七章講日志系統,沒看。
          第八章講加載器,可以參考tomcat類加載器及jar包沖突問題分析 http://www.aygfsteel.com/zyskm/archive/2011/12/06/365653.html 就不重復了。
          第九章講session管理,沒什么特別的。
          第十章講安全,沒看。
          第十一章講StandardWrapper,在第五章的基礎上重點分析了wrapper的運作機制。
          其余章節目前工作中用不到,有空再看了。

          作者:zyskm
          http://www.aygfsteel.com/zyskm

          posted @ 2011-12-06 16:25 zyskm 閱讀(3194) | 評論 (7)編輯 收藏

          tomcat類加載器及jar包沖突問題分析

          開發過程中遇到過這樣一個情況,在本地tomcat下開發調試正常,打包到測試環境的jboss下所有頁面都變成空白頁。
          項目日志和jboss日志沒有一點異常信息,費了半天勁把jboss所有日志全部打出來,發現是el.jar這個包里有空指針調用。
          檢查一下,項目WEB-INF\lib里有這個包呀,那應該是跟什么地方的jar包版本沖突了猜想,繼續找,在jboss-4.0.5.GA\server\default\lib下找到了對應包,比較了一下版本果然版本不一樣。
          把項目下的el-api.jar,jsp-api.jar,servlet-api.jar刪除,重新啟動,問題解決。
          接著有同事提出,同樣在tomcat下開發也出現這種情況,經檢查是他本地tomcat版本跟大家的不一致。開發環境這地方沒做到很好的統一。

          這樣問題是解決了,但是有一點就想不明白了,按照java的類加載委托機制,推測應該是先從jboss-4.0.5.GA\server\default\lib加載,如果加載不到的話再用當前類加載器加載WEB-INF\lib下的jar包,所有如果jboss下有jar包WEB-INF\lib下的應該不起作用,也有不會有沖突了。
          難到情況不是這樣的?

          一直想著找tomcat源碼分析一下來著,拖了好久。趕上這兩天不忙,就把apache-tomcat-6.0.33-src源文件弄了一份,debug看看到底怎樣。
          tomcat的類加載器結構和其他java項目是一致的。見圖一


          圖一
          其類圖見圖二

          圖二
          elipse debug截圖倒過來看跟這個就一樣了。
          Tomcat 通過Lifecycle接口來實現容器生命周期的統一管理,跟類加載器關系不大,這里就不討論了。
          通過這樣大容器啟動的時候啟動子容器,逐級加載。其結構關系跟server.xml描述的基本一致,詳細可以參考我的上一篇文章

          Tomcat6結構分析
          http://www.aygfsteel.com/zyskm/archive/2011/10/24/361870.html

          (這個編輯工具不太會用,樣式難看點,湊合看了)
          每個容器都有自己的類加載器,在默認情況下都是StandardClassLoader的實例。
          委托機制也和標準的java實現沒什么兩樣。

          接著往下看項目對應的類加載
          StandardContext.start();調用WebappLoader.start()開始加載項目,WebappLoader又通過創建一個WebappClassLoader實例進行類加載。
          WebappClassLoader.loadClass()實現依然波瀾不驚,規規矩矩的先從緩存找,找不到調用findClass()進行加載。
          果然這里實現有點不同,是先自己找,找不到再委托上級查找。和java默認的加載方式不同。
          見源代碼,只留下原理部分,日志和調試信息都去掉了。

          public Class findClass(String name) throws ClassNotFoundException {
                  
          // 先自己加載類,找不到則請求parent來加載,注意這點和java默認的委托模式不同
                  Class clazz = null;
                  
          try {
                      
          if ((clazz == null)) {
                              clazz 
          = findClassInternal(name);
                      }

                      
          if ((clazz == null&& hasExternalRepositories && !searchExternalFirst) {
                              clazz 
          = super.findClass(name);
                      }

                      
          if (clazz == null{
                          
          throw new ClassNotFoundException(name);
                      }

                  }
           catch (ClassNotFoundException e) {
                      
          if (log.isTraceEnabled())
                          log.trace(
          "    --> Passing on ClassNotFoundException");
                      
          throw e;
                  }

                  
          return (clazz);
          }

          據此可以認為,在web項目WEB-INF\lib下的jar包優先級高于jboss,tomcat 下的lib.
          兩處版本不一致的話會導致程序異常。
          比較省事的辦法是WEB-INF\lib下不再保留重復的jar包,實在閑著沒事的話可以自己寫個類加載器替換tomcat下WebappClassLoader改變加載順序。
          但是還可能有隱患,WebappClassLoader權限較低,它加載的類只能訪問web應用下的資源,如果servlet-api.jar等包用到其他資源時可能出現異常。
          這個沒實際測過,只是推測。但是catalina要提供對整個容器的支持,servlet-api實現對http協議的封裝轉換用到外部資源的可能性很大。


          圖三 類加載器結構圖

          總結:
          sevlet-api.jar,jsp-api.jar,el-api.jar這類容器提供的jar包web項目下沒必要再保留一份了,容易出現版本不一致。

          附錄:
          查看tomcat源碼的時候可以看看how tomcat works這本書,很不錯,雖然老了點。

          作者:zyskm
          http://www.aygfsteel.com/zyskm

          posted @ 2011-12-06 13:43 zyskm 閱讀(10720) | 評論 (3)編輯 收藏

          spring聲明性事務常見問題分析(續)

          上一篇說明了一種spring事務配置方式,這次補上另一種。
          見配置文件:
          <!-- 事務攔截 -->
              
          <tx:advice id="txAdvice" transaction-manager="transactionManager">
                  
          <tx:attributes>
                      
          <tx:method name="get*" propagation="REQUIRED" read-only="true" />
                      
          <tx:method name="find*" propagation="REQUIRED" read-only="true" />
                      
          <tx:method name="search*" propagation="REQUIRED" read-only="true" />
                      
          <tx:method name="save*" propagation="REQUIRED" />
                      
          <tx:method name="modify*" propagation="REQUIRED" />
                      
          <tx:method name="send*" propagation="REQUIRED" />
                      
          <tx:method name="revoke*" propagation="REQUIRED" />
                      
          <tx:method name="del*" propagation="REQUIRED" />
                      
          <tx:method name="logging*" propagation="NOT_SUPPORTED" read-only="true" />
                      
          <tx:method name="*" propagation="SUPPORTS" read-only="true"  />
                  
          </tx:attributes>
              
          </tx:advice>
              
          <aop:config>
                  
          <aop:pointcut id="projectServiceOperation" expression="execution(* cn.ceopen.bss..*.service..*(..))" />
                  
          <aop:advisor pointcut-ref="projectServiceOperation" advice-ref="txAdvice" />
              
          </aop:config>

          重點說明兩點:
          1.<aop:pointcut id="projectServiceOperation" expression="execution(* cn.ceopen.bss..*.service..*(..))" />
          表示你要進行事務控制的類名。詳細資料可以查下 aspectj語法。
          配置完成一定要實際測試一下,我配置過 expression="execution(* cn.ceopen.bss..*.service.*(..))" 少了一個點,導致事務不起作用。
          導致項目很長一段時間事務方面沒經過嚴格測試。
          2.
          Spring的AOP事務管理默認是針對unchecked exception回滾。
          也就是默認對RuntimeException()異常極其子類進行事務回滾。
          在項目中定義公共的RuntimeException異常,避免每個開發人員隨意拋出異常。
          不然的話沒新定義一個異常,就要修改tx:method rollback-for 太麻煩了。

          總結:
          1.對事務配置進行檢查,對復雜嵌套的事務邏輯必要的時候debug到spring源碼中確認。
          2.定義統一異常類型
          3.同一個類調用自身方法,子方法的事務配置不起作用。解決方法見上一篇文章。
          http://www.aygfsteel.com/zyskm/archive/2011/11/11/363535.html

          作者: zyskm
          本文地址:
          http://www.aygfsteel.com/zyskm/archive/2011/11/30/365225.html

          posted @ 2011-11-30 17:35 zyskm 閱讀(3279) | 評論 (0)編輯 收藏

          客戶端緩存和服務器端緩存

          在開發中緩存是經常要用到的,什么東西都一遍遍的找數據庫問,數據庫表示壓力很大。
          緩存方案常見的有兩種,一種是在客戶端,也就是web開發中的瀏覽器。一種是在服務器端,以memcached為代表。

          瀏覽器緩存

          這幾天因為項目需了解了下瀏覽器緩存相關的知識。

          重點說一下HTML5 Storage技術,之前的那些技術都是浮云,存不了什么東西還賊難用,各瀏覽器不兼容。
          聽名字就知道這是這兩年剛出來的技術,相應的也就對瀏覽器版本要求比較高,低版本的瀏覽器不支持。對用公司內部用的話這個技術還是很不錯的。
          最多支持5M的容量,是個xml文件,備注:
          Html5 storage最大支持5M容量
          文件位置,根據系統用戶位置會有區別。
          C:\Documents and Settings\Administrator\Local Settings\Application Data\Microsoft\Internet Explorer\DOMStore\KF2G0R0O
          也就是說別人都能看到你的數據,所以不能存放需要保密的內容。
          每次用戶登錄的時候最好檢查一下緩存版本是否跟服務器一致。

          現在的項目中有個業務受理環節,設置的過程中需要大量使用一些基礎數據。類似分公司、部門、產品列表這類數據,不經常變動,反復到服務器請求的話對服務器來講是不小的壓力。
          業務受理的步驟很多,得點好幾個下一步才能把操作完成,半截數據放到數據庫里維護起來結構就復雜了。
          因此考慮采用本地緩存技術,比較來比較去HTML5 Storage是不錯的選擇,反正都是公司同事可以要求都使用IE8以上瀏覽器。

          下邊這段是js中用到的代碼

          //用jquery方式,為加載緩存到本地按鈕綁定方法
              $('#loadBtn').bind('click',function(event){
                  
          if(supports_html5_storage()){
                      localStorage.clear();
                      load();
                      alert(
          "數據已經緩存到本地!");
                      initCorpSelect();
                      initDptSelect(
          "-1");
                  }
          else{
                      alert(
          "You're out!");
                  }

                  
              }
          ) ;
              
          //清空緩存
              $('#clearBtn').bind('click',function(event){
                  
          if(supports_html5_storage()){
                      localStorage.clear();
                      alert(
          "本地緩存已經清空!");
                      initCorpSelect();
                      initDptSelect(
          "-1");
                  }
          else{
                      alert(
          "You're out!");
                  }

              }
          ) ;
          //加載緩存到本地瀏覽器
          function load(){
               
          var params = {
                  url:
          "${ctx}/crm/product/product/webstorage!ajaxLoad.do"
                  type: 
          "post",
                  dataType: 
          "json",
                  data: 
          null
                  success: 
          function(response){
                      
          var corps = response.corps;  
                      
          var dpts = response.dpts; 
                      
          var stations = response.stations; 

                      
          for(var i=0;i<corps.length;i++){
                          
          var corp = corps[i];
                          
          var jsCorp = new Corp(corp.corpCode,corp.corpName);
                          localStorage.setItem(
          "corp."+corp.corpCode, jsCorp.toJSONString()); 
                      }


                      
          for(var i=0;i<dpts.length;i++){
                          
          var dpt = dpts[i];
                          localStorage.setItem(
          "dpt."+dpt.dptCode, dpt.toJSONString()); 
                      }


                      
          for(var i=0;i<stations.length;i++){
                          
          var station = stations[i];
                          localStorage.setItem(
          "station."+station.stationCode, station.toJSONString()); 
                      }

                  }

              }
          ;                
              ajax(params);
          }


           

          服務器緩存

          緩存解決方案很多了,只說剛剛用到的memcached。
          memcached很好用了
          1.安裝啟動memcached
          2.寫一個memcached客戶端工具類,我用的是com.danga.MemCached.MemCachedClient 比較穩定,至于其他性能更高的目前沒有需求沒用到。
          3.加載數據到memcached
          4.程序中需要基礎數據的時候從memcached獲取。

          /**
           * memcached接口
           * 
          @author ce
           *
           
          */

          public interface Imemcached {
              
          /**
               * 可增加,可覆蓋
               
          */

              
          public abstract boolean set(Object key, Object value);
              
          public abstract boolean set(Object key, Object value,Date expire);
              
          /**
               * 只能增加;如果增加的key存在返回“false”
               
          */

              
          public abstract boolean add(Object key, Object value);
              
          public abstract boolean add(Object key, Object value,Date expire);

              
          public abstract boolean delete(Object key);

              
          /**
               * 只能修改;如果修改不存在的key返回“false”
               
          */

              
          public abstract boolean modify(Object key, Object value);

              
          /**
               * 如果key不存在返回null
               
          */

              
          public abstract Object getKey(Object key);

              
          /**
               * 清除服務器上所有數據
               
          */

              
          public abstract boolean flushAll();

              
          /**
               * 判斷服務器上是否存在key
               
          */

              
          public abstract boolean keyExists(Object key);

              
          /**
               * 一次獲取多個數據,減少網絡傳輸時間
               
          */

              
          public abstract Map<String, Object> getKeys(String[] keys);
              
              
              
          /**
               * 設置是否使用字符串方式
               
          */

              
          public abstract void setPrimitiveAsString(boolean flag);
          }


          實際測試結果
          查詢一張人員表,同時需要根據分公司編號,部門編號從分公司、部門表獲取名稱。
          使用緩存技術的時候,不管客戶端還是服務器端,因為只查詢單表,速度明顯比多表聯合查詢要快的多。
          在系統使用高峰的時候這種差別明顯能改善用戶的操作體驗。
          實際測試了以下幾種情況:

          1.查詢:
          直接通過表關聯的方式進行查詢,速度最慢,開發最簡便
          查詢結果列表中的公司名稱、部門名稱直接從數據庫查出。
          2.查詢(本地緩存),緩存數據到本地,清除本地緩存:
          速度最快,數據同步有一定工作量,需要高版本瀏覽器支持
          查詢結果列表中的公司名稱、部門名稱從本地緩存獲取,增加_IE標識以區別。
          3.查詢(服務器緩存),緩存數據到服務器,清除服務器緩存:
          速度也很快,也有數據同步問題,緩存數據到服務器比本地慢
          查詢結果列表中的公司名稱、部門名稱從服務器緩存獲取,增加_mem標識以區別。





          緩存使用總結

          1.采用緩存技術后系統性能有明顯改善,可以從性能測試工具明顯感受到(這不是廢話嘛,呵呵)。
          2.緩存的數據同步問題必須解決好
          3.項目中采用緩存技術需要統一規劃,各系統不能互相干擾。
          4.性能壓力不明顯的情況下,緩存開發要顯得復雜一點。


           
          作者: zyskm

          本文地址:
          http://www.aygfsteel.com/zyskm/archive/2011/11/30/364722.html

           

          posted @ 2011-11-30 16:25 zyskm 閱讀(6715) | 評論 (0)編輯 收藏

          spring聲明性事務常見問題分析

          聲明性事務是spring一個很重要的功能,可以避免開發陷入繁瑣的事務控制邏輯中。
          但是可能是用著太方便了很多人對spring事務原理并不清楚,有必要做一番分析。
          下邊以攔截器配置方式進行說明,tx標簽配置方式將在接下來另一篇文章做分析。
          一、首先看配置文件:
          <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
              
          <property name="sessionFactory" ref="sessionFactory" />
          </bean>
          <bean id="matchAllTxInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
              
          <property name="transactionManager">
                  
          <ref bean="transactionManager" />
              
          </property>
              
          <property name="transactionAttributes">
                  
          <props>
                      
          <prop key="get*">PROPAGATION_REQUIRED,readOnly,-Exception </prop>
                      
          <prop key="find*">PROPAGATION_REQUIRED,readOnly,-Exception </prop>
                      
          <prop key="search*">PROPAGATION_REQUIRED,readOnly,-Exception </prop>
                      
          <prop key="save*">PROPAGATION_REQUIRED,-Exception </prop>
                      
          <prop key="modify*">PROPAGATION_REQUIRED,-Exception </prop>
                      
          <prop key="send*">PROPAGATION_REQUIRED,-Exception </prop>
                      
          <prop key="revoke*">PROPAGATION_REQUIRED,-Exception </prop>
                      
          <prop key="del*">PROPAGATION_REQUIRED,-Exception </prop>
                      
          <prop key="logging*">PROPAGATION_NOT_SUPPORTED,readOnly,-Exception </prop>
                      
          <prop key="*">PROPAGATION_SUPPORTS,-Exception </prop>
                  
          </props>
              
          </property>
          </bean>
          <bean id="autoProxyCreator"
              class
          ="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
              
          <property name="interceptorNames">
                  
          <list><idref local="matchAllTxInterceptor" /></list>
              
          </property>
              
          <property name="proxyTargetClass"><value>true</value></property>
              
          <property name="beanNames">
                  
          <list><value>*Service</value></list>
              
          </property>
          </bean>

          配置第一步引入AOP代理autoProxyCreator,使用的是spring默認的jdk動態代理BeanNameAutoProxyCreator。
          有兩個屬性要介紹一下:
          1.攔截范圍beanNames;例子中攔截范圍是*Service,表示IOC容器中以Service結尾的bean,一般配置在spring.xml,serviceContext.xml之類的spring配置文件。
          要注意這里不是值src下邊的類。
          bean配置信息:
          <bean id="menuService" class="cn.ceopen.bss..service.impl.MenuServiceImpl"/>
          有圖有真相,下邊是BeanNameAutoProxyCreator 調試信息。




          2.截器interceptorNames
          interceptorNames定義事務屬性和事務管理器
          配置第二步就是定義事務屬性:事務傳播范圍、事務隔離級別
          事務屬性沒什么好說的,使用spring進行事務管理的都了解,不在這里詳細說了網上有大量資料。

          配置第三步,指定事務管理器
          這里用的是HibernateTransactionManager,spring提供對常見orm的事務支持。從spring源碼可以看出HibernateTransactionManager.doGetTransaction()同時支持hibernate和jdbc。
          支持hibernate和jdbc混合事務,不使用jta方式的話有個前提條件:使用同一個數據源,
          這里所說的同一個數據源,不僅僅指物理上是同一個,在spring配置文件中也要是同一個。
          我在開發中遇到過這個問題,最早定義了一個數據baseDataSource,hibernate和jdbc都使用此數據源,后來項目要求使用動態數據源就又配了一個數據源dynamicDataSource
          僅在hibernate下做了改動,未改動jdbc對應配置,出現了事務控制問題。
          出錯了事務配置:
          <bean id="sessionFactory"
                  class
          ="com.sitechasia.webx.dao.hibernate3.NamedMoudleHbmLocalSessionFactoryBean">
              
          <property name="dataSource" ref="dynamicDataSource" />
              
          <!--與主題無關,省略部分內容-->
          </bean>
          <bean id="dynamicDataSource" class="cn.ceopen.bss.pub.base.dao.RoutingDataSource">      
             
          <property name="targetDataSources">      
                
          <map key-type="java.lang.String">      
                   
          <entry key="baseDataSource" value-ref="baseDataSource"/>  
                
          </map>      
             
          </property>      
             
          <property name="defaultTargetDataSource" ref="baseDataSource"/>      
          </bean>
           
          <bean id="baseDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
              
          <!--與主題無關,省略部分內容-->
          </bean>
          <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 
              
          <!--dataSource應該與sessionFactor一致-->
              
          <property name="dataSource"><ref bean="baseDataSource"/></property> 
          </bean> 
          <bean id="abstractJdbcDao" abstract="true">
              
          <property name="jdbc" ref="jdbcTemplate" />
          </bean>
          dao配置文件:
          <bean id="actDao" class="cn.ceopen.bss.impl.ActDaoImpl" parent="abstractJdbcDao"/>
          dao中同時支持hibernate操作和jdbc操作。

          二、事務屬性傳播
          先看這樣一個列子:

          1.基于jdk動態代理的AOP事務控制,只能針對接口。
          在上邊的配置文件中設置的事務屬性對a3()都不起作用,a3()不能單獨設計事務屬性,只能繼承接口方法的事務屬性。
          2.類自身事務嵌套
          第一種情況:
          AbcIService abcService;
          BcdIService bcdService;
          abcService.a1();
          abcService.a2();
          bcdService.b1();
          這三個方法對應的事務屬性都起作用。

          第二種情況:
          方法定義
          public void a1() {
            bcdService.b1();
          }
          調用:
          abcService.a1();
          結果:
          abcService.a1();
          bcdService.b1();
          這兩個方法對應的事務屬性都起作用。

          第三種情況:
          方法定義
          public void a1() {
            this.a2();
          }
          調用:
          abcService.a1();
          結果:
          abcService.a1();
          abcService.a2();
          a2()對應的事務屬性配置不起作用。
          解決辦法:
          1.把a2()拆到另一個類中去;
              缺點:麻煩,會把相關業務邏輯拆分了
          2.調用是不用this.a2(),用abcService.a2();
          public void a1() {
            abcService.a2();
          }
          缺點:要在類中注入自身引用。
          原因分析:
          為什么會出現這種情況呢?
          我們在調用abcService.a1();時abcService是從IOC容器獲取的,并AbcServiceImpl而是它的動態代理AbcServiceProxy。
          示意圖如下,spring不一定是這么實現的但原理一樣.



          AbcServiceProxy.a()方法進行了AOP增強,根據配置文件中事務屬性增加了事務控制。
          public void a1() {
            this.a2();
          }
          this.a2()這里this指的是AbcIServiceImpl并沒用進行AOP增強,所以沒用應用事務屬性,只能繼承a1()的事務屬性。
          public void a1() {
            abcService.a2();
          }
          abcService則實際是AbcServiceProxy.a2()所以可以應用事務屬性。

          所以在類內部進行方法嵌套調用,如果被嵌套的方法a2()需要區別于嵌套方法a1()的事務屬性,需要:1.在接口公開;2.通過代理調用。


          作者:zyskm
          http://www.aygfsteel.com/zyskm

          posted @ 2011-11-11 16:33 zyskm 閱讀(1761) | 評論 (3)編輯 收藏

          <<七步掌握業務分析>>閱讀筆記

                上周在西單圖書城看到本不錯的書<<七步掌握業務分析>>,系統的講了些業務分析方面的知識,以前看到業務分析方面的資料都是站在需求捕獲分析的角度來寫,向這么完整系統介紹業務分析概念方法的不多。本著書非借不能讀的精神,我用了兩個周末在西單圖書大廈看完了整本書。記錄一下閱讀感受,同時結合一些自己的理解。
                  目錄結構見下圖,大概是這個意思,名詞記得不是很準確。


                  一個個說一下。
                  1.什么是業務分析
                  一般來講"什么是...?"這種問題都可以細分為這幾個問題,什么是業務分析、業務分析做什么(工作范圍)、跟系統分析有什么區別、什么人能做業務分析。
                  讓大家明白組織是怎么運作的,為了實現組織的目標提供解決方案。
                  實際開發過程中誰都可以做業務分析工作,目前很少看到這門業務分析師這一角色,一般是需求和系統分析師主要來做。根據個人能力的不同介入的程度不同。
                  說到范圍,業務分析主要從業務需求的角度來考慮,區別于系統分析更多的從系統能否實現、怎么實現的角度來考慮。很多時候系統分析師是同時兼這兩個角色的,優勢是考慮問題有統一方向,缺點的是容易互相干擾。通用的解決思路是顏色帽子法,做哪塊工作時戴那個帽子只想這部分的事情,先不想別的部分怎么辦,先過今天再說明天。每一部分單獨考慮后再進行綜合考慮,這樣工作才好開展,要不然一次想太多了事情就變得太復雜。
                  2.什么人來做
                  有人的地方就有江湖,既然在江湖里混就要了解這個江湖是什么人組成的,每個人的立場、對事情的認識程度、角色、權利、影響。對這些人有了認識,才能更好的認識人做的事情。
          這一點一般的需求書上只是提了一下,不想這本書就組織架構、角色關系、溝通技巧各方面做了闡述。有個理論認識實際操作起來更容易有方向感。
                  3.項目做什么
                  很多開發人員經常是領導讓做什么就做什么,作為一個業務分析師顯然不能這么干。
                  業務分析師很多時候起的是一個導游的角色,探險的時候大家全兩眼一抹黑只能掉溝里了。
                  誰發起的項目,項目要達成什么目標,對哪些人有益,對哪些人不利。這些都要了解,在項目的過程中才知道從哪方面去推動,避開可能會引起的麻煩。
                  4.了解業務
                  這個是當然了,業務分析師嘛,具體方法先不寫了,都是寫常見的,重要的是要經常實際總結提高。
                  5.了解業務環境
                  這也是一個江湖,更多的是從事情的角度進行分類
                  6.了解技術環境
                  業務分析雖然不用寫代碼技術還是要懂一點的,好的服裝設計師都會自己做衣服(這可不是隨便說的,我見過幾個,呵呵)。
                  7.提升自身價值
                  洞悉事態,能夠提出完整解決方案,這樣的人當然是有價值的。做得越好越有價值。
                  書中提到了幾種提高技能方法:時間管理和一些分析技術,包括溝通技巧。
          先寫這么多。

          作者:zyskm
          http://www.aygfsteel.com/zyskm

          posted @ 2011-11-02 17:18 zyskm 閱讀(1849) | 評論 (2)編輯 收藏

          基于spring的動態數據源

          基于spring的動態數據源
          項目過程中遇到這樣一個需求,系統啟動后動態設置數據源,不同用戶登錄系統后訪問的數據庫不同。
          好在所有書庫結構一致。
          用spring 的AbstractRoutingDataSource解決了這個問題。
          原理如圖:

          項目采用的是hibernate,直接在spring.xml設置sessionFactory的dataSource屬性為動態數據源即可。
          因為項目所有數據庫結構都一致,為了避免每次設置數據源的時候要改一堆參數,修改了spring AbstractRoutingDataSource類增加了一個getTargetDataSources方法,獲取當前數據源詳細信息,在其基礎上修改數據庫名稱、用戶名、密碼即可,不用每次設置一堆參數。
          Map<String,ComboPooledDataSource> targetDataSources=dynamicDataSource.getTargetDataSources();
                          
          if(targetDataSources==null){
                              targetDataSources
          =new HashMap<String, ComboPooledDataSource>();
                              targetDataSources.put(
          "baseDataSource", baseDataSource);
                          }

                          targetDataSources.put(dataSourceName, subSystemDataSource);
                          dynamicDataSource.setTargetDataSources(targetDataSources);
                          dynamicDataSource.afterPropertiesSet();
          另外,設置AbstractRoutingDataSource參數后要調用afterPropertiesSet()方法,spring容器才會進行加載操作。

          在動態設置數據源方面,可以通過兩種方式實現:
          1.在action(項目使用struts)中進行設置,可以確保在每個servlet線程中數據源是一致的。
          2.以aop方式,對service方法進行攔截,根據需求設置不同數據源。

          posted @ 2011-10-24 17:11 zyskm 閱讀(3583) | 評論 (2)編輯 收藏

          開發過程注意事項(數據庫方面)

          在開發過程中數據庫的操作和使用要有一定規范,不然會引起混亂。
          下邊是我們開發中具體例子,因為涉及公司在用項目詳細代碼就不列出來了,整個思路可供參考。

          1.初始化腳本
          各子系統建立自己的數據庫初始化腳本,格式可參照附件產品管理初始化腳本sql.rar(需要解壓到c:/sql才能在命令行執行,見init.sql說明)
          包括兩部分內容:1.建表語句(ddl);2.基礎數據初始化(data)
          建表語句由數據庫設計文檔(PowerDesigner)導出,基礎數據由excel文件導出(sql.rar提供示例,開發框架提供工具支持DbUtilTest.testExcel2Sql ())
          作用:
          1.數據庫結構和基礎數據文檔化
          2.便于快速搭建開發測試環境,新建一套環境時不用拷貝原數據庫而是執行腳本
          3.便于獨立開發測試,有一個干凈的數據,避免開發測試依賴歷史數據和調試過程中互相影響
          2.數據庫結構比對
          在部署多套數據庫時,懷疑表結構不一致,可使用DbUtilTest.testCompareDataBase ()進行檢查。
          執行后會在子系統根目錄生成dbcompare.html 文件,參加附件。
          說明:紅色表示兩個表結構不一致,綠色表示多出一表,黑色表示一致
           
          3.數據庫字符集(UTF-8)
          create database dbname CHARACTER SET utf8 COLLATE utf8_bin
          不單獨對表和字段設置字符集,整個庫統一使用utf-8
          4.表名字段名
          建庫腳本中表名和字段名不區分大小寫
          5.數據庫引擎(InnoDb)
          目前主要使用的兩種引擎MyIsam,InnoDb。MyIsam查詢較快,不支持事務。InnoDb支持事務。
          在建表sql指明引擎。
          create table ***
          (
             id bigint(11) not null auto_increment,
          .........
             primary key (id)
          )
          type = innodb;
          powerdesiner按如下方式設置:

          posted @ 2011-10-24 16:24 zyskm 閱讀(213) | 評論 (0)編輯 收藏

          集群環境下log4j日志文件命名方式

              在項目中,通過log4j可以實現對不同模塊不同級別日志輸出到不同的日志文件中。
          以xml格式為例:
          <appender name="FILE_INFO" class="org.apache.log4j.RollingFileAppender">
              
          <param name="File" value="/usr/local/logs/xxx_info.log"/>
              
          <layout class="org.apache.log4j.PatternLayout">
                  
          <param name="ConversionPattern" value="%d %p [%c] [%M]:%L - %m%n"/>
              
          </layout>
              
          <filter class="org.apache.log4j.varia.LevelRangeFilter">
                  
          <param name="levelMin" value="INFO" />
                  
          <param name="levelMax" value="INFO" />
                  
          <param name="AcceptOnMatch" value="true" />
              
          </filter>
          </appender>
          按這種方式在單機環境下可以很方便的查詢日志。
          但是在集群環境下,因為項目同時部署在多個機器上,log4j生成的日志文件在每臺機器上都叫xxx_info.log,在開發維護的過程中很難區分那個文件是哪臺服務上的,尤其在日志查詢比較頻繁的情況下做區分有一定的工作量。
              為解決這一問題,可以在生成日志文件時增加機器標識。如100_xxx_info.log,標識ip為172.20.80.100這臺機器上的。
          實現這一功能需要對log4j的Appender做些改動。
          通過查看log4j源碼可以看出來所有的文件輸出都會繼承FileAppender ,修改setFile(String file)方法即可實現。
          重寫DailyRollingFileAppender,改繼承重寫后的fileappender。
          /** 從配置文件讀取日志文件名 */
              
          public void setFile(String file) {
                  String val 
          = file.trim();
                  
          int index = val.lastIndexOf('/'+ 1;
                  String path 
          = val.substring(0, index);
                  String name 
          = val.substring(index);
                  fileName 
          = path + getIpSuffix() + "_" + name;
              }


              
          // 獲取本機ip最后一位(雙網卡內網ip)
              private String getIpSuffix() {
                  String ip 
          = null;
                  String suffix 
          = "";
                  NetworkInterface ni;
                  
          try {
                      ni 
          = NetworkInterface.getByName("eth1");
                      
          if (ni == null)
                          ni 
          = NetworkInterface.getByName("eth0");
                      Enumeration
          <InetAddress> ias = ni.getInetAddresses();
                      
          for (; ias.hasMoreElements();) {
                          InetAddress ia 
          = ias.nextElement();
                          
          if (ia instanceof InetAddress) {
                              ip 
          = ia.getHostAddress();
                          }

                      }

                      
          if (ip == null{
                          InetAddress addr 
          = InetAddress.getLocalHost();
                          ip 
          = addr.getHostAddress();
                      }

                  }
           catch (SocketException e1) {
                      e1.printStackTrace();
                  }
           catch (UnknownHostException e) {
                      e.printStackTrace();
                  }

                  
          if (ip != null{
                      
          int index = ip.lastIndexOf('.'+ 1;
                      suffix 
          = ip.substring(index);
                  }

                  
          return suffix;
              }


          posted @ 2011-10-24 15:33 zyskm 閱讀(3422) | 評論 (6)編輯 收藏

          mysql字符集和校對規則

          字符集的概念大家都清楚,校對規則很多人不了解,一般數據庫開發中也用不到這個概念,mysql在這方便貌似很先進,大概介紹一下。
          簡要說明
          字符集和校對規則
          字符集是一套符號和編碼。校對規則是在字符集內用于比較字符的一套規則。
          MySql在collation提供較強的支持,oracel在這方面沒查到相應的資料。
          不同字符集有不同的校對規則,命名約定:以其相關的字符集名開始,通常包括一個語言名,并且以_ci(大小寫不敏感)、_cs(大小寫敏感)或_bin(二元)結束
          校對規則一般分為兩類:
          binary collation,二元法,直接比較字符的編碼,可以認為是區分大小寫的,因為字符集中'A'和'a'的編碼顯然不同。
          字符集_語言名,utf8默認校對規則是utf8_general_ci
          mysql字符集和校對規則有4個級別的默認設置:服務器級、數據庫級、表級和連接級。
          具體來說,我們系統使用的是utf8字符集,如果使用utf8_bin校對規則執行sql查詢時區分大小寫,使用utf8_general_ci 不區分大小寫。不要使用utf8_unicode_ci。
          如create database demo CHARACTER SET utf8; 默認校對規則是utf8_general_ci 。
           
          Unicode與UTF8
          Unicode只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲.
          UTF8字符集是存儲Unicode數據的一種可選方法。 mysql同時支持另一種實現ucs2。

          詳細說明

          字符集(charset):是一套符號和編碼。
          校對規則(collation):是在字符集內用于比較字符的一套規則,比如定義'A'<'B'這樣的關系的規則。不同collation可以實現不同的比較規則,如'A'='a'在有的規則中成立,而有的不成立;進而說,就是有的規則區分大小寫,而有的無視。
          每個字符集有一個或多個校對規則,并且每個校對規則只能屬于一個字符集。

          binary collation,二元法,直接比較字符的編碼,可以認為是區分大小寫的,因為字符集中'A'和'a'的編碼顯然不同。除此以外,還有更加復雜的比較規則,這些規則在簡單的二元法之上增加一些額外的規定,比較就更加復雜了。
          mysql5.1在字符集和校對規則的使用比其它大多數數據庫管理系統超前許多,可以在任何級別進行使用和設置,為了有效地使用這些功能,你需要了解哪些字符集和 校對規則是可用的,怎樣改變默認值,以及它們怎樣影響字符操作符和字符串函數的行為。

          校對規則一般有這些特征:

           兩個不同的字符集不能有相同的校對規則。
           每個字符集有一個默認校對規則。例如,utf8默認校對規則是utf8_general_ci。
           存在校對規則命名約定:它們以其相關的字符集名開始,通常包括一個語言名,并且以_ci(大小寫不敏感)、_cs(大小寫敏感)或_bin(二元)結束


          確定默認字符集和校對
           字符集和校對規則有4個級別的默認設置:服務器級、數據庫級、表級和連接級。
           數據庫字符集和校對
           每一個數據庫有一個數據庫字符集和一個數據庫校對規則,它不能夠為空。CREATE DATABASE和ALTER DATABASE語句有一個可選的子句來指定數據庫字符集和校對規則:
           例如:
           CREATE DATABASE db_name    DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;
           MySQL這樣選擇數據庫字符集和數據庫校對規則:
          ·         如果指定了CHARACTER SET X和COLLATE Y,那么采用字符集X和校對規則Y。
          ·         如果指定了CHARACTER SET X而沒有指定COLLATE Y,那么采用CHARACTER SET X和CHARACTER SET X的默認校對規則。
          ·         否則,采用服務器字符集和服務器校對規則。
          在SQL語句中使用COLLATE
          •使用COLLATE子句,能夠為一個比較覆蓋任何默認校對規則。COLLATE可以用于多種SQL語句中。
          使用WHERE:
           select * from pro_product where product_code='ABcdefg' collate utf8_general_ci
          Unicode與UTF8
          Unicode只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲.Unicode碼可以采用UCS-2格式直接存儲.mysql支持ucs2字符集。
          UTF-8就是在互聯網上使用最廣的一種unicode的實現方式。其他實現方式還包括UTF-16和UTF-32,不過在互聯網上基本不用。
          UTF8字符集(轉換Unicode表示)是存儲Unicode數據的一種可選方法。它根據 RFC 3629執行。UTF8字符集的思想是不同Unicode字符采用變長字節序列編碼:
          ·         基本拉丁字母、數字和標點符號使用一個字節。
          ·         大多數的歐洲和中東手寫字母適合兩個字節序列:擴展的拉丁字母(包括發音符號、長音符號、重音符號、低音符號和其它音符)、西里爾字母、希臘語、亞美尼亞語、希伯來語、阿拉伯語、敘利亞語和其它語言。
          ·         韓語、中文和日本象形文字使用三個字節序列。

          posted @ 2011-10-24 14:59 zyskm 閱讀(1815) | 評論 (3)編輯 收藏

          僅列出標題
          共5頁: 上一頁 1 2 3 4 5 下一頁 
          主站蜘蛛池模板: 青川县| 环江| 北安市| 昌宁县| 绩溪县| 会宁县| 巴里| 穆棱市| 昌乐县| 大名县| 教育| 白山市| 南陵县| 肥乡县| 十堰市| 巴林左旗| 南开区| 凤凰县| 二手房| 东方市| 门源| 柘城县| 泾源县| 井陉县| 青田县| 屏山县| 谢通门县| 拜泉县| 肇源县| 萝北县| 正镶白旗| 固阳县| 铁岭县| 西城区| 新龙县| 辰溪县| 黔西| 九寨沟县| 江永县| 黑山县| 高陵县|