我的Session Bean Container实现(2)

第二节 一个执行Bean的通用框架

 

上面的例子中,客户端程序是通过网络来调用的。我们首先也不要求那么的复杂,只要写一个能够本地的Session Bean的框架就可以了。

 

Home接口和Remote接口都是没有实现的,要调用Home接口和Remote接口的方法,一定要实现Home接口和Remote接口。但是我们真的要一个一个地去实现他们吗?显然即使能够实现,也不是好的方案。还好,Java1.3提供的

Proxy机制可以帮助我们实现一个通用的Home接口和Remote接口。下面让我们先来了解Proxy吧。

 

Proxy用于动态地创建一个或多个接口的“实现“。这里所说地“实现”并不是调用Proxy的方法后,就有新的实现接口的二进制代码产生。只是用Proxy动态创建的对象(准确地说是Proxy对象)可以将用户的调用进行拦截,拦截的内容包括:方法名和用户传递的参数。

 

有点抽象吧,让我们以创建 CartHome接口的Proxy对象为例来帮助我们理解Proxy。为了创建一个接口的Proxy对象,先要创建一个Proxy类。

 

下面是创建CartHome接口的代理类的代码

 

Class cartHomeProxyClass = Proxy.getProxyClass( CartHome.class.getClassLoader(),

                                                                                       new Class[]{CartHome.class} );

 

Proxy.getProxyClass()有两个参数,第一个参数为ClassLoader,表示我们创建的cartHomeProxyClass代理类由这个ClassLoader管理。第二个参数表示我们要在新创建的Proxy类“实现”哪几个接口。这里我们只实现了CartHome接口。

 

Proxy类创建好了以后,就可以调用它的构造函数创建一个Proxy对象,下面为创建CartHomeProxy对象代码:

 

InvocationHandler handler = new CartHomeInvocationHandler(…);

 

CartHome cartHome = (CartHome) cartHomeProxyClass.getConstructor( 

                                    new Class[]  {InvocationHandler.class }).

newInstance(new Object[] { handler });

 

 

我们用两步创建了一个Proxy对象,Proxy中提供了newProxyInstance()方法可以将上面的两步合为一步创建Proxy对象。一步创建CartHome Proxy对象的代码为:

 

InvocationHandler handler = new CartHomeInvocationHandler(…);

CartHome cartHome = (CartHome) Proxy.newProxyInstance( CartHome.class.getClassLoader(),

                                           new class[]{CartHome.class},

                                           handler );

 

 

在创建Proxy对象的时候,要一个InvocationHandler对象。上面所说的Proxy对象拦截的用户调用的内容就会传入到我们实现的InvocationHandler对象中。InvocationHandler的声明为:

 

public interface InvocationHandler {

        public Object invoke( Object proxy,

Method method,

Object[] args )

throws Throwable;

}

 

当用户调用cartHome.create( “Xiao Min” )时,CartHomeInvocationHandler实现的invoke()方法就会被调用,参数包括:

 

1) proxy,就是用CartHomeProxy类创建的Proxy对象,即cartHome对象。

2) method就是CartHome接口的create(String person)方法。

3) args就是调用create(String)方法时用户传入的参数,即“Xiao Min

 

对被拦截的cartHome.create(String person)方法在InvocationHandler.invoke()中进行解释执行,返回同CartHome.create(String person)返回类型一样的对象(即Cart类型对象)。对CartHome方法调用进行拦截的InvocationHandler代码框架如下:

 

public class CartHomeInvocationHandler implements InvocationHandler {

 

               //变量声明

               ...

              

               public CartHomeInvocationHandler(...) {

               }

              

               public Object invoke( Object proxy,

                                             Method method,

                                             Object[] args ) throws Throwable

               {

                              Cart cart = ...;

                             

                              ....;

                              return cart;                                                       

               }

}

 

现在我们可以对调用一个接口的Proxy对象的方法进行拦截,解释,执行并返回所要求得结果类型了。

 

因为Cart类也是一个接口类,所以也要用Proxy创建一个CartProxy类。因此,CartHomeInvocationHandler的定义可以细化为:

 

public class CartHomeInvocationHandler implements InvocationHandler {

              

               public CartHomeInvocationHandler(...) {

               }

              

               public Object invoke( Object proxy,

                                             Method method,

                                             Object[] args ) throws Throwable

               {

                              InvocationHandler handler = CartInvocationHandler(...);

                              Cart cart = (Cart)Proxy.newProxyInstance( proxy.getClass().getClassLoader(),

                                                                                                         new Class[]{Cart.class},

                                                                                                         handler );

 

                              ....;

                              return cart;                                                       

               }

}

 

 

根据SunEJB规范,对被创建的Cart Proxy对象的方法进行调用,就是对某个CartBean对象的同名方法(指方法明,参数类型和返回类型都相同)进行调用。因此,被创建的Cart Proxy对象中用到的CartInvocationHandler类可以写为:

 

public class CartInvocationHandler implements InvocationHandler {

              

               private CartBean cartBean_;

              

               public CartInvocationHandler( CartBean cartBean ) {

                              cartBean_ = cartBean;

               }

              

               public Object invoke( Object proxy,

                                             Method method,

                                             Object[] args ) throws Throwable

               {

                              Method m = cartBean_.getClass().getMethod( method.getName(),method.getParameterTypes() );

                             

                              if( m != null )

                                             return m.invoke( cartBean_, args );

                              throw new RemoteException( "Fail to find the method:" + method );

               }

}

 

这里我们忽略了对异常的处理。我们利用Java的反射机制寻找在CartBean对象中对应的方法并对它进行调用。

 

如果用户每调用一个代理对象cartHomecreate方法时,创建一个新的CartBean对象的话,CartHomeInvocationHandler类的定义就可以为:

 

public class CartHomeInvocationHandler implements InvocationHandler {

              

               public CartHomeInvocationHandler(...) {

               }

              

               public Object invoke( Object proxy,

                                             Method method,

                                             Object[] args ) throws Throwable

               {

                             CartBean cartBean = new CartBean();

                              InvocationHandler handler = CartInvocationHandler(cartBean );

                              Cart cart = (Cart)Proxy.newProxyInstance( proxy.getClass().getClassLoader(),

                                                                                                         new Class[]{Cart.class},

                                                                                                         handler );

return cart;

                }

 

 

根据Sun EJB Container规范,Session BeanHome接口中定义的create方法应该调用Bean中对应的ejbCreate()方法,CartHomeInvocationHandler可进一步细化为:

public class CartHomeInvocationHandler implements InvocationHandler {

              

               public CartHomeInvocationHandler() {

               }

              

               public Object invoke( Object proxy,

                                             Method method,

                                             Object[] args ) throws Throwable

               {

                             CartBean cartBean = new CartBean();

                              InvocationHandler handler = CartInvocationHandler(cartBean );

                              Cart cart = (Cart)Proxy.newProxyInstance( proxy.getClass().getClassLoader(),

                                                                                                         new Class[]{Cart.class},

                                                                                                         handler );

if( method.getName().equals( “create” ) ) {

        Method m = cartBean.getClass().getMethod(“ejbCreate”,

                                                                             method. GetParameterTypes() );

        if( m == null ) throw new RemoteException(“fail to find the ejbCreate()”);

        m.invoke( cartBean, args );

}

return cart;

                }

 

 

到现在为止我们已经完成了CartHomeInvocationHandlerCartInvocationHandler, 可以编写测试程序来测试一下了。

 

InvocationHandler handler = new CartHomeInvocationHandler();

CartHome cartHome = (CartHome) Proxy.newProxyInstance( CartHome.class.getClassLoader(),

                                                                                      new class[]{CartHome.class},

                                                                                          handler );

Cart cart = cartHome.create( "Xiao Min" );

cart.addBook( "Effective Java");

cart.addBook( "Master Java");

 

Vector books = cart.getContents();

for( int i = 0, n = books.size(); i < n; i++ ) {

               System.out.println( books.get( i ) );

}

 

虽然,我们没有实现CartHome接口和Cart接口,但是通过运用JavaProxy机制,我们可以执行CartBean中的方法了。

 

如果为每一个具体的Home接口和Remote接口都编写各自的InvocationHandler,虽然工作量不大,但是不能做到通用。能不能够为所用的Home接口和Remote接口编写通用的InvocationHandler呢?答案是肯定的。我们分析一下如何编写通用的InvocationHandler

 

先考察Remote接口的通用InvocationHandler的编写。虽然我们传递到CartInvocationHandler构造函数的参数类型是CartBean,但是在CartInvocationHandler.invoke()方法中调用CartBean的方法是使用反射机制。这就是说我们只传递一个Object对象到CartInvocationHandler中,CartInvocationHandler.invoke()也可以工作。我们将CartInvocationHandler改名为RemoteInvocationHandler以表示我们编写的是一个通用的Remote接口的InvocationHandlerRemoteInvocationHandler的新代码如下:

 

public class RemoteInvocationHandler implements InvocationHandler {

              

               private Object bean_;

              

               public RemoteInvocationHandler ( Object bean ) {

                              bean_ = bean;

               }

              

               public Object invoke( Object proxy,

                                             Method method,

                                             Object[] args ) throws Throwable

               {

                             try {

                                      Method m = bean_.getClass().getMethod( method.getName(),method.getParameterTypes() );

                             

                                       if( m != null )

                                             return m.invoke( bean_, args );

                                       throw new RemoteException( "Fail to find the method:" + method );

}catch( Exception ex ) {

         throw new RemoteException(“Fail to execute method:” + method, ex );

}

               }

}

 

 

再考察Home接口的通用InvocationHandler的编写。在CartHomeInvocationHandler中,每次调用CartHomeInvocationHandler.invoke()时,都要创建一个CartBean对象。如果我们有什么方法只要知道CartBean的名字,也能创建CartBean的话,那么我们的CartHomeInvocationHandler就可以改造成为一个通用的InvocationHandler

SunEJB规范中规定,一个Bean必须要有一个不带参数的构造函数。这就是说我们在构造Bean时不能传递参数到Bean 中。如果要传递参数的话就只能通过函数调用。EJB规范中,我们在构造好Bean以后,可以立即调用ejbCreate方法传递参数到Bean中(在这一点上,Sun完全没有遵守面向对象的守则,只是用一种约定)。既然Bean中一定有一个不带参数构造函数,我们完全可以用Bean的名字构造一个Bean对象。将CartHomeInvocationHandler名字改为HomeInvocationHandler以表示我们编写的是一个通用的Home接口的InvocationHandler,其代码如下:

 

public class HomeInvocationHandler implements InvocationHandler {

private String remoteClassName_;

private String beanClassName_;

 

               public HomeInvocationHandler( String remoteClassName, String beanClassName ) {

remoteClassName_ = remoteClassName;

beanClassName_ = beanClassName;

               }

              

               public Object invoke( Object proxy,

                                             Method method,

                                             Object[] args ) throws Throwable

               {

                             try {

       Class beanClass = Class.forName( beanClassName_ );

       Class remoteClass = Class.forname( remoteClassName_ );

       Object bean = beanClass.newInstance();

                                     InvocationHandler handler = RemoteInvocationHandler(bean );

                                     Object proxyObj = Proxy.newProxyInstance( proxy.getClass().getClassLoader(),

                                                                                                         new Class[]{remoteClass },

                                                                                                         handler );

       if( method.getName().equals( "create" ) ) {

               Method m = bean.getClass().getMethod(“ejbCreate”,

                                                                             method. getParameterTypes() );

               if( m == null ) throw new RemoteException(“fail to find the ejbCreate()”);

               m.invoke( bean, args );

       }

       return proxyObj;

}catch( Exception ex ) {

        throw new RemoteException( “fail to call the method:” + method, ex );

}

                }

}

 

 

使用新编写的HomeInvocationHandlerRemoteInvocationHandler,我们的测试代码就变为:

 

InvocationHandler handler = new HomeInvocationHandler( “Cart”, “CartBean”);

CartHome cartHome = (CartHome) Proxy.newProxyInstance( CartHome.class.getClassLoader(),

                                                                                      new class[]{CartHome.class},

                                                                                          handler );

Cart cart = cartHome.create( "Xiao Min" );

cart.addBook( "Effective Java");

cart.addBook( "Master Java");

 

Vector books = cart.getContents();

for( int i = 0, n = books.size(); i < n; i++ ) {

               System.out.println( books.get( i ) );

}

 

如果进一步可以将Home接口的代理对象的创建封装起来,就更是完美了。我们将他封装到HomeProxyFactory类中吧。因此,HomeProxyFactory可以写为:

public class HomeProxyFactory

{

               public static Object createHomeProxy( String homeClassName, String remoteClassName, String beanClassName ) {

                              try {

                                             InvocationHandler handler = new HomeInvocationHandler ( remoteClassName, beanClassName );                       

                                                                                         

                                             Class homeClass = Class.forName( homeClassName );

                                             return Proxy.newProxyInstance( homeClass.getClassLoader(), new Class[]{homeClass}, handler );                      

                              }catch( Exception ex ) {

                                             throw new RuntimeException( ex );

                              }            

               }

}

 

测试代码引用新创建的HomeProxyFactory的话,测试代码就变为了:

CartHome cartHome = (CartHome) HomeProxyFactory.createHomeProxy( “CartHome”, “Cart”, “CartBean”)

Cart cart = cartHome.create( "Xiao Min" );

cart.addBook( "Effective Java");

cart.addBook( "Master Java");

 

Vector books = cart.getContents();

for( int i = 0, n = books.size(); i < n; i++ ) {

               System.out.println( books.get( i ) );

}

 

是不是很简单?

 

你可能感兴趣的:(我的Session Bean Container实现(2))