随笔-57  评论-202  文章-17  trackbacks-0
 
      在JDK1.3版本中引入了Dynamic Proxy的代理机制,通过实现java.lang.reflect.InvocationHandler接口,可以实现拦截需要改写的方法。下面是一个简单范例。
      有下面一个接口TestInterface和它的一个实现TestImpl:

package sample.proxy;

/**
 * <p>Title: </p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2005</p>
 *
 * <p>Company: </p>
 *
 * @author George Hill
 * @version 1.0
 
*/


public interface TestInterface {

  
public String print();

}


package sample.proxy;

/**
 * <p>Title: </p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2005</p>
 *
 * <p>Company: </p>
 *
 * @author George Hill
 * @version 1.0
 
*/


public class TestImpl implements TestInterface {
  
  
public String print() {
    
return "Hello, it's from TestImpl class";
  }

  
}


      下面拦截print方法,调用自己的实现,这需要实现java.lang.reflect.InvocationHandler接口。

package sample.proxy;

import java.lang.reflect.
*;

/**
 * <p>Title: </p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2005</p>
 *
 * <p>Company: </p>
 *
 * @author George Hill
 * @version 1.0
 
*/


public class TestHandler implements InvocationHandler {
  
  TestInterface test;
  
  
/**
   * 将动态代理绑定到指定的TestInterface
   * @param test TestInterface
   * @return TestInterface 绑定代理后的TestInterface
   
*/

  
public TestInterface bind(TestInterface test) {
    
this.test = test;
    
    TestInterface proxyTest 
= (TestInterface) Proxy.newProxyInstance(
      test.getClass().getClassLoader(), test.getClass().getInterfaces(), 
this);
    
    
return proxyTest;
  }

  
  
/**
   * 方法调用拦截器,拦截print方法
   * @param proxy Object
   * @param method Method
   * @param args Object[]
   * @return Object
   * @throws Throwable
   
*/

  
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
// 如果调用的是print方法,则替换掉
    if ("print".equals(method.getName())) {
      
return "HaHa, It's come from TestHandler";
    }
 else {
      
return method.invoke(this.test, args);
    }

  }

  
}


      下面是测试用例:

package sample.test;

import junit.framework.
*;

import sample.proxy.
*;

/**
 * <p>Title: </p> 
 * 
 * <p>Description: </p> 
 * 
 * <p>Copyright: Copyright (c) 2005</p> 
 * 
 * <p>Company: </p>
 * 
 * @author George Hill
 * @version 1.0
 
*/


public class TestDynamicProxy extends TestCase {
  
  
private TestInterface test = null;

  
protected void setUp() throws Exception {
    super.setUp();
    TestHandler handler 
= new TestHandler();
    
// 用handler去生成实例
    test = handler.bind(new TestImpl());
  }


  
protected void tearDown() throws Exception {
    test 
= null;
    super.tearDown();
  }


  
public void testPrint() {
    System.
out.println(test.print());
  }


}


      运行测试用例,可以看到输出的是“HaHa, It's come from TestHandler”。
posted @ 2005-05-24 17:47 小米 阅读(3493) | 评论 (3)编辑 收藏
      昨天,终于收到了等待已久的《深入浅出Hibernate》一书,到现在已经看了40多页,感觉很不错,以前的一些问题在看了作者的剖析后,豁然开朗! 作者看来下了很大功夫去写这本书,真正做到了深入浅出。我评价此书为,向大家推荐这本书。
      另外还买了一本《精通Spring》和《Java线程编程》,呵呵,看来有一段时间忙看书了。
posted @ 2005-05-24 17:30 小米 阅读(581) | 评论 (1)编辑 收藏
      在工作中,经常需要写充值这样的功能,这个功能无非是要做下面四件事:
      1. 判断是否可以充值;
      2. 将充值卡的余额减去充值金额;
      3. 将用户的余额加上充值金额;
      4. 写日志。
      在实际的应用中,还是有不少地方需要考虑的,主要有以下几个方面:
      1. 这四步中,后面三步都涉及到数据库的操作,所以必须在一个事务中完成;
      2. 后面三步的执行顺序是需要考虑的,我觉得比较好的顺序是这样的:
         a. 写日志;
         b. 将充值卡的余额减去充值金额;
         c. 将用户的余额加上充值金额。
      由于写日志操作是不涉及到金额的,即使失败了,对系统也没有什么很大的影响,所以我放在第一步执行。至于是先给用户增加金额还是先减去充值卡的金额,似乎谁先谁后都没有什么关系。不过,其实有一个微妙的地方,如果先给用户加上余额而不幸在减去充值卡的余额时出错,甚至很不幸数据库也出了问题,没有回滚事务。那么就等于用户平白无故的多了钱,用户当然很高兴,也许会投诉,也许不会投诉(是我肯定不投诉,高兴都来不及呢)。如果是相反的情况,那么用户必然投诉,也就可以发现系统的问题。所以我觉得应该先减去充值卡的余额。
      3. 在执行更改充值卡的余额时,必须要在数据库中进行加减操作,而且要在数据库中再检查一遍充值卡的余额是否足够。执行的SQL语句类似下面:
      UPDATE card SET card_balance=card_balance-? WHERE card_number=? AND card_balance>=?
      由于现在大部分的系统都是多进程或者多线程的,有可能在你提交之前,有其它的进程或者线程更新了数据库中的记录,所以如果不在数据库中进行加减操作,不再检查一遍余额是否充足,会给系统带来隐患。
posted @ 2005-05-20 18:19 小米 阅读(491) | 评论 (0)编辑 收藏
     摘要:       使用Jakarta Commons Pool可以根据需要快速的实现自己的对象池,只需要实现PoolableObjectFactory或者KeyedPoolableObjectFactory接口。KeyedPoolableObjectFactory和PoolableObjectFactory的不同之处在于KeyedPoolabl...  阅读全文
posted @ 2005-05-20 14:08 小米 阅读(4087) | 评论 (0)编辑 收藏
      续上一篇随笔,struts1.2的源代码和struts1.1的源代码有些不同,struts1.1的eoncode方法是用的RequestUtils.encodeURL(String s)处理的,指定用UTF-8进行encoding。所以我的修改有些不同。
      从struts的网站上下载1.1的源代码:http://apache.justdn.org/jakarta/struts/source/jakarta-struts-1.1-src.zip,解压到本地目录。打开文件jakarta-struts-1.1-src\src\share\org\apache\struts\taglib\bean\WriteTag.java,新增一个布尔属性encode,表示是否需要用UTF-8编码输出字符串。代码片断如下:

    /**
     * The encode flag for the value.
     * Added by George Hill, 05/19/2005
     
*/

    
protected boolean encode = false;
    
    
public boolean isEncode() {
        
return (this.encode);
    }

    
    
public void setEncode(boolean encode) {
        
this.encode = encode;
    }

      修改方法formatValue,把这段:


        
// Return String object as is.
        if ( value instanceof java.lang.String ) {
                
return (String)value;
        }
 else {

        }


      替换成:


        
// Return String object as is.
        if ( value instanceof java.lang.String ) {
            
if (encode)
                
return RequestUtils.encodeURL((String)value);
            
else
                
return (String)value;
        }
 else {

        }


      保存修改后的文件。然后需要修改文件jakarta-struts-1.1-src\doc\userGuide\struts-bean.xml。这个文件在ant编译时会变成strtus-bean.tld文件。在tag write后面增加一个attribute,如下所示:


    
<attribute>
      
<name>encode</name>
      
<required>false</required>
      
<rtexprvalue>true</rtexprvalue>
      
<info>
      
<p>Added by George Hill, specifies the value need UTF-8 encode or not.</p>
      
</info>
      
<default>false</default>
    
</attribute>

      然后修改一下ant的编译配置文件build.xml,这一部分和上一篇的随笔类似,请参考上篇随笔:<<用URLEncoder输出<bean:write />的值>>
      运行ant,编译后的jar文件保存在jakarta-struts-1.1-src\target\library目录中,把原来的struts.jar和struts-bean.tld文件替换掉,就可以用自定义的<bean:write /> tag了。
posted @ 2005-05-20 11:52 小米 阅读(1033) | 评论 (0)编辑 收藏
      我在JSP页面中,当处理<a href></a>时,经常不用<html:link/>的方式处理,而是用下面这样的方式处理:
      <a href="foo.do?param1=<bean:write name="n1" property="p1"/>&param2=<bean:write name="n2" property="p2"/>">Test</a>
      这样在处理多个参数时,就不需要先放到一个Collection中。按照<html:link/>的多个参数的处理方法去做,有时确实是很繁琐。不过这样带来一个新的问题,在处理中文参数值时,这样就行不通了。用request.getParameter("param1")获取的中文,在中文的个数为奇数个时,就会显示不正确。例如“三个字”在getParameter中获取的值是“三个?”。
      这个问题是由于<bean:write />没有用URLEncoder的encode方法处理值,因为<bean:write />主要是用来在页面上显示bean的信息,并不是用在链接中当作参数的值。这个问题可以通过给<bean:write />增加新的属性来解决。
      从Struts的网站上下载Struts 1.2.4的源代码:http://apache.freelamp.com/struts/source/jakarta-struts-1.2.4-src.zip
      解压到本地目录,然后修改文件jakarta-struts-1.2.4-src\src\share\org\apache\struts\taglib\bean\WriteTag.java。新增一个属性charset,表示需要用什么编码进行编码。代码片断如下:

    /**
     * Added by George Hill, the string value charset to encoding.
     * 05/19/2005
     
*/

    
protected String charset = null;

    
public String getCharset() {
        
return (this.charset);
    }


    
public void setCharset(String charset) {
        
this.charset = charset;
    }

      修改方法formatValue,把这段


        
if (value instanceof java.lang.String) {
                
return (String) value;
        }
 else {

        }


      替换为:


        
if (value instanceof java.lang.String) {
            
if (charset != null && charset.length() != 0)
                
return TagUtils.getInstance().encodeURL((String) value, charset);
            
else
                
return (String) value;
        }
 else {

        }


      保存修改后的文件。然后需要修改文件jakarta-struts-1.2.4-src\doc\userGuide\struts-bean.xml。这个文件在ant编译时会变成strtus-bean.tld文件。在tag write后面增加一个attribute,如下所示:


  
<tag>
    
<name>write</name>

    
<attribute>
      
<name>charset</name>
      
<required>false</required>
      
<rtexprvalue>true</rtexprvalue>
      
<info>
      
<p>Added by George Hill, use this charset to encoding the value.</p>
      
</info>
    
</attribute>

  
</tag>

      然后修改一下ant的编译配置文件build.xml,有几个部分需要修改:
      1.属性catalina.home需要修改成你安装的tomcat的目录;
      2.compile.classpath需要修改,把jar文件的路径指向正确;
      3.prepare.library taget部分的copy,把相关的jar文件的路径指向正确。
      运行ant,编译后的jar文件保存在jakarta-struts-1.2.4-src\target\library目录中,把原来的struts.jar和struts-bean.tld文件替换掉,就可以用自定义的<bean:write /> tag了。如果不写charset属性,那么和原来的<bean:write />处理是一样的。
      这样,类似于下面的链接地址:
      <a href="foo.do?param1=<bean:write name="n1" property="p1"/>&param2=<bean:write name="n2" property="p2"/>">Test</a>
      就可以修改成:
      <a href="foo.do?param1=<bean:write name="n1" property="p1" charset="UTF-8"/>&param2=<bean:write name="n2" property="p2" charset="UTF-8"/>">Test</a>
      在request.getParameter("param1")中将会获得正确的中文值。
      对于struts 1.1,程序又稍微有些不同。我将在下一篇随笔中介绍。
posted @ 2005-05-20 00:50 小米 阅读(2073) | 评论 (5)编辑 收藏
      等了好多天,总于等到了。在China-pub和第二书店都有售。地址:
      http://www.china-pub.com/computers/common/info.asp?id=24500
      http://www.dearbook.com.cn/book/viewbook.aspx?pno=TS0028982
      期待快递公司快点送过来!
posted @ 2005-05-18 18:21 小米 阅读(388) | 评论 (0)编辑 收藏
      下面是我的一个简单的网络服务器端的程序,程序的流程是监听ACCEPT事件,然后往客户端输出一串字符串。是不是很简单。

package sample.nio;

import java.io.
*;
import java.net.
*;
import java.nio.channels.
*;
import java.util.
*;

/**
 * <p>Title: </p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2005</p>
 *
 * <p>Company: </p>
 *
 * @author George Hill
 * @version 1.0
 
*/


public class Server {

  
private int port;

  
public Server(int port) {
    
this.port = port;
  }


  
public void startServer() throws IOException {
    
// 创建ServerSocketChannel并且绑定到指定的端口
    ServerSocketChannel ssc = ServerSocketChannel.open();
    InetSocketAddress address 
= new InetSocketAddress(InetAddress.getLocalHost(), port);
    ssc.socket().bind(address);
    ssc.configureBlocking(
false);

    
// 创建Selector,并且注册ACCEPT事件
    Selector selector = Selector.open();
    SelectionKey skey 
= ssc.register(selector, SelectionKey.OP_ACCEPT);

    boolean stop 
= false;
    
int n = 0;

    System.
out.println("Server Start");

    
// 轮询
    while (!stop) {
      
// 获取Selector返回的时间值
      n = selector.select();

      
// 当传回的值大于0事,读时间发生了
      if (n > 0{
        Set 
set = selector.selectedKeys();
        Iterator it 
= set.iterator();

        
while (it.hasNext()) {
          skey 
= (SelectionKey) it.next();
          it.remove();

          
if (skey.isAcceptable()) {
            
// 从channel()中取得刚刚注册的Channel
            Socket socket = ((ServerSocketChannel) skey.channel()).accept().socket();

            PrintWriter writer 
= new PrintWriter(socket.getOutputStream(), true);

            
// 将"Hello, World"写入
            writer.write("Hello, World!\n");

            
// 睡眠3秒
            try {
              Thread.sleep(
3000);
            }
 catch (InterruptedException ie) {
            }


            
// 将"EXIT"写入Buffer
            writer.write("EXIT");

            
// 退出程序
            writer.close();
            
// stop = true;
          }

        }

      }

    }


    ssc.close();
    System.
out.println("Server Stop");
  }


  
public static void main(String[] args) throws Exception {
    Server server 
= new Server(5000);
    server.startServer();
  }

}

posted @ 2005-05-18 12:21 小米 阅读(719) | 评论 (0)编辑 收藏
     摘要:       最近在做有关Socket的程序,写了两个客户端程序,第一个客户端程序如下:  1package sample.nio; 2 3import java.io.IOException; 4import java.net.*; 5import&nb...  阅读全文
posted @ 2005-05-18 12:18 小米 阅读(3881) | 评论 (1)编辑 收藏

      从JDK1.4开始,SUN提供了JCE包,可以实现多种加密算法。下面是我的一个用JCE进行DES加密解密的程序:

package sample;

import java.security.
*;
import javax.crypto.
*;

/**
 * <p>Title: </p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2005</p>
 *
 * <p>Company: </p>
 *
 * @author George Hill
 * @version 1.0
 
*/


public class Test {
  
  
// 加密使用的Key
  private SecretKey key;
  
  
// 加密算法,JCE可用DES,DESede和Blowfish
  private static final String algorithm = "DES";
  
  
public Test() throws NoSuchAlgorithmException {
    KeyGenerator generator 
= KeyGenerator.getInstance(algorithm);
    key 
= generator.generateKey();
  }

  
  
/**
   * 利用DES算法加密
   * @param s String 需要加密的字符串
   * @return String 加密后的字符串
   * @throws Exception
   
*/

  
public String encryptData(String s) throws Exception {
    Cipher c 
= Cipher.getInstance(algorithm);
    c.init(Cipher.ENCRYPT_MODE, key);

    
return new String(c.doFinal(s.getBytes()));
  }

  
  
/**
   * 利用DES算法解密
   * @param s String 需要解密的字符串
   * @return String 解密后的字符串
   * @throws Exception
   
*/

  
public String decryptData(String s) throws Exception {
    Cipher c 
= Cipher.getInstance(algorithm);
    c.init(Cipher.DECRYPT_MODE, key);

    
return new String(c.doFinal(s.getBytes()));
  }

  
  
/**
   * 测试程序
   * @param args String[]
   * @throws Exception
   
*/

  
public static void main(String[] args) throws Exception {
    String s 
= "Hello";
    Test test 
= new Test();
    String encrypt 
= test.encryptData(s);
    System.
out.println(encrypt);
    String decrypt 
= test.decryptData(encrypt);
    System.
out.println(decrypt);
  }

}



      在实际的使用中,往往需要对加密后的byte数组进行转换,可以自己实现转换,或者用一些第三方的API。
posted @ 2005-05-18 11:55 小米 阅读(2620) | 评论 (4)编辑 收藏
仅列出标题
共6页: 上一页 1 2 3 4 5 6 下一页