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.blogjava.net/zyskm/archive/2011/12/06/365653.html 就不重复了。
第九章讲session管理,没什么特别的。
第十章讲安全,没看。
第十一章讲StandardWrapper,在第五章的基础上重点分析了wrapper的运作机制。
其余章节目前工作中用不到,有空再看了。

作者:zyskm
http://www.blogjava.net/zyskm

posted @ 2011-12-06 16:25 zyskm 阅读(3142) | 评论 (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.blogjava.net/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.blogjava.net/zyskm

posted @ 2011-12-06 13:43 zyskm 阅读(10658) | 评论 (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.blogjava.net/zyskm/archive/2011/11/11/363535.html

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

posted @ 2011-11-30 17:35 zyskm 阅读(3271) | 评论 (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.blogjava.net/zyskm/archive/2011/11/30/364722.html

 

posted @ 2011-11-30 16:25 zyskm 阅读(6699) | 评论 (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.blogjava.net/zyskm

posted @ 2011-11-11 16:33 zyskm 阅读(1749) | 评论 (3)编辑 收藏

<<七步掌握业务分析>>阅读笔记

      上周在西单图书城看到本不错的书<<七步掌握业务分析>>,系统的讲了些业务分析方面的知识,以前看到业务分析方面的资料都是站在需求捕获分析的角度来写,向这么完整系统介绍业务分析概念方法的不多。本着书非借不能读的精神,我用了两个周末在西单图书大厦看完了整本书。记录一下阅读感受,同时结合一些自己的理解。
        目录结构见下图,大概是这个意思,名词记得不是很准确。


        一个个说一下。
        1.什么是业务分析
        一般来讲"什么是...?"这种问题都可以细分为这几个问题,什么是业务分析、业务分析做什么(工作范围)、跟系统分析有什么区别、什么人能做业务分析。
        让大家明白组织是怎么运作的,为了实现组织的目标提供解决方案。
        实际开发过程中谁都可以做业务分析工作,目前很少看到这门业务分析师这一角色,一般是需求和系统分析师主要来做。根据个人能力的不同介入的程度不同。
        说到范围,业务分析主要从业务需求的角度来考虑,区别于系统分析更多的从系统能否实现、怎么实现的角度来考虑。很多时候系统分析师是同时兼这两个角色的,优势是考虑问题有统一方向,缺点的是容易互相干扰。通用的解决思路是颜色帽子法,做哪块工作时戴那个帽子只想这部分的事情,先不想别的部分怎么办,先过今天再说明天。每一部分单独考虑后再进行综合考虑,这样工作才好开展,要不然一次想太多了事情就变得太复杂。
        2.什么人来做
        有人的地方就有江湖,既然在江湖里混就要了解这个江湖是什么人组成的,每个人的立场、对事情的认识程度、角色、权利、影响。对这些人有了认识,才能更好的认识人做的事情。
这一点一般的需求书上只是提了一下,不想这本书就组织架构、角色关系、沟通技巧各方面做了阐述。有个理论认识实际操作起来更容易有方向感。
        3.项目做什么
        很多开发人员经常是领导让做什么就做什么,作为一个业务分析师显然不能这么干。
        业务分析师很多时候起的是一个导游的角色,探险的时候大家全两眼一抹黑只能掉沟里了。
        谁发起的项目,项目要达成什么目标,对哪些人有益,对哪些人不利。这些都要了解,在项目的过程中才知道从哪方面去推动,避开可能会引起的麻烦。
        4.了解业务
        这个是当然了,业务分析师嘛,具体方法先不写了,都是写常见的,重要的是要经常实际总结提高。
        5.了解业务环境
        这也是一个江湖,更多的是从事情的角度进行分类
        6.了解技术环境
        业务分析虽然不用写代码技术还是要懂一点的,好的服装设计师都会自己做衣服(这可不是随便说的,我见过几个,呵呵)。
        7.提升自身价值
        洞悉事态,能够提出完整解决方案,这样的人当然是有价值的。做得越好越有价值。
        书中提到了几种提高技能方法:时间管理和一些分析技术,包括沟通技巧。
先写这么多。

作者:zyskm
http://www.blogjava.net/zyskm

posted @ 2011-11-02 17:18 zyskm 阅读(1818) | 评论 (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 阅读(3570) | 评论 (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 阅读(201) | 评论 (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 阅读(3378) | 评论 (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 阅读(1794) | 评论 (3)编辑 收藏

仅列出标题
共5页: 上一页 1 2 3 4 5 下一页