<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>skydream</title>
    <description>java 工程师，工作6年，混迹广州。</description>
    <link>http://skydream.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>loadrunner license设置问题</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/173942" style="color:red;">http://skydream.javaeye.com/blog/173942</a>&nbsp;
          发表时间: 2008年03月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          初学loadrunner，今天准备用这个工具测试一下手头的一个cs服务器，想用java Vuser来调用。边学边用吧，先简单写了一个脚本，就是打印“HelloWorld”，然后在脚本编辑器里面运行是通过了，但在场景控制器里面并发运行这个脚本，报如下错误：<br />“You do not have a for this Vuser type.Please contact Mercury Interactive to renew your license.”<br /><br />    目前用的loadrunner是最新的9.0版本，具体的破解方式请google，基本原来是利用8.1版本的破解方式（同样感谢HP公司的大度！）。使用的License也就是目前网络上比较通用的两个：<br />global 100user<br />AEAMAUIK-YAFEKEKJJKEEA-BCJGI<br />10000 web clients<br />AEABEXFR-YTIEKEKJJMFKEKEKWBRAUNQJU-KBYGB<br /><br />    但是使用java Vuser时，还是发现出现了没有License的情况，经过一番折腾，发现是自己的设置不对，google了一下发现很多人似乎犯了和我类似的错误，整理一下分享出来，避免后来人继续犯错。<br /><br />    具体说，是loadrunner的License管理器，只支持一个License，我先后输入了上面的两个License，最后实际生效的只有最后一个 10000 web clients的。而loadrunner的不同协议是要求不同的License的，上面的10000 web clients是不能用于java Vuser的（估计其他协议也会遇到同样问题）。因此必须将License修改为global 100user，这样java Vuser就可以跑起来了。注意修改License后要关闭现有的loadrunner程序然后再重新打开，否则License依然无效。如果需要测试不同的协议，则视具体需要自己动手设置不同的License了，没有办法，买不起啊，loadrunner的license简直是天价。<br /><br />这样就出现一个问题，如果要测试100以上的java Vuser就没有办法了，google一遍网络没有发现更大更好的License，只能使用这个，限制在100了。好在最常用的 web clients有10000，怎么也够用了。<br /><br />补充：刚google到一个500 VU的LoadRunner 8.0 Global licence，尽管已经过期，但是可以通过修改机器时间来正常使用。我将机器时间设置为2003年2月，可以输入这个license，虽然 loadrunner给出警告，但是重起后可以正常使用。我试了一下200vu可以跑java vuser，看来这也是一个突破100vu限制的办法，虽然修改时间的方法恶心了点。<br />licence:<br />BGAUGLIX-AJGI-AEIEKEKJJKEAFJP-BDFHW<br />Valid until 31. 十月 2003
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/173942#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 20 Mar 2008 00:04:20 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/173942</link>
        <guid>http://skydream.javaeye.com/blog/173942</guid>
      </item>
      <item>
        <title>linux下安装apache + subversion</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/173940" style="color:red;">http://skydream.javaeye.com/blog/173940</a>&nbsp;
          发表时间: 2008年03月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          最近准备开工一个大项目，给自己练手用，考虑需要保存源代码，又不方便放到公司的cvs上。因此决定安装一个给自己用，顺便学习一下 subversion。简单翻了一下资料，决定使用apache + subversion的方式，比较适合我，而且这种方式的好处是可以用浏览器就直接访问，比较方便浏览。<br /><br />    整理了一下安装配置过程，给新手一个简单可行的参考。<br /><br />一.首先安装apache服务器:<br />1. 下载最新的apache 2.2.6<br />    httpd-2.2.6.tar<br />2. 安装<br />    ./configure --prefix=/data/aoxj/soft/svn/apache --enable-so --enable-dav<br />    make<br />    make install<br /><br />    --prefix指定安装目录，注意一定要加--enable-so和--enable-dav<br /><br />安装后修改apache/conf/httpd.conf文件，修改Listen 80为其他端口。以后就通过这个端口访问apache，而且基本上这个apache是为subversion专用的.<br /><br />二. 然后安装subversion<br />1. 下载最新的subversion-1.4.6.tar<br />2. 安装<br />  <br /><pre name="code" class="java">./configure --prefix=/data/aoxj/soft/svn/subversion --with-apache=/data/aoxj/soft/svn/apache --with-apxs=/data/aoxj/soft/svn/apache/bin/apxs --with-apr=/data/aoxj/soft/svn/apache/bin/apr-1-config --with-apr-util=/data/aoxj/soft/svn/apache/bin/apu-1-config
make
make install</pre><br /><br />3. 为了方便使用subversion的命令，将subversion安装目录下的bin目录加入到Path中<br /><br />   <br />三. 配置subversion<br />首先要创建一个资料库（我准备使用单资料库的方式），使用svnadmin增加资料库<br />./svnadmin create /data/aoxj/soft/svn/svnroot<br /><br />再建立一个client目录，用于客户端获取文件，测试和打包用。<br />这样在svn总目录下就有apache  client  subversion  svnroot四个目录，分别是apache/subversion的安装目录，subversion的资料库和客户端目录。<br /><br /><pre name="code" class="java">aoxj@linux:~/soft/svn> ls
apache  client  subversion  svnroot</pre><br /><br />注意这里采用的是apache + subversion的方式，不使用svnserver，因此不需要修改资料库下的conf/svnserve.conf文件，改了也没有用。<br /><br />四. 配置apache<br /><br />打开apache的conf/httpd.conf，注意用前面的安装方法安装subversion后，已经自动修改了apache的conf/httpd.conf文件，增加了<br /><br /><pre name="code" class="java">LoadModule dav_svn_module     modules/mod_dav_svn.so
LoadModule authz_svn_module   modules/mod_authz_svn.so</pre><br /><br /><br />相应的so文件也自动copy到了apache/modules。这些工作就不用自己动手了。<br /><br />需要自己动手修改apache下的httpd.conf，增加以下内容<br /><pre name="code" class="java">&lt;Location /svn>
    DAV svn
    SVNPath /data/aoxj/soft/svn/svnroot
&lt;/Location></pre><br /><br /><br />注意这里用的是SVNPath，因为我要使用单资料库的方式，如果需要多个资料库，可以设置为SVNParentPath.<br /><br />五. 验证安装<br /><br />打开浏览器，输入地址为http://服务器ip: apache启动端口/svn<br />如果可以正常打开页面则说明安装配置正常，可以正常使用了，在页面上可以看到<br />Powered by Subversion version 1.4.6 (r28521).<br />由于目前资料库中没有内容，因此看到的内容为空。<br /><br />六. 提交代码<br />简单验证一下功能，打开eclipse（已经安装好了subversion插件），建立一个测试项目，然后提交，轻松搞定。<br />用浏览器可以直接看到提交的项目和代码，ok，安装完毕.
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/173940#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 19 Mar 2008 23:59:55 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/173940</link>
        <guid>http://skydream.javaeye.com/blog/173940</guid>
      </item>
      <item>
        <title>spring和依赖注入的价值</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/155177" style="color:red;">http://skydream.javaeye.com/blog/155177</a>&nbsp;
          发表时间: 2008年01月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          看到有帖子，置疑spring和依赖注入的价值，回复内容整理如下：<br /><br />依赖注入对设计有利，而spring则促进了依赖注入的使用。<br /><br />如果业务处理类，它所使用的倚赖，都是依靠在这个类内部实现或者查找，那么必然使得正常的业务逻辑和获取依赖的方法混在一起。<br /><br />我取个最简单的场景，某个注册的工作类，它需要获取当前"容许的用户名的最大长度"，这个依赖非常简单吧？基本每个注册类都有这个限制，我们现在把场景考虑的全面一点，对于复杂一点的系统，这个最大长度的限制可能来源很多，比如配制文件，数据库，可能类工作在前台比如web而配制在后台，可能需要和第三放系统一起工作而需要到第三方系统中获取而对方只提供web service...<br /><br />这么一个简单的依赖，“用户名的最大长度”，如果用依赖注入，只要一个简单的setUsernameMaxLength()方法就可以搞定。考虑上面那么多种可能都出现，最恶劣的情况是要求一个系统可以同时支持然后通过配制方式进行，这对于将一个产品卖给n家客户的公司来说是最正常不过的要求。<br /><br />那么我们来看一个很有"spring"风格的采用依赖注入的设计，通常都将会是这样：<br /><br />注册工作类：<br />public void RegisterWork {<br />    public void setUsernameMaxLengthProvider(UsernameMaxLengthProvider provider) {<br />        int maxLength = provider.getUsernameMaxLength(); //简单获取结果，不管provider细节<br />    }<br />    ....<br />}<br />“用户名的最大长度”的提供者接口<br />public interfacd UsernameMaxLengthProvider {<br />    public int getUsernameMaxLength();<br />}<br />“用户名的最大长度”的提供者则可以有以下实现，都只负责读取数据，不关心数据被谁使用，怎么使用：<br />1.直接提供，可以用spring 构造函数方式注入usernameMaxLength值，也可以junit测试时直接new DirectdProvide对象<br />public class DirectdProvider implements UsernameMaxLengthProvider  {<br />    private int usernameMaxLength = 10;<br />    public int getUsernameMaxLength() {<br />        return UsernameMaxLength;<br />    }<br />    public DirectdProvider (int usernameMaxLength) {<br />        this.usernameMaxLength = usernameMaxLength;<br />    }<br />}<br />2.读本地配制文件<br />public class LocalConfigFileProvider implements UsernameMaxLengthProvider  {    <br />    public void read(File configFile) {<br />        usernameMaxLength = ....<br />    }<br />}<br />3.类似的从数据库读取 DatabaseProvide<br />4.类似的web service从第三方读取 WebServiceProvider<br />5.其他的可能扩展的方式<br /><br />开发时逻辑清晰，代码可读性超强，基本看类名和函数名搞定。测试时，轻松测试RegisterWork，testcase中只要worker.setUsernameMaxLengthProvider(new DirectdProvider(20))就搞定。而针对UsernameMaxLengthProvider的几个实现类，更是轻松使用junit，每个都覆盖一遍。<br /><br />呵呵，现在我们可以砍刀，最简单的一个int型的“用户名的最大长度”，都可能遭遇如此复杂的场景。如果不用倚赖注入，而是选择在RegisterWork中自己搞定“用户名的最大长度”的获取，那么可能要遇到以下问题：<br />1. RegisterWork极其复杂，可以想像类似的依赖肯定还有其他<br />2. 获取“用户名的最大长度”的方式和注册的业务处理逻辑完全没有直接联系，对注册过程来说它只关注结果，“用户名的最大长度”是10还是20，而不是到底读本地文件还是读数据库。喧宾夺主了，次要逻辑干扰了主要逻辑<br />3. 难于测试。获取“用户名的最大长度”的方式的这些代码，被藏在RegisterWork类中，而且很有可能是private方法不对外暴露（如果不依赖注入还需要暴露吗？暴露给谁呢），怎么用mock测试？怎么能保证以上几种的实现都覆盖到？<br />4. 难于扩展。就算上面都搞定了，某一天突然来了一个变态需求，要求用ldap从另一个地方取配置呢？难道再把ldap请求的那一大片代码也写到RegisterWork里面？<br />5. 更难于被第三方扩展。运气好，上面这个ldap取配制的变态需求客户开始没有要求。顺利开发完成测试通过然后准备上线，最后一晚了客户才发现，"哦，给忘了，你们想办法给加上，快，快，明天一早就要上线运行了...你们写死在代码里面了？那只能你们修改了原代码了"。吐血了吧，先骂一顿，可是活还的干啊，咬牙切齿的把新的实现代码加上了，还得编译打包更新重启...如果是spring多好，单独写一个LdapProvider类，测试（这个测试比杂在RegisterWork里面测试简单的多）通过后单独提供这个class仍classpath下，修改spring的配制将原来的***Provider替换掉，轻松搞定，甚至可以把这活仍给客户的开发人员，告诉他们怎么替换就可以了，管你ladp还是其他，谁让你们需求不明确，自己扩展去。<br />6. 容易出错。刚吐血完成上面的变态需求，更新完毕，一会客户电话来了，“...怎么...不正常了？”。又吐血几升地检查，终于找出来了，原来是刚才写ladp访问的代码时不小心改错了RegisterWork的一个地方，谁让RegisterWork类有几十上百个方法好几千行呢，一时急，又没有测试到......可是客户不会理解的。<br /><br />上述的场景，spring + 依赖注入的设计方式，优点很明显吧。<br /><br />再考虑一下维护和二次开发的问题，上面的spring + 依赖注入的代码，好看易懂，方便扩展，维护起来轻松。如果是那么堆在RegisterWork里面，在那个大堆中代码要找出这些代码并读懂，估计不是件轻松的事情。<br /><br />代码维护是需要成本的，写出易于维护的代码，是一个优秀程序员的基本素养，至少，不能让下一个接手的人骂娘吧？
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/155177#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 11 Jan 2008 22:26:10 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/155177</link>
        <guid>http://skydream.javaeye.com/blog/155177</guid>
      </item>
      <item>
        <title>jdk小工具jps介绍</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/151897" style="color:red;">http://skydream.javaeye.com/blog/151897</a>&nbsp;
          发表时间: 2007年12月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          jdk小工具jps介绍<br />jps(Java Virtual Machine Process Status Tool)是JDK 1.5提供的一个显示当前所有java进程pid的命令，简单实用，非常适合在linux/unix平台上简单察看当前java进程的一些简单情况。<br /><br />jps存放在JAVA_HOME/bin/jps，使用时为了方便请将JAVA_HOME/bin/加入到Path.<br /><br />$> jps<br />23991 Jps<br />23789 BossMain<br />23651 Resin<br /><br /><br />比较常用的参数：<br /><br />-q 只显示pid，不显示class名称,jar文件名和传递给main 方法的参数<br /><br />$>  jps -q<br />28680<br />23789<br />23651<br /><br /><br />-m 输出传递给main 方法的参数，在嵌入式jvm上可能是null<br /><br />$> jps -m<br />28715 Jps -m<br />23789 BossMain<br />23651 Resin -socketwait 32768 -stdout /data/aoxj/resin/log/stdout.log -stderr /data/aoxj/resin/log/stderr.log<br /><br /><br />-l 输出应用程序main class的完整package名 或者 应用程序的jar文件完整路径名<br /><br />$> jps -l<br />28729 sun.tools.jps.Jps<br />23789 com.asiainfo.aimc.bossbi.BossMain<br />23651 com.caucho.server.resin.Resin<br /><br /><br />-v 输出传递给JVM的参数<br /><br />$> jps -v<br />23789 BossMain<br />28802 Jps -Denv.class.path=/data/aoxj/bossbi/twsecurity/java/trustwork140.jar:/data/aoxj/bossbi/twsecurity/java/:/data/aoxj/bossbi/twsecurity/java/twcmcc.jar:/data/aoxj/jdk15/lib/rt.jar:/data/aoxj/jd<br /><br />k15/lib/tools.jar -Dapplication.home=/data/aoxj/jdk15 -Xms8m<br />23651 Resin -Xss1m -Dresin.home=/data/aoxj/resin -Dserver.root=/data/aoxj/resin -Djava.util.logging.manager=com.caucho.log.LogManagerImpl -<br /><br />Djavax.management.builder.initial=com.caucho.jmx.MBeanServerBuilderImpl<br /><br /><br />详细情况请参考sun官方文档。<br />http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jps.html<br /><br />注：jps命令有个地方很不好，似乎只能显示当前用户的java进程，要显示其他用户的还是只能用unix/linux的ps命令。
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/151897#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 29 Dec 2007 22:34:36 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/151897</link>
        <guid>http://skydream.javaeye.com/blog/151897</guid>
      </item>
      <item>
        <title>resin的session id reuse特性(3)--总结</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/151896" style="color:red;">http://skydream.javaeye.com/blog/151896</a>&nbsp;
          发表时间: 2007年12月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          从对resin源码的追踪到resin配置文件中的设置，可以明确的看到，resin在设计上是提供了session id 的reuse功能，而且resin.conf默认就是打开reuse的。惭愧的是，我一直不知道......<br /><br />    事情要从前段时间的工作谈起，我被要求设计出一套合适的方案来解决目前公司现有的几个前台模块各自为政的问题。其中最核心的两个就是多机负载分担和统一认证功能。目前公司产品中多机负载有两种方式： 1. 纯resin，放弃了对HttpSession和本地资源的使用 2. apache + resin，需要传递所有需要用到的参数，因为麻烦所有干脆只有一个单一入口，因为使用了HttpSession，因此虽然页面跳转进来了，但是由于没有原来的jsessionid无法利用上一次进入该模块时的session，造成要重新创建新的session，非常的吐血。<br /><br />    之后针对apache + resin的多机分布方案进行了调研，随即发现这个方案的核心就在于jsessionid参数的传递。在研究jsessionid传递的时候无意中发现使用cookie传递jsessionid到另外一个webapp，这个webapp新生成的HttpSession的id(就也是 jsessionid)，居然和传递过来的上一个webapp的jsessionid相同！<br /><br />    惊喜万分啊，依照这个特性，完全可以在各个webapp之间只传递jsessionid这一个参数。负责登录的"主webapp"在 HttpSession中保存用户资料，所有其他webapp都可以使用jsessionid作为标志到"主webapp"来获取这些用户资料，只要"主 webapp"提供一个简单的接口即可。随后编码测试了一下，发现这个方案非常好的解决了我目前的问题，简直完美了： apache + resin多机分布，多webapp之间页面任意跳转，简单到只要携带一个jsessionid(这个还可以放cookie)就可以跨webapp四处乱跑。<br /><br />    随即编码测试了一遍，验证这个方法的的确可行。稍后我再将这个方案的详细情况整理出来分享给大家。<br /><br />    这个方案基石，就是jsessionid的传递和jsessionid的重用。在这次方案探索之前，我对jsessionid重用完全没有概念，也根本不知道resin已经有对这个特性的支持。一路摸索过来，几经周折，最后发现原来resin早就准备好了现成的解决方案，为类似我这种多webapp的系统提供session id reuse的支持。<br /><br />    想起了这句词：“众里寻她前百度，蓦然回首，那人却在灯火阑珊处”。呵呵，颇有感觉。<br /><br />    后记： 看来对resin的了解还是不够深入啊，否则如果之前对session id reuse有了解的话，应该可以直接就想到这个方案了。这次能误打误撞的发现，运气着实不错。另外似乎tomcat好象不提供类似的特性支持，稍后再继续研究。
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/151896#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 29 Dec 2007 22:33:22 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/151896</link>
        <guid>http://skydream.javaeye.com/blog/151896</guid>
      </item>
      <item>
        <title>resin的session id reuse特性(2)--分析问题</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/151895" style="color:red;">http://skydream.javaeye.com/blog/151895</a>&nbsp;
          发表时间: 2007年12月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          上文中详细描述了问题的表现情况，由于这个特性严重影响到目前为公司设计的一套前台统一认证方案，因此不得不特别关注。好在resin的源代码是公开的，直接从resin的官网将resin的源代码拿下来，看resin到底是如何处理的。<br /><br />首先找到com.caucho.server.http.HttpRequest，发现是extends AbstractHttpRequest<br /> 在AbstractHttpRequest中找到方法<br />  public HttpSession getSession(boolean create)<br /><br />        发现调用<br />_session = createSession(create, hasOldSession);<br />    这里有一段注释解释了重用jsessonid的行为：<br />        Must accept old ids because different applications in the same server must share the same cookie<br />        But, if the session group doesn't match, then create a new session.<br /><br />  在createSession方法中找到<br />  manager.createSession(id, now, this, _isSessionIdFromCookie);<br /><br /> <br />  打开com.caucho.server.session.SessionManager的createSession()方法，<br />  发现有注释： @param oldId the id passed to the request.  Reuse if possible.<br /> <br />  看代码：<br /><pre name="code" class="java">  String id = oldId;

    if (id == null || id.length() &lt; 4 ||
        ! isInSessionGroup(id) || ! reuseSessionId(fromCookie)) {
      id = createSessionId(request, true);
    }

    SessionImpl session = create(id, now, true);</pre><br /><br /> <br />        我们关注! reuseSessionId(fromCookie)这句，打开看函数<br />   /**<br />   * True if the server should reuse the current session id if the<br />   * session doesn't exist.<br />   */<br />  public boolean reuseSessionId(boolean fromCookie)<br />  {<br />    int reuseSessionId = _reuseSessionId;<br />   <br />    return reuseSessionId == TRUE || fromCookie && reuseSessionId == COOKIE;<br />  }<br /><br />  注：非常不喜欢resin的这种代码风格，一般我宁可写成下面的这种，看代码时容易理解<br />  return (reuseSessionId == TRUE) || (fromCookie && (reuseSessionId == COOKIE));<br />        再看<br /> <pre name="code" class="java">
/**
   * True if the server should reuse the current session id if the
   * session doesn't exist.
   */
  public void setReuseSessionId(String reuse)
    throws ConfigException
  {
    if (reuse == null)
      _reuseSessionId = COOKIE;
    else if (reuse.equalsIgnoreCase("true") ||
         reuse.equalsIgnoreCase("yes") ||
         reuse.equalsIgnoreCase("cookie"))
      _reuseSessionId = COOKIE;
    else if (reuse.equalsIgnoreCase("false") || reuse.equalsIgnoreCase("no"))
      _reuseSessionId = FALSE;
    else if (reuse.equalsIgnoreCase("all"))
      _reuseSessionId = TRUE;
    else
      throw new ConfigException(L.l("'{0}' is an invalid value for reuse-session-id.  'true' or 'false' are the allowed values.",
                    reuse));
  }</pre><br />      并且可以看到默认值为COOKIE<br />private int _reuseSessionId = COOKIE;<br />   <br />    翻一下resin的文档，可以发现在resin.conf的&lt;session-config>有reuse-session-id这个配置项，resin文档的说明如下：<br />   <br />    "reuse-session-id defaults to true so that Resin can share the session id amongst different web-apps."<br />   <br />    默认情况下reuse-session-id设置为true，setReuseSessionId("true")会使得_reuseSessionId=COOKIE，而reuseSessionId()方法中的表达式可以简化:<br />    (reuseSessionId == TRUE) || (fromCookie && (reuseSessionId == COOKIE));<br />    -->  (COOKIE == TRUE) || (fromCookie && (COOKIE == COOKIE))<br />    --> (false) || (fromCookie && true)<br />    --> fromCookie<br />    因此默认情况下jsessionid用cookie传递就可以做到重用,否则就要生成新的jsessionid.<br />   <br />    按照这个思路,只要将reuse-session-id配置项设置为"all",就可以做到即使使用url rewrite也可以重用jsessionid.<br />    ok，问题解决
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/151895#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 29 Dec 2007 22:32:34 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/151895</link>
        <guid>http://skydream.javaeye.com/blog/151895</guid>
      </item>
      <item>
        <title>resin的session id reuse特性(1)--发现问题</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/151894" style="color:red;">http://skydream.javaeye.com/blog/151894</a>&nbsp;
          发表时间: 2007年12月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          近期因工作需求探索apache + resin的多机负载分布和多个webapp统一认证的实现方案, 期间设计多个webapp统一认证的实现方案时, 发现resin下通过cookie来传递jsessionid和通过url重写将jsessionid放url中传递, 会有细微的差异.<br /><br />    注：后来研究发现是resin提供的session id reuse特性，只是此文第一次发布时我还不知道有此特性，惭愧。<br /><br />在servlet规范中，HttpServletSession的获取时通过调用request.getSession(boolean createnew)方法来实现，其实现机制可以简单的理解为: 存在一个大的hashMap结构，key就是jsessionid，而valule是HttpservletSession对象。 request.getSession(boolean createnew)方法通过jsessionid来获取对应的HttpservletSession，如果不存在并且参数createnew= true，则创建一个新的HttpservletSession对象，并设置jsessionid=session.getId() ，保存到hashMap结构中。以后再传递这个jsessionid. （详细的过程比较复杂，各家的实现也不尽相同，但大体的实现原理是如此。）<br /><br />关注以下几点：<br />一). 获取jsessionid<br />    jsessionid的传递可以是以下途径<br />    1. 放在cookie中<br />        Cookie: JSESSIONID=abcrmF3Gx-5Z-hhkgHfzr<br /><br />    2. 以参数形式放在url<br />        http://10.3.2.35:11280/wmail/welcome.action?jsessionid=abcQNqiT4C01rg-necLBr<br /><br />    3. 用form表单传递，通常是用隐藏域<br />        &lt;input type="hidden" name="jsessionid" value="abcQNqiT4C01rg-necLBr"/><br /><br />    4. url重写<br />        http://10.3.2.35:11280/jid=abcQNqiT4C01rg-necLBr/wmail/welcome.action<br />           或者<br />        http://10.3.2.35:11280/wmail/welcome.action;jsessionid=abcQNqiT4C01rg-necLBr<br /><br />    如果当前还没有jsessionid则当然就无法获取，通常用户第一次访问或者登录前就是这种情况.<br />   <br />    可以通过request.getRequestedSessionId() 方法来获取本次http 请求的jsessonid值。<br /><br />二）获取到的HttpServletSession对象<br /><br />    如果HttpServletSession对象是已经存在的，则<br />    1. session.isNew()=false<br />    2. request.getRequestedSessionId() == jsessionid == session.getId()<br /><br />    如果HttpServletSession对象是调用request.getSession(true) (简写的request.getSession()方法等同于request.getSession(true) )时新创建的，则有以下特征：<br />    1. session.isNew()=true<br />    2. 以后传递的jsessionid=session.getId()<br />        注意这里，如果request.getRequestedSessionId() 是空值，情况比较简单，以后传递jsessionid=session.getId()就是了。<br />        但是如果request.getRequestedSessionId() 不是空值，通过这个值没有获取到已经存在的session对象，而是返回了一个新的session对象，这个时候新的session.getId()和原有的request.getRequestedSessionId() 关系如何呢？下面详细阐述这种情况。<br />   <br /><br />三) request.getRequestedSessionId() 不是空值时，新的session.getId() = ?<br /><br /><br />    1). 测试代码如下:<br />    HttpServletRequest request = ServletActionContext.getRequest();<br />    String jid1 = request.getRequestedSessionId();<br />    HttpSession session = request.getSession(true);<br />    String jid2 = request.getRequestedSessionId();<br /><br />    logger.info("get HttpSession , isNew()=" + session.isNew()<br />                    + " getId()=" + session.getId()<br />                    + " and jid1=" + jid1<br />                    + " and jid2=" + jid2);<br /><br />    其中jid1和jid2分别是调用request.getSession(true)方法前后的request.getRequestedSessionId()值。<br /><br />    在resin中运行以上代码，测试request.getRequestedSessionId() 不是空值而对应jsessionid的session不存在的情况。<br />   <br />    2). 通过cookie来传递jsessionid的情况，测试结果如下：<br /><br />        get HttpSession, isNew()=true getId()=abcqIgQroQ2Ov9lGYcYAr and jid1=abcqIgQroQ2Ov9lGYcYAr and jid2=abcqIgQroQ2Ov9lGYcYAr<br /><br />        get HttpSession, isNew()=true getId()=abcPQ3mpxKz8H-4UMdYAr and jid1=abcPQ3mpxKz8H-4UMdYAr and jid2=abcPQ3mpxKz8H-4UMdYAr<br /><br />        get HttpSession, isNew()=true getId()=abcdeE3iDy_bI536tLYAr and jid1=abcdeE3iDy_bI536tLYAr and jid2=abcdeE3iDy_bI536tLYAr<br /><br />        可以发现以下规律：<br />        1.  isNew()=true<br />        2. session.getId() == jid1 == jid2<br />            即新创建的session会使用传递过来的jsessionid值，即使这个jsessionid值根本没有对应的session存在<br /><br />    3)  通过url重写，将jsessionid放url中传递, 测试结果如下：<br /><br />        get HttpSession, isNew()=true getId()=abccw1zEC_RcN43qHMYAr and jid1=abcdUdTfKuLbge8h_LYAr and jid2=abcdUdTfKuLbge8h_LYAr<br />        http://10.3.2.35:11280/jid=abccw1zEC_RcN43qHMYAr/uab/contactList.action<br /><br />        get HttpSession, isNew()=true getId()=abcFK7yOB1irgaYqgNYAr and jid1=abci-HpMPJU3egCB7MYAr and jid2=abci-HpMPJU3egCB7MYAr<br />        http://10.3.2.35:11280/jid=abcFK7yOB1irgaYqgNYAr/uab/contactList.action<br /><br />        (后面的http地址为页面跳转完成后显示在浏览器地址框中的页面url)<br />       <br />        可以发现以下规律：<br />        1. isNew()=true<br />        2. jid1 == jid2<br />            request.getRequestedSessionId()值在request.getSession(true)方法调用前后无变化<br />        3. session.getId()  != jid1<br />            即新创建的session不使用传递过来的jsessionid值,而是采用新值<br />        4. 跳转完成后的http地址中,使用的是session.getId(), 而不是原来通过url重写传递过来的jsessionid<br />            此时新的jsessionid覆盖了旧有的jsessionid.<br /><br />    4) 总结<br />        在resin的实现中, 通过cookie来传递jsessionid的情况和通过url重写将jsessionid放url中传递, 会有细微的差异.<br />        以上测试的resin版本为3.0.26, 稍后有时间考虑测试其他版本和tomcat.<br /><br />   <br />        这个差异直接影响到跨webapp的多个webapp直接相互传递jsessionid的方式, 通过cookie传递jsessionid可以做到多个webapp之间在页面跳转时始终是一个相同的jsessionid,这种各个应用都可以方便的获取到自己的HttpServletSession对象. 但是如果是通过url重写,则破坏了jsessonid的一致性, 逼迫各个webapp之间跳转时必须用其他额外的方法来保证传递给对方的jsessionid的准确性,因为此时每个webapp的jsessionid 都不一样了,必须记住其他每个webapp的jsessionid，造成跨webapp的页面跳转极其复杂，难于接受.
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/151894#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 29 Dec 2007 22:31:04 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/151894</link>
        <guid>http://skydream.javaeye.com/blog/151894</guid>
      </item>
      <item>
        <title>谁在创建session(4)-为什么要关注session的创建</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/151893" style="color:red;">http://skydream.javaeye.com/blog/151893</a>&nbsp;
          发表时间: 2007年12月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          看到有留言，对我如此“执着”的关注session创建很好奇，解释一下吧。<br /><br />    首先是关注性能，前面提到过session的使用是有代价的，需要在保存在服务器端内容中，每次request.getSeesion()方法获取 session时，实际是在服务器段的一个大的hasp结构中以当前的jsessionid为key，获取对应的value HttpSession对象，这个过程是需要消耗cpu的，当然目前hash算法比较好，这里消耗不那么明显。而一般的应用，消耗的cpu远比这个小开销大出2-3个数量级，因此通常情况不敏感。如果这个session是我们需要使用的，那么付出这些内存和cpu的代码是完全值得的。但是，如果产生大量的没有任何用处的"垃圾session"，对大容量，大并发，需要长期稳定运行的系统会带来很无谓的负载。<br /><br />    注意，我们要讨论是"垃圾session"，即是在我们计划外因为某个原因创建，从不使用，完全浪费的session。正常使用的session不在讨论范围内，虽然也有些比较极端的系统号称不使用session来提高服务器性能，有些对性能比较关注的系统/框架则采用其他的方式来避免使用 session，有兴趣的可以google找资料看。<br /><br />    下面我们来进行一个简单的性能测试，模拟一下比较极端的情况，我在linux下启动resin，只跑两个最简单的jsp文件:<br /><br />a.jsp不会自动生成session:<br /><br /><pre name="code" class="java">&lt;%@ page contentType="text/html; charset=UTF-8" %>
&lt;%@ page session="false" %>

&lt;!DOCTYPE html PUBLIC "-//W3C/m/DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
&lt;html xmlns="http://www.w3.org/1999/xhtml">
&lt;head>
&lt;title>&lt;/title>
&lt;/head>
&lt;body>
&lt;%=1%>
&lt;/body>
&lt;/html></pre><br /><br />b.jsp基本相同，但是设置为&lt;%@ page session="true" %>，这个将自动增加<br />HttpSession session = request.getSesson(true)<br />语句，我们进行最恶劣的假设，每次请求都生成新的session，看看会是什么情况：<br /><br />测试工具采用loadrunner，部署在我的笔记本上（dell d620机器，intel 双核 2g内存）。测试比较简单，测试方法和过程忽略（loadrunner的使用也不复杂），只给出结果：<br /><br />测试中两个场景的运行情况相同：100个线程并发，每次只访问一下a.jsp/b.jsp，运行时间2分钟。使用top命令在服务器段看resin的内存消耗和cpu使用情况，这个只能大概估计，不好准确衡量。<br /><br />补充：第一次测试时忘了设置resin的&lt;session-max>参数了，后来写了一个sessionLinstener做计数器， resin的默认没有配置session-max，这个时候resin取的值是默认4096，计数器打印日志发现session总数达到4096后，就会 remove掉已经存在的session以保证不超过4096.下面的测试数据时将session-max设置为4096000后测试的结果<br /><br />测试1：<br />    a.jsp 不生成session<br />    resin内存消耗始终是在175-177之间，基本没有变化。cpu基本稳定在19%-22%。<br />测试2：<br />    b.jsp 每次生成session<br />    resin内存消耗从181m逐步增加，到2分钟测试结束时达到283m，大概增加了102M。cpu大体在25%-30%间。<br /><br />下面可以总结了：<br />1. 垃圾session会占用内存<br />    上面只测试了2分钟，考虑通常session的超时时间会是30分钟，这意味着这些占据的内存至少要到30分钟之后resin才能判定超时从resin的session hash结构出移除，之后再被jvm回收。<br /><br />    创建的session总数为202941，102M内存/202941次请求= 527字节/session，这个数据比较粗糙，但是还是能大体反映问题。一个垃圾session大概要浪费我们0.5k的内存，时间长达30分钟。<br /><br />2. 垃圾session的调用消耗了cpu资源<br />    HttpSession session = request.getSesson(true)，每次都要new出新的HttpSession对象，然后resin还要给这个HttpSession算出一个jsessionid，再将jsessionid/session以key/value保存到hash结构中。以后resin检查session超时的线程每次检查时都要查看每个垃圾session，看是否超时。<br /><br />3. 垃圾session增加正常获取session的开销<br />    前面提到垃圾session也是要保存hash结构中，request.getSesson()每次都要用jsessionid在这个hash结构中取一次数据。当垃圾session大量充斥时，获取当正常有用的session的时间也会增加，具体就要看这个hash结构的算法如何了。<br /><br />    当前上述的测试都是建立在最苛刻最恶劣的情况下，大多数情况我们的系统不会这么糟糕，也不是每个系统都对访问量/性能有高要求。如果觉得可以浪费的起，那浪费好了，只是我这边系统的情况不同，我们的产品对性能很敏感，能省就省点。<br /><br />    除考虑性能外, session的创建在我们新设计的系统中,是必须非常严格控制的.这个和我们目前的系统结构,包括部署/用户身份认证有关.简单的说我们的系统是基于 apache + resin的多机分布, 各个功能模块是作为不同的webapp发布的,session的生成和jsessionid的传递必须可控，因此我必须严格掌控系统中session的生成情况。<br /><br />    另外说一点个人意见，知不知道有这些session自动创建的情况，和决定是否要在自己的代码中严格控制session创建，不是同一个概念。完全可以在了解情况后，根据自己的实际需要和个人习惯去做决定，比如选择无视。但是如果不知道呢？呵呵，很惭愧，在这次探索之前，我对jsp/webwork标签自动生成session是没有概念的，我们的原有系统是不使用HttpSession的（和当时的分布式方案有关），但是现在看来，由于 jsp/webwork标签的存在，其实每次都有session被创建，这些session也就成了我上面说的垃圾session: 在不需要创建时意外创建，从来不使用，除了浪费资源外没有其他存在价值。<br /><br />    整理一下留言中的内容，感谢大家的关注：<br />疑问：<br />    我觉得系统的瓶颈不会出在session生成上 ,花时间在后台缓存和sql优化、架构调整这些事情上来的实惠.<br />回答：<br />     滴水的水龙头要不要拧紧的问题？<br /><br />    没有哪个家庭的财政会因为那个滴水的水龙头造成收支失衡以致破产，那是否就意味着可以无视它的存在，只管专心挣钱，再每个月少吃一次大餐？后台缓存和 sql优化、架构调整，这些当然是更重要的，但不意味着其他东西就可以完全忽略。我无意强调session的这些细节的高度，只是希望告诉大家，在某些角落里，有些我们没有意识到的小水龙头，在一滴一滴的浪费资源。<br /><br />    后台缓存和sql优化、架构调整，到处可以找到资料。可是我google了一圈，上述session的几个问题根本没有成文的全面一点的资料可以参考，我相信jsp世界中肯定还有很多很多类似的小水龙头在悄悄地滴水，而主人们根本没有看见。<br /><br />ps: 这个系列的文章最初发在blogjava，现在复制到javaeye(担心哪天某家blog不在了丢失文章)，感觉这个话题很少看到有人谈起，我斗胆发到论坛上来看看，是否有人和我持有相同的看法。
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/151893#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 29 Dec 2007 22:22:45 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/151893</link>
        <guid>http://skydream.javaeye.com/blog/151893</guid>
      </item>
      <item>
        <title>谁在创建session(3)-凑热闹的webwork标签</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/151892" style="color:red;">http://skydream.javaeye.com/blog/151892</a>&nbsp;
          发表时间: 2007年12月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          按照前面的方法，将jsp页面加入&lt;%@ page session="false"%>之后，发现还是有新的session会意外的蹦出来。仔细检查action没有操作session，那这次是谁干的呢？将目光转回jsp文件，首先来次狠的，将这个jsp文件的内容删空为只有纯html内容。重新运行后发现不会自动生成session，ok，问题在jsp文件里面了。将原来的内容一点点的加回去，反复测试直到session自动创建的问题再次出现, 哈,发现问题出现在webwork的标签上。<br /><br />这次出问题的a.jsp代码内容为：<br /><br />﻿<pre name="code" class="java">&lt;%@ page contentType="text/html; charset=UTF-8" %>
&lt;%@ page session="false" %>
&lt;%@taglib prefix="ww" uri="webwork" %>

&lt;!DOCTYPE html PUBLIC "-//W3C/m/DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
&lt;ww:i18n name="'msg'">
&lt;html xmlns="http://www.w3.org/1999/xhtml">
&lt;head>
&lt;title>&lt;/title>
&lt;/head>
&lt;body>
&lt;%=1%>
&lt;/body>
&lt;/html>
&lt;/ww:i18n></pre><br /><br />部署到webapp的根目录，在浏览器中直接用http://****/a.jsp访问，通过抓包工具发现有<br />Set-Cookie: JSESSIONID=abclEpuvWZhHD_UWW7WBr; path=/<br /><br />将上述文件复制为b.jsp，删除&lt;ww:i18n>标签,代码修改为:<br /><br />﻿&lt;%@ page contentType="text/html; charset=UTF-8" %><br />&lt;%@ page session="false" %><br />&lt;%@taglib prefix="ww" uri="webwork" %><br /><br /><pre name="code" class="java">&lt;!DOCTYPE html PUBLIC "-//W3C/m/DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
&lt;html xmlns="http://www.w3.org/1999/xhtml">
&lt;head>
&lt;title>&lt;/title>
&lt;/head>
&lt;body>
&lt;%=1%>
&lt;/body>
&lt;/html></pre><br />再次测试,发现不再创建session，由此可以确认是webwork的标签所为。<br /><br />继续追踪为什么webwork的标签会如此处理，还是google大法，很快在webwork的2.2文档中发现了这么一段：<br /><br />http://wiki.javascud.org/display/ww2cndoc/WebWork+2.2+Migration+Notes<br /><br />"session map wrapper (在ActionContext里建立的) 已经改变了不在为每个请求创建session. 如果你的应用程序依赖session会被自动创建,WebWork 2.2已经不在那样做了.作为替代,你必须自己创建session或者当把一个数据放到session Map里时session会被创建. "<br /><br />对照了一下我当前测试的版本, webwork2.1.7，看来是这里了。马上上webwork的网站下了最新的2.2.6版本，最快速度搭建了一个测试环境。将刚才的 a.jsp/b.jsp拉过去测试了以下，ok，果然新的2.2版本修改了原来的做法，不再创建session。搞定！
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/151892#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 29 Dec 2007 22:18:43 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/151892</link>
        <guid>http://skydream.javaeye.com/blog/151892</guid>
      </item>
      <item>
        <title>谁在创建session(2)-悄悄干活的jsp</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/151890" style="color:red;">http://skydream.javaeye.com/blog/151890</a>&nbsp;
          发表时间: 2007年12月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          jsp文件是session创建的一个源头，这里指的不是在jsp文件中用代码或者标签来操作session，这些都是在控制中的。容易忽视或者说根本就不会意识到的（比如我，就是写jsp三年后才发现的)是，jsp有自动创建session的机制，在jsp页面中，如果没有明确的给出 &lt;%  @page session="false"%>，jsp页面会非常乖巧（如果刚好需要）或者说是偷偷摸摸（如果不需要）的自动在生成的java文件中增加一句： javax.servlet.http.HttpSession session = request.getSession(true)。<br /><br />为了验证这个说法，我们做以下测试，先来一个最简单的jsp文件，名字也简单a.jsp，放到resin下。a.jsp的内容如下，注意里面有一个&lt;%=1%>，后面会详细解释为什么需要这句话：<br /><br /><pre name="code" class="java">&lt;%@ page session="true"%>
&lt;html>
&lt;head>
&lt;title>test&lt;/title>
&lt;/head>
&lt;body>
&lt;%=1%>
&lt;/body>
&lt;/html></pre><br /><br />用页面访问一下，然后到resin下webapp目录的WEB-INF/work/_jsp目录下找到_a__jsp.java，打开可以看到<br />public class _a__jsp extends com.caucho.jsp.JavaPage<br />有关jsp页面是如何转换为java文件再被编译成class的介绍，请google。看我们关心的public void  _jspService()方法:<br /><br /><pre name="code" class="java">public void  _jspService(javax.servlet.http.HttpServletRequest request,
              javax.servlet.http.HttpServletResponse response)
    throws java.io.IOException, javax.servlet.ServletException
  {
    javax.servlet.http.HttpSession session = request.getSession(true);
    com.caucho.server.webapp.Application _jsp_application = _caucho_getApplication();</pre><br /><br />可以看到第一行，明确的调用了request.getSession(true)，session就是再这里被自动创建的，这里也就是JSP中隐含的session对象的来历。<br />使用抓包软件，可以看到请求这个jsp页面的http response里面有以下内容：<br />Set-Cookie: JSESSIONID=abc0zn72YuHtacvaaORBr; path=/<br />这个是刚才创建的session的jsessionid，被保存到cookie中。<br /><br />然后继续测试，设置为<br />&lt;%@ page session="false"%><br /><br />打开java文件：<br /><pre name="code" class="java">public void _jspService(javax.servlet.http.HttpServletRequest request,
              javax.servlet.http.HttpServletResponse response)
    throws java.io.IOException, javax.servlet.ServletException
  {
    com.caucho.server.webapp.Application _jsp_application = _caucho_getApplication();</pre><br /><br />没有javax.servlet.http.HttpSession session = request.getSession(true);<br />这行代码了，同时http response 中没有Set-Cookie: JSESSIONID=***的语句了。<br />ok，这下清晰了。<br /><br />再来解释一下为什么要在刚才的jsp文件里面增加&lt;%=1%>这行，我们先做测试，将&lt;%=1%>删除，同样测试&lt;%@ page session="true/false"%>两种情况。可以看到<br /><pre name="code" class="java">public void _jspService(javax.servlet.http.HttpServletRequest request,
              javax.servlet.http.HttpServletResponse response)
    throws java.io.IOException, javax.servlet.ServletException
  {
    javax.servlet.http.HttpSession session = request.getSession(true);</pre><br /><br /><br />则不管是否有&lt;%@ page session="true"%>都不自动创建session。考虑删除&lt;%=1%>后的jsp文件内容<br /><br /><pre name="code" class="java">&lt;%@ page session="true"%>
&lt;html>
&lt;head>
&lt;title>test&lt;/title>
&lt;/head>
&lt;body>
&lt;/body>
&lt;/html></pre><br />这个是最简单的纯html页面，估计是resin的实现考虑优化了这点。(resin: 都纯html了，还要session干嘛？)<br /><br />最后再澄清一点，发现网络上很多人持有一个观点： session在第一次访问时创建。这个明显的是被jsp文件自动创建（默认是true哦）session给误导了，其实只有明确的调用 request.getSession()/request.getSession(true)才会生成session。只是大多数人的jsp页面不会明确加入&lt;%@ page session="false"%>，也不了解这个机制,造成了错误的理解。
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/151890#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 29 Dec 2007 22:17:08 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/151890</link>
        <guid>http://skydream.javaeye.com/blog/151890</guid>
      </item>
      <item>
        <title>谁在创建session(1)-不恰当的request.getSession()</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/151889" style="color:red;">http://skydream.javaeye.com/blog/151889</a>&nbsp;
          发表时间: 2007年12月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          在HttpServlet中，HttpSession对象通常在request.getSession(true)方法调用时才创建。 HttpSession的使用是有代价的，需要占用服务器资源，本着能不浪费就不浪费的原则，我希望系统中的session都在掌握之中，在需要创建时由我们的代码明确创建。但是最近在开发中发现，新的session对象经常在意料之外出现，究竟是谁在创建session呢？<br /><br />    最常见的地方是错误的使用request.getSession()函数，通常在action中检查是否有某个变量/标记存放在session中。这个场景中可能出现没有session存在的情况，正常的判断应该是这样：<br /><br /><pre name="code" class="java">private boolean ifFlagExistInSession(HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    if (session != null) {
        if (session.getAttribute("flagName")  != null) {
            return true;
        }
    }
    return false;
}</pre><br /><br />    而下面的写法，则可能会生成一个新的不在我们意图之外的session：<br /><pre name="code" class="java">private boolean ifFlagExistInSession(HttpServletRequest request) {
    HttpSession session = request.getSession();   // a new session created if no session exists
    if (session.getAttribute("flagName")  != null) {
        return true;
    }
    return false;
}</pre><br /><br />    注意request.getSession() 等同于 request.getSession(true)，除非我们确认session一定存在或者sesson不存在时明确有创建session的需要，否则请尽量使用request.getSession(false)。
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/151889#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 29 Dec 2007 22:14:53 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/151889</link>
        <guid>http://skydream.javaeye.com/blog/151889</guid>
      </item>
      <item>
        <title>namespace对axis解析xml请求的影响</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/146239" style="color:red;">http://skydream.javaeye.com/blog/146239</a>&nbsp;
          发表时间: 2007年12月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          发生在我身上的实际故事，最后发现和axis解析xml时的处理机制有关，namespace的有无会影响xml解析的方式，简单的说就是有namespace按照元素名解析，没有namespace则按照index下标的顺序来解析。<br /><br />中间惊险，一一道来，做技术的不容易啊。<br /><br />这个市公司的一个大项目，使用web service，我负责服务器端的开发，其他厂商开发客户端。好说，axis上，几个月下来，设计/开发/测试一路ok，就进移动研究院准备最后的入网测试了。<br />    和我们一起联合测试的cx公司，报告说发现错误，经查找是服务器端解析他们发过来的请求失败，出现异常 java.lang.NumberFormatException。非常郁闷，按说不大可能，代码是从wsdl文件自动生成的，怎么可能出这种低级错误，而且我们自己反复测试都通过。于是想办法抓包，发现他们的报文如下：<br /><br />&lt;env:Envelope xmlns:env='http://schemas.xmlsoap.org/soap/envelope/'><br /> &lt;env:Header/><br /> &lt;env:Body><br />  &lt;ssoRegister><br />   &lt;Version>0100&lt;/Version><br />   &lt;OpCode>D0001&lt;/OpCode><br />   &lt;UID>13689000001&lt;/UID><br />   &lt;UIDType>SYSUSER&lt;/UIDType><br />   &lt;Service>WEBADMIN&lt;/Service><br />   &lt;LocalZone>5&lt;/LocalZone><br />   &lt;Province>0&lt;/Province><br />   &lt;Privilege>SUPER&lt;/Privilege><br />  &lt;/ssoRegister><br /> &lt;/env:Body><br />&lt;/env:Envelope><br /><br />格式怪怪的，对照标准的报文：<br /><br />&lt;?xml version="1.0" encoding="UTF-8"?><br />&lt;soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/en<br />coding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><br />            &lt;soapenv:Body><br />                        &lt;ssoRegister xmlns="http://system.chinamobile.com"><br />                            &lt;OpCode>D0001&lt;/OpCode><br />                            &lt;UID>13660000001&lt;/UID><br />                            &lt;UIDType>MDN&lt;/UIDType><br />                            &lt;Service>WEBMAIL&lt;/Service><br />                            &lt;LocalZone>0&lt;/LocalZone><br />                            &lt;Province>0&lt;/Province><br />                            &lt;Privilege>SUPER&lt;/Privilege><br />                        &lt;/ssoRegister><br />                    &lt;/soapenv:Body><br />    &lt;/soapenv:Envelope><br /><br />发现他们的请求多了一个 &lt;Version>0100&lt;/Version>字段，试着去掉，解析就通过了。于是通知cx公司修改，同时提示他们说他们的格式不够标准，xsd的namespace很多都没有写。当时就奇怪，从现象看似乎axis是按照index/下标顺序来解析xml,因为多了一个 version,因此后面的字段都顺推了一位,造成用"WEBMAIL"的值来作为LocalZone解析，而localzone是int类型，所以解析失败，出现异常：java.lang.NumberFormatException.<br /><br />    有些奇怪axis怎么会这样解析xml，当时忙也无暇细想。继续测试中又发现另外一个接口出现问题，cx公司的soap请求的xml字段顺序和wsdl文件规定的顺序不一致，造成数据解析出来内容错乱。晕倒，细问才知道cx公司不是按照wsdl来自动生成代码，也不依照wsdl文件的格式要求，而是很奇怪的以协议附件中的soap请求示例为基准（很荒谬的事情，这还是电信级别的软件开发方式，想不通）。偏偏协议在一个接口上wsdl和示例的顺序不一致，造成这个问题。<br /><br />之后就是非技术的扯皮了，总之cx公司坚持他们的正确性和合理性，非逼我们公司修改服务器端做法。细的不说了，俺们技术人员不懂也不该去关注，黑暗的内幕。由于那个协议的内容是俺修改的（前人离职了），这个出问题的地方就是我陆续修订的，于是责任就压到我身上了，当时那个郁闷啊。写了封邮件准备给领导，将事情说清楚，认错并承认是俺的责任......惨就一个字。就在邮件发出去之后，突然想到，恩，怎么老是按照index解析呢,axis没有这么笨吧？用测试脚本又测试了一下，没有问题，再看soap 报文，惊奇的发现顺序也是有差异的，但是怎么服务器端就能正确解析呢？<br /><br />灵光一动，想起cx的报文格式来了，他们的格式非常的不规范，简直就粗糙到极点了，当时我们几个研发还说笑，说他们肯定是手工拼凑文本，将 soap/web service退化为http + xml，然后再将xml退化为文本。难道是格式的问题？对照了一下，发现少了&lt;*** xmlns="http://system.chinamobile.com">这里的namespace，试着将cx的报文加上这个 namespace，然后用脚本工具提交测试，服务器端解析ok。<br /><br />这下问题明朗了，可以发现是这样的规律,axis在解析时发现没有namespace，就按照顺序来解析：<br /><br />wsdl标准顺序   实际报文顺序    最终解析出来内容<br />&lt;ServiceCode>   &lt;Alias>&lt;/Alias>                    --〉 serviceCode=<br />&lt;Source>    &lt;Source>WEB&lt;/Source>                   --〉 source=WEB<br />&lt;Alias>    &lt;ServiceCode>YXZZY&lt;/ServiceCode>         --〉 alias=YXZZY<br /><br />于是长出一口气，又赶紧写了封新邮件，解释清楚终于将责任踢给cx了．真是惊险。平时哪里会遇到这样格式不规范的报文，这次长见识了．cx终于不再坚持了，增加了namespace后测试通过，最后赶在移动的时间期限前完成了测试，大家不用互相推责任了。
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/146239#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 05 Dec 2007 17:00:12 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/146239</link>
        <guid>http://skydream.javaeye.com/blog/146239</guid>
      </item>
      <item>
        <title>apache下安装mod_rewrite模块</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/146197" style="color:red;">http://skydream.javaeye.com/blog/146197</a>&nbsp;
          发表时间: 2007年12月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          apache下安装mod_rewrite模块<br /><br />在网上找了一些apache下安装mod_rewrite模块的文章，说得都很轻巧，但是俺一路碰壁，怎么都出不来。整理了一下，以后再来研究。<br /><br />一. 编译rewrite模块<br /><br />在apache 2.0.61下按照rewrite模块失败，目前还没有找到原因。<br />使用apache 2.2.6按照rewrite模块，采用的方式是在编译apache前，configure增加参数<br /><br />./configure --prefix=/data/aoxj/artest/apache --enable-so  --enable-rewrite=shared<br />然后再执行make;make install可以成功的编译出mod_rewrite.so<br /><br />(在2.0.61下失败，只编译出mod_rewrite.a文件，原因不明)<br /><br /><br />尝试在2.0.61下用以下设置编译apache都没有成功。<br /><br />./configure --prefix=/data/aoxj/artest/apache --enable-so --enable-rewrite --enable-shared=rewrite<br /><br />./configure --prefix=/data/aoxj/artest/apache --enable-module=so --enable-module=rewrite --enable-shared=rewrite<br />这句是网络硬盘系列编译apache时使用的，在apache1.*上可以打包出mod_rewrite.so。<br />但是在apache2.0.61上失败。<br /><br />（估计是参数写法各个版本不同，以后有时间再来研究）。<br /><br /><br />二. 配置apache<br /><br />在apache的配置文件apache/conf/httpd.conf中增加以下内容：<br /><br />LoadModule rewrite_module modules/mod_rewrite.so<br />RewriteEngine On<br />RewriteLog  logs/rewrite.log<br />RewriteLogLevel 3<br /><br />#RewriteRule ^/~jid=[^/]*/(.*) /$1 [PT,L]<br /><br />RewriteRule中测试过上面的^/~jid=[^/]*/(.*) /$1 [PT,L]，apache会将<br />http://10.3.2.35:11280/~jid=abcMLHHOULJHLKJ/wmail/welcome.action<br />这样的请求，改写为<br />http://10.3.2.35:11280/wmail/welcome.action
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/146197#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 05 Dec 2007 15:21:08 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/146197</link>
        <guid>http://skydream.javaeye.com/blog/146197</guid>
      </item>
      <item>
        <title>resin采用url rewrite来传递jsessionid</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/146192" style="color:red;">http://skydream.javaeye.com/blog/146192</a>&nbsp;
          发表时间: 2007年12月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          前段时间做公司前台方案的技术探索时，涉及到jsseionid的传递，期间尝试使用resin自己的url rewrite功能来传递jsessionid参数，总结如下：<br /><br />一) 默认的jsessionid<br /><br />	默认配置中，jsessionid以cookie的方式在页面传递，即在http请求的header中有以下内容：<br /><br />		Cookie: JSESSIONID=abcrmF3Gx-5Z-hhkgHfzr<br /><br />	此时resin/conf/resin.conf中的配置为默认的：<br />		&lt;session-config><br />			&lt;enable-url-rewriting>false&lt;/enable-url-rewriting><br />		&lt;/session-config><br /><br />二）使用url rewrite传递jsessionid<br /><br />	将jsessionid放到url中，这样可以不使用cookie。<br />	不使用cookie，可以避免遭遇用户禁用cookie的情况。另外，有安全性方面的考虑。<br /><br />	使用url rewrite有两种方式: <br /><br />	1. http://***/wmail/welcome.action;jsessionid=abcLFJLwoeurlsjdlf?...<br />		这种是将jsessionid放在action后面<br /><br />		需要修改resin/conf/resin.conf中的配置为：<br />		&lt;session-config><br />			&lt;enable-cookies>false&lt;/enable-cookies><br />			&lt;enable-url-rewriting>true&lt;/enable-url-rewriting><br />		&lt;/session-config><br /><br />	2. http://***/~jid=abcLFJLwoeurlsjdlf/wmail/welcome.action?...<br /><br />		除了要按照前面的同样修改resin/conf/resin.conf中的配置为：<br />		&lt;session-config><br />			&lt;enable-cookies>false&lt;/enable-cookies><br />			&lt;enable-url-rewriting>true&lt;/enable-url-rewriting><br />		&lt;/session-config><br /><br />		还要在resin/conf/resin.conf中增加alternate-session-url-prefix的设置：<br />		&lt;server>   <br />		&lt;class-loader>...&lt;/class-loader><br />		&lt;alternate-session-url-prefix>/~jid=&lt;/alternate-session-url-prefix><br />		......<br />		<br />		注意alternate-session-url-prefix的位置，不是在&lt;session-config>里面<br /><br />		测试中发现，按照这种方式配置后，url被重写为<br />		http://***/~jid=abcLFJLwoeurlsjdlf/wmail/welcome.action?...<br /><br />		在浏览器中访问会产生http 404 file not found 错误，经反复检查（吐血的经历），发现需要修改resin/conf/resin.conf中的配置：<br />		<br />		&lt;server><br />			......<br />			&lt;host id="" root-directory="."><br />			&lt;!--<br />			&lt;web-app id="/" document-directory="webapps/ROOT"/><br />			 --><br />			&lt;/host><br />		&lt;/server><br />		将&lt;web-app id="/" document-directory="webapps/ROOT"/>的设置屏蔽后才能正常访问<br />		http://***/~jid=abcLFJLwoeurlsjdlf/wmail/welcome.action?...<br />		这样的地址。
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/146192#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 05 Dec 2007 15:11:45 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/146192</link>
        <guid>http://skydream.javaeye.com/blog/146192</guid>
      </item>
      <item>
        <title>apache + resin的多机部署方案实现方法</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/146187" style="color:red;">http://skydream.javaeye.com/blog/146187</a>&nbsp;
          发表时间: 2007年12月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          前言：前段时间，对公司前台多机分布方案的做了一些探索，总结了一些东西，贴出来分享。类似内容网上比较多，但是都不够详细，我第一次弄时费力不少，希望后来人可以更方便一些。<br /><br />一. 编译安装apache<br />1) 从apache官网上下载apache最新的 release版本2.2.6<br />	unix版本取httpd-2.2.6.tar.gz<br />2) 解开包<br />	gunzip httpd-2.2.6.tar.gz<br />	tar xvf httpd-2.2.6.tar<br />3) 编译安装<br />	进入解压后的目录httpd-2.2.6，依次执行<br />./configure --prefix=/*要安装apache的目录*/ --enable-so<br />make<br />make install <br />	成功后apache就安装到前面指定的目录了<br /><br />二)  启动apache<br />	进入apache安装目录<br />1) 修改apache/conf/httpd.conf<br />	Listen 80 修改80为需要的端口如 11280<br />2) 启动<br />	进入apache/bin/执行： ./apachectl start<br />3) 打开浏览起访问<br />	http://ip: 11280<br /><br />三) 编译安装resin<br /><br />./configure --prefix=/data/aoxj/artest/resin --with-apxs=/data/aoxj/artest/apache/bin/apxs  --with-apache=/data/aoxj/artest/apache<br />make<br />make install <br />上述操作除了编译安装resin外（其实不做这些操作，resin本身也是可以跑起来的），还会修改apache，包括：<br />1. copy mod_caucho.so到apache目录（就是前面指定的--with-apache=/data/aoxj/artest/apache）的modules<br />2. 修改apache的配置文件conf/httpd.conf，自动增加以下内容<br />LoadModule caucho_module ***/modules/mod_caucho.so<br />ResinConfigServer localhost 6802<br />CauchoConfigCacheDirectory /tmp<br />CauchoStatus yes <br /><br />四）配置resin<br />需要修改resin.conf文件<br /><br />&lt;cluster><br />	&lt;srun server-id="a" host="192.168.0.1" port="6802"/><br />	&lt;srun server-id="a" host="192.168.0.1" port="6802"/><br />&lt;/cluster><br /><br />五）启动resin<br />	运行resin/bin/httpd.sh<br />	注意一定要加-server，否则resin启动后是监听80/8080这样的端口，而不是上面cluster设置里面的6802<br />	./httpd.sh -server a start<br />	这样resin才会监听6802，建议手工telnet确认一下。如果resin启动不正确，后面apache启动后访问resin就会失败，然后在页面报503错误。<br />	以后stop/restart 时也需要加-server<br /><br />六) 配置apache<br />	确认conf/httpd.conf文件中的以下内容<br />	1) LoadModule caucho_module ***/modules/mod_caucho.so<br />		检查mod_caucho.so是否存在<br />	2) ResinConfigServer localhost 6802<br />		这个ResinConfigServer只能出现一行,如果resin有多台,请在这里指定的那台resin配置文件中的&lt;cluster>中配置其他机器的ip/port<br />	3) CauchoConfigCacheDirectory /tmp<br />	4)CauchoStatus yes <br /><br />最后修改的配置为: <br /><br />	LoadModule caucho_module "/data/aoxj/artest/apache/modules/mod_caucho.so"<br />	ResinConfigServer 192.168.0.1 6802<br />	AddHandler caucho-request .action<br />	CauchoConfigCacheDirectory /tmp<br />	CauchoStatus yes<br /><br />八）web访问<br />	启动apache<br />	用浏览器访问apache的端口，注意不是访问resin的端口
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/146187#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 05 Dec 2007 15:00:27 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/146187</link>
        <guid>http://skydream.javaeye.com/blog/146187</guid>
      </item>
      <item>
        <title>linux/unix + RESIN 验证码无法显示的问题</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/133359" style="color:red;">http://skydream.javaeye.com/blog/133359</a>&nbsp;
          发表时间: 2007年10月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          最近公司项目开发中遇到的一个问题，整理一下，和大家分享。<br /><br />    验证码无法显示的问题，验证码的代码就是google上查找到的最常见的代码，服务器采用resin部署于linux或unix。不是常见的out.clear()问题，这次的问题发现在一个我压根就没有想到的地方，<span style="color: red">profile DISPLAY</span> 环境变量。<br /><br />    1) 问题描述：<br />    登录页面等有验证玛显示的页面,通常可以正确显示验证码图片,但是在某些情况下发现验证码图片无法显示,并且目前只发生在linux/unix平台,windows下正常.而且和resin/jdk版本无关.<br /><br />    bug的直接表现是表现为ie下是红叉,firefox下无实现.将验证码图片的地址在ie输入框中输入,则页面报错:<br /><pre name="code" class="java">
500 Servlet Exception
java.lang.NoClassDefFoundError
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:164)
	at java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:68)
	at java.awt.image.BufferedImage.createGraphics(BufferedImage.java:1141)
        at com.asiainfo.aimc.wmail.action.CreateImageServlet.doGet(CreateImageServlet.java:104)</pre><br /><br />    这里的java.lang.NoClassDefFoundError 极其误导人，一直以为是CLASSPATH或者jar包的问题，所以反复检查resin和jdk版本。<br />   始终无法找到问题，只好尝试追查jdk源码，看到底发生了什么。<br /><br />2) jdk源码追查<br /><br />   调用的servlet：<br />	BufferedImage bi = new BufferedImage(...)<br />	Graphics2D g = bi.createGraphics();<br /><br />   查jdk: BufferedImage.createGraphics():<br />	GraphicsEnvironment env =<br />	    GraphicsEnvironment.getLocalGraphicsEnvironment();<br /><br />   再查GraphicsEnvironment.getLocalGraphicsEnvironment：<br />	String nm = (String) java.security.AccessController.doPrivileged<br />		(new sun.security.action.GetPropertyAction<br />		 ("java.awt.graphicsenv", null));<br />	......<br />	localEnv = GraphicsEnvironment) Class.forName(nm).newInstance();<br />	......<br />    问题应该和nm有关，这里明显是一个类似工厂模式的设计，"java.awt.graphicsenv"到nm 然后Class.forName() 生成GraphicsEnvironment对象。<br />    由于代码在jdk中，不方便修改，因此单独将这些代码提出来到简单的测试类 Test.java:<br /><br />3) 测试代码分析<br /><pre name="code" class="java">public class Test {
	public static void main(String[] args) {
		String nm = (String) java.security.AccessController.doPrivileged
		(new sun.security.action.GetPropertyAction
		 ("java.awt.graphicsenv", null));
		
		System.out.println(nm);
		
		try {
			Class.forName(nm).newInstance();
		} catch (Throwable e) {
			System.out.println("error=" + e.getClass().getName());
			
			e.printStackTrace();
		} 
	}
}</pre><br />     在windows平台下运行，结果正常，打印：<br />	sun.awt.Win32GraphicsEnvironment<br /><br />     将代码放到出问题的resin安装所在的linux平台，手工编译运行：<br />javac Test.java<br />java -cp . Test<br /><br />    报错，打印为：<br /><br /><pre name="code" class="java">sun.awt.X11GraphicsEnvironment
Throwable=java.lang.InternalError
java.lang.InternalError: Can't connect to X11 window server using '10.3.18.16' as the value of the DISPLAY variable.
        at sun.awt.X11GraphicsEnvironment.initDisplay(Native Method)
        at sun.awt.X11GraphicsEnvironment.access$000(X11GraphicsEnvironment.java:53)
        at sun.awt.X11GraphicsEnvironment$1.run(X11GraphicsEnvironment.java:142)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.awt.X11GraphicsEnvironment.&lt;clinit>(X11GraphicsEnvironment.java:131)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:164)
        at Test.main(Test.java:13)</pre><br /><br />从错误信息" Can't connect to X11 window server using '10.3.18.16' as the value of the DISPLAY variable."来看，和DISPLAY环境变量有关<br />执行unset再运行可以发现问题消失：<br />$> unset DISPLAY<br />$> java -cp . Test<br />sun.awt.X11GraphicsEnvironment<br />$><br /><br />   在此情况下（unset  DISPLAY )下重新启动resin，发现验证码可以正常显示。<br /><br />4) 解决的方法:<br />        必须保证resin运行时DISPLAY 环境变量没有设置,如果resin运行的环境有其他要求必须使用DISPLAY,则可以在运行resin前使用unset清除. 建议的简单而有效的方法是直接修改resin/bin/httpd.sh文件,在第二行(具体行数无所谓,但必须在最后一行前)插入:<br /> #! /bin/sh<br /> unset DISPLAY<br /> #....<br /><br />5）疑惑<br />	1. Can't connect to X11 window server using '10.3.18.16' as the value of the DISPLAY variable<br />	为什么要去连X11 window server ？不懂<br /><br />	2. 从Test.java运行看抛出的是Error : java.lang.InternalError,但是页面上显示的是java.lang.NoClassDefFoundError，看了看源代码也没有发现先catch 后throws的错误处理，不清楚这里的具体处理，不方便继续追查，作罢。
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/133359#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 19 Oct 2007 11:34:02 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/133359</link>
        <guid>http://skydream.javaeye.com/blog/133359</guid>
      </item>
      <item>
        <title>java编译器对string常量表达式的处理和优化</title>
        <author>skydream</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://skydream.javaeye.com">skydream</a>&nbsp;
          链接：<a href="http://skydream.javaeye.com/blog/48351" style="color:red;">http://skydream.javaeye.com/blog/48351</a>&nbsp;
          发表时间: 2007年01月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          首先把问题摆出来，先看这个代码 <br /><br />String a = "ab";<br />String b = "a" + "b";<br />System.out.println((a == b));<br /><br />打印结果会是什么？类似这样的问题，有人考过我，我也拿来考过别人(蛮好玩的，大家也可以拿来问人玩)，一般答案会是以下几种：<br /><br /> 1.true<br />    "a" + "b" 的结果就是"ab"，这样a,b都是"ab"了，内容一样所以"相等"，结果true<br />    一般java新人如是答。<br />2.false<br />    "a" + "a"会生成新的对象"aa"，但是这个对象和String a = "ab";不同，(a == b)是比较对象引用，因此不相等，结果false<br />    对java的String有一定了解的通常这样回答。<br />3.true<br />    String a = "ab";创建了新的对象"ab"； 再执行String b = "a" + "b";结果b="ab",这里没有创建新的对象，而是从JVM字符串常量池中获取之前已经存在的"ab"对象。因此a,b具有对同一个string对象的引用，两个引用相等，结果true.<br />    能回答出这个答案的，基本已经是高手了，对java中的string机制比较了解。<br />    很遗憾,这个答案,是不够准确的。或者说，根本没有运行时计算b = "a" + "b";这个操作.实际上运行时只有String b = "ab";<br />    3的观点适合解释以下情况：<br />    String a = "ab";<br />    String b = "ab";<br />    System.out.println((a == b));<br />    如果String b = "a" + "b";是在运行期执行，则3的观点是无法解释的。运行期的两个string相加，会产生新的对象的。(本文后面对此有解释)<br /><br />4.true<br />    下面是我的回答：编译优化+ 3的处理方式 = 最后的true<br />    String b = "a" + "b";编译器将这个"a" + "b"作为常量表达式，在编译时进行优化，直接取结果"ab"，这样这个问题退化<br />    String a = "ab";<br />    String b = "ab";<br />    System.out.println((a == b));<br />    然后根据3的解释，得到结果true<br /><br />    这里有一个疑问就是String不是基本类型，像<br />int secondsOfDay = 24 * 60 * 60;<br />    这样的表达式是常量表达式,编译器在编译时直接计算容易理解，而"a" + "b" 这样的表达式,string是对象不是基本类型,编译器会把它当成常量表达式来优化吗?<br />    下面简单证明我的推断，首先编译这个类:<br />public class Test {<br />    private String a = "aa";<br />}<br />       复制class文件备用,然后修改为<br />public class Test {<br />    private String a = "a" + "a";<br />}<br />    再次编译,用ue之类的文本编辑器打开,察看二进制内容,可以发现,两个class文件完全一致,连一个字节都不差.<br />    ok,真相大白了.根本不存在运行期的处理String b = "a" + "b";这样的代码的问题,编译时就直接优化掉了。<br /><br /><br />下面进一步探讨，什么样的string + 表达式会被编译器当成常量表达式？<br />String b = "a" + "b";<br />这个String + String被正式是ok的,那么string + 基本类型呢？ <br /><br />String a = "a1";<br />String b = "a" + 1;	<br />System.out.println((a == b));  //result = true<br /><br />String a = "atrue";<br />String b = "a" + true;<br />System.out.println((a == b));  //result = true<br /><br />String a = "a3.4";<br />String b = "a" + 3.4;<br />System.out.println((a == b));  //result = true<br />   <br />可见编译器对string + 基本类型是当成常量表达式直接求值来优化的。<br /><br />再注意看这里的string都是"**"这样的，我们换成变量来试试：<br />String a = "ab";<br />String bb = "b";<br />String b = "a" + bb; <br />System.out.println((a == b));   //result = false<br />这个好理解，"a" + bb中的bb是变量，不能进行优化。这里很很好的解释了为什么3的观点不正确，如果String+String的操作是在运行时进行的，则会产生新的对象，而不是直接从jvm的string池中获取。<br /><br />再修改一下,把bb作为常量变量：<br />String a = "ab";<br />final String bb = "b";<br />String b = "a" + bb; <br />System.out.println((a == b));   //result = true<br />竟然又是true，编译器的优化好厉害啊，呵呵，考虑下面这种情况：<br />String a = "ab";<br />final String bb = getBB();<br />String b = "a" + bb; <br />System.out.println((a == b));    //result = false<br />private static String getBB() {<br />return "b";<br />}<br />看来java(包括编译器和jvm)对string的优化，真的是到了极点了，string这个所谓的"对象"，完全不可以看成一般的对象，java对string的处理近乎于基本类型，最大限度的优化了几乎能优化的地方。<br /><br />另外感叹一下,string的+号处理，算是java语言里面唯一的一个"运算符重载"(接触过c++的人对这个不会陌生)吧？
          <br/>
          <span style="color:red;">
            <a href="http://skydream.javaeye.com/blog/48351#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 18 Jan 2007 14:58:30 +0800</pubDate>
        <link>http://skydream.javaeye.com/blog/48351</link>
        <guid>http://skydream.javaeye.com/blog/48351</guid>
      </item>
  </channel>
</rss>