您的位置:首页 > 编程语言 > Java开发

Java探索之——动态代理(JDK和CGlib方式)

2013-05-08 23:42 429 查看
动态代理是java中非常有用的特性之一,目前Spring作为MVC框架的主流选择,主要归功于其最重要的两个特性:Ioc和AOP,他们使得项目模块可以以一种非常松散的耦合的关系组织起来,大大减轻了开发者的负担。AOP正是动态代理实现的典型案例之一,动态代理目前主要有两种方式,JDK动态代理以及CGlib动态代理,下面以代码为例一一讲解。CGlib需要用到cglibasm的jar包。

首先定义接口:

public interface SayHello {

@MyAnnotation("annotation declared in interface")
public void sayHello();
}


再定义实现类:

public class SayHelloImpl implements SayHello{

@Override
@MyAnnotation(value="annotation declared in method")
public void sayHello() {
System.out.println("Hello!");
}
}


为了测试对注解的支持,定义一个注解MyAnnotation,@Retention注解表示注解的保存位置,@Target注解则表示该注解可以标记的目标。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
String value();
}


JDK动态代理类,该类实现了InvocationHandler接口,需要在类中实现invoke方法。在本实例中invoke方法中获取了被代理方法中的@MyAnnotation注解的value值,并打印;若注解值为空串,则抛出异常。

/**
* 实现JDK动态代理接口类
* @author rui.chen
*
*/
public class JDKProxy<T> implements InvocationHandler{
private T obj;

public JDKProxy(T obj){
this.obj = obj;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
MyAnnotation ann = method.getAnnotation(MyAnnotation.class);
if(ann!=null){
String value = ann.value();
if("".equals(value)){
System.out.println("You input a null value");
throw new NullPointerException();
}else{
System.out.println("Annotation:"+value);
return method.invoke(obj, args);
}
}else{
return null;
}
}

}


实现CGlib动态代理类需要实现MethodInterceptor接口,该接口中有一个类似于invoke的方法intercept,从名字中可以看出,CGlib的AOP意味比JDK更浓,InvocationHandler更像一个纯代理接口。

/**
* 实现CGlib动态代理接口类
* @author rui.chen
*
*/
public class CglibProxy<T> implements MethodInterceptor{

private T target;

public Object getInstance(T target){
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}

@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
MyAnnotation ann = method.getAnnotation(MyAnnotation.class);
if(ann!=null){
String value = ann.value();
if("".equals(value)){
System.out.println("You input a null value");
throw new NullPointerException();
}else{
System.out.println("Annotation:"+value);
return methodProxy.invoke(target, args);
}
}else{
return null;
}
}

}


测试类。在这里要特别注意,使用JDK动态代理时,由于其只支持接口,因此注解必须标记在接口上面才会生效,而且Proxy.newProxyInstance方法的第二个参数必须保证是一个接口class数组,否则将导致被代理类无法正常被加载并实例化。而在CGlib代理时,SayHelloImpl类不必实现任何接口。在这里也一并说说二者实现方式的区别,JDK方式是通过生成一个实现了代理接口的实现类来完成的代理,而CGLib则是通过继承被代理类,生成一个子类并覆盖子类的实现方法来完成的,因此,使用CGLib时,被代理对象不能被声明为final,否则无法实现继承。

public class Run {

/**
* JDK动态代理测试
*/
public static void JDKProxyTest(){
SayHello t = new SayHelloImpl();
InvocationHandler handler = new JDKProxy<SayHello>(t);

SayHello p = (SayHello) Proxy.newProxyInstance(SayHello.class.getClassLoader(),
new Class[]{SayHello.class},handler);
/*
* SayHello.class.getInterfaces()会导致无法实例化类,必须使用SayHelloImpl.class.getInterfaces()
SayHello p = (SayHello) Proxy.newProxyInstance(SayHello.class.getClassLoader(),
SayHello.class.getInterfaces(),handler);     */
p.sayHello();
}

/**
* CGlib动态代理测试
*/
public static void CGlibProxyTest(){
SayHelloImpl t = new SayHelloImpl();

CglibProxy<SayHelloImpl> cglib = new CglibProxy<SayHelloImpl>();
SayHelloImpl p = (SayHelloImpl) cglib.getInstance(t);
p.sayHello();
}

/**
* @param args
*/
public static void main(String[] args) {
JDKProxyTest();
CGlibProxyTest();
}

}


运行结果:

Annotation:annotation declared in interface
Hello!
Annotation:annotation declared in method
Hello!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: