elathen的博客

« | »

使用CGLIB包创建动态代理(3)(原)

elathen | 16 一月, 2007 23:01

Use a MethodInterceptor为了更好的使用代理,我们可以使用自己定义的MethodInterceptor类型回调(callback)来代替net.sf.cglib.proxy.NoOp回调。当对代理中所有方法的调用时,都会转向MethodInterceptor类型的拦截(intercept)方法,在拦截方法中再调用底层对象相应的方法。下面我们举个例子,假设你想对目标对象的所有方法调用进行权限的检查,如果没有经过授权,就抛出一个运行时的异常AuthorizationException。其中AuthorizationService.java接口的代码如下:

package com.lizjason.cglibproxy;

import java.lang.reflect.Method;

/**
* A simple authorization service for illustration purpose.
*
* @author Jason Zhicheng Li (jason@lizjason.com)
*/
public interface AuthorizationService {
/**
* Authorization check for a method call. An
AuthorizationException
* will be thrown if the check fails.
*/
void authorize(Method method);
}
net.sf.cglib.proxy.MethodInterceptor接口的实现的类AuthorizationInterceptor.java代码如下:

package com.lizjason.cglibproxy.impl;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

import com.lizjason.cglibproxy.AuthorizationService;

/**

* A simple MethodInterceptor implementation to

* apply authorization checks for proxy method calls.

*

* @author Jason Zhicheng Li (jason@lizjason.com)

*

*/

public class AuthorizationInterceptor implements MethodInterceptor {

private AuthorizationService authorizationService;

/**

* Create a AuthorizationInterceptor with the given

* AuthorizationService

*/

public AuthorizationInterceptor (AuthorizationService authorizationService) {

this.authorizationService = authorizationService;

}

/**

* Intercept the proxy method invocations to inject authorization check.

* The original method is invoked through MethodProxy.

* @param object the proxy object

* @param method intercepted Method

* @param args arguments of the method

* @param proxy the proxy used to invoke the original method

* @throws Throwable any exception may be thrown; if so, super method will not be invoked

* @return any value compatible with the signature of the proxied method.

*/

public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy ) throws Throwable {

if (authorizationService != null) {

//may throw an AuthorizationException if authorization failed

authorizationService.authorize(method);

}

return methodProxy.invokeSuper(object, args);

}

}


我们可以看到在拦截方法中,首先进行权限的检查,如果通过权限的检查,拦截方法再调用目标对象的原始方法。由于性能的原因,对原始方法的调用我们使用CGLIBnet.sf.cglib.proxy.MethodProxy对象,而不是反射中一般使用
java.lang.reflect.Method
对象。


Use a CallbackFilter

net.sf.cglib.proxy.CallbackFilter允许我们在方法层设置回调(callback)。假如你有一个PersistenceServiceImpl类,它有两个方法:saveload,其中方法save需要权限检查,而方法load不需要权限检查。



package com.lizjason.cglibproxy.impl;

import com.lizjason.cglibproxy.PersistenceService;

/**

* A simple implementation of PersistenceService interface

*

* @author Jason Zhicheng Li (jason@lizjason.com)

*/

public class PersistenceServiceImpl implements PersistenceService {

public void save(long id, String data) {

System.out.println(data + " has been saved successfully.");

}

public String load(long id) {

return "Jason Zhicheng Li";

}

}

注意到PersistenceServiceImpl类实现了PersistenceService 接口,因此没有要求要使用CGLIB创建代理。
net.sf.cglib.proxy.CallbackFilter
接口的实现如下:

package com.lizjason.cglibproxy.impl;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.CallbackFilter;

/**

* An implementation of CallbackFilter for PersistenceServiceImpl

*

* @author Jason Zhicheng Li (jason@lizjason.com)

*/

public class PersistenceServiceCallbackFilter implements CallbackFilter {

//callback index for save method

private static final int SAVE = 0;

//callback index for load method

private static final int LOAD = 1;

/**

* Specify which callback to use for the method being invoked.

* @method the method being invoked.

* @return the callback index in the callback array for this method

*/

public int accept(Method method) {

String name = method.getName();

if ("save".equals(name)) {

return SAVE;

}

// for other methods, including the load method, use the

// second callback

return LOAD;

}

}


accept
方法中对代理方法和回调进行了匹配,返回的值是某方法在回调数组中的索引。下面是PersistenceServiceImpl类代理的实现。


...

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(PersistenceServiceImpl.class);

CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();

enhancer.setCallbackFilter(callbackFilter);

AuthorizationService authorizationService = ...

Callback saveCallback = new AuthorizationInterceptor(authorizationService);

Callback loadCallback = NoOp.INSTANCE;

Callback[] callbacks = new Callback[]{saveCallback, loadCallback };

enhancer.setCallbacks(callbacks);

...

return (PersistenceServiceImpl)enhancer.create();

在这个例子中save方法使用了AuthorizationInterceptor实例,load方法使用了NoOp实例。此外,你也可以通过
net.sf.cglib.proxy.Enhancer.setInterfaces(Class[])
方法指定代理对象所实现的接口。

除了为net.sf.cglib.proxy.Enhancer指定回调数组,你还可以通过net.sf.cglib.proxy.Enhancer.setCallbackTypes(Class[]) 方法指定回调类型数组。当创建代理时,如果你没有回调实例的数组,就可以使用回调类型。象使用回调一样,你必须使用,net.sf.cglib.proxy.CallbackFilter为每一个方法指定一个回调类型索引。你可以从http://www.lizjason.com/downloads/下载设置回调类型和接口的完整代码。

小结 GLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。 它底层使用字节码处理框架ASM。其原理是,生产一个要代理类的子类,子类覆盖要代理的类的所有不是final的方法。 它比使用java反射的JDK动态代理要快。通常情况下,你可以使用JDK的动态代理创建代理,当你要代理的类没有实现接口 或者为了更好的性能,CGLIB是一个好的选择。


最新回复

Comment Icon 得到学习

xmddl369 | 31/12/2008, 16:05

三篇看下来也算入门了,自己写了例子深有体会

发表评论

 
Accessible and Valid XHTML 1.0 Strict and CSS
Powered by pLog - Design by BalearWeb