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

首先定义接口:

1 public interface SayHello {
2 
3     @MyAnnotation("annotation declared in interface")
4     public void sayHello();
5 }

再定义实现类:

1 public class SayHelloImpl implements SayHello{
2 
3     @Override
4     @MyAnnotation(value="annotation declared in method")
5     public void sayHello() {
6         System.out.println("Hello!");
7     }
8 }

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

1 @Retention(RetentionPolicy.RUNTIME)
2 @Target(ElementType.METHOD)
3 public @interface MyAnnotation {
4     String value();
5 }

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

 1 /**
 2  * 实现JDK动态代理接口类
 3  * @author rui.chen
 4  *
 5  */
 6 public class JDKProxy<T> implements InvocationHandler{
 7     private T obj;
 8     
 9     public JDKProxy(T obj){
10         this.obj = obj;
11     }
12     
13     @Override
14     public Object invoke(Object proxy, Method method, Object[] args)
15             throws Throwable {
16         MyAnnotation ann = method.getAnnotation(MyAnnotation.class);
17         if(ann!=null){
18             String value = ann.value();
19             if("".equals(value)){
20                 System.out.println("You input a null value");
21                 throw new NullPointerException();
22             }else{
23                 System.out.println("Annotation:"+value);
24                 return method.invoke(obj, args);
25             }
26         }else{
27             return null;
28         }
29     }
30 
31 }

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

 1 /**
 2  * 实现CGlib动态代理接口类
 3  * @author rui.chen
 4  *
 5  */
 6 public class CglibProxy<T> implements MethodInterceptor{
 7 
 8     private T target;
 9     
10     public Object getInstance(T target){
11         this.target = target;
12         Enhancer enhancer = new Enhancer();
13         enhancer.setSuperclass(target.getClass());
14         enhancer.setCallback(this);
15         return enhancer.create();
16     }
17     
18     @Override
19     public Object intercept(Object proxy, Method method, Object[] args,
20             MethodProxy methodProxy) throws Throwable {
21         MyAnnotation ann = method.getAnnotation(MyAnnotation.class);
22         if(ann!=null){
23             String value = ann.value();
24             if("".equals(value)){
25                 System.out.println("You input a null value");
26                 throw new NullPointerException();
27             }else{
28                 System.out.println("Annotation:"+value);
29                 return methodProxy.invoke(target, args);
30             }
31         }else{
32             return null;
33         }
34     }
35 
36 }

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

 1 public class Run {
 2 
 3     /**
 4      * JDK动态代理测试
 5      */
 6     public static void JDKProxyTest(){
 7         SayHello t = new SayHelloImpl();
 8         InvocationHandler handler = new JDKProxy<SayHello>(t);
 9         
10         SayHello p = (SayHello) Proxy.newProxyInstance(SayHello.class.getClassLoader(), 
11                 new Class[]{SayHello.class},handler);
12         /*
13          * SayHello.class.getInterfaces()会导致无法实例化类,必须使用SayHelloImpl.class.getInterfaces()
14         SayHello p = (SayHello) Proxy.newProxyInstance(SayHello.class.getClassLoader(), 
15                 SayHello.class.getInterfaces(),handler);     */
16         p.sayHello();
17     }
18     
19     /**
20      * CGlib动态代理测试
21      */
22     public static void CGlibProxyTest(){
23         SayHelloImpl t = new SayHelloImpl();
24         
25         CglibProxy<SayHelloImpl> cglib = new CglibProxy<SayHelloImpl>();
26         SayHelloImpl p = (SayHelloImpl) cglib.getInstance(t);
27         p.sayHello();
28     }
29     
30     /**
31      * @param args
32      */
33     public static void main(String[] args) {
34         JDKProxyTest();
35         CGlibProxyTest();
36     }
37 
38 }

 运行结果:

1 Annotation:annotation declared in interface
2 Hello!
3 Annotation:annotation declared in method
4 Hello!

 

posted on 2013-05-08 23:42  壹零叁柒  阅读(268)  评论(0编辑  收藏  举报