Java 动态代理

1 What is that?

   代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。 

   动态代理: 在程序运行过程中产生的这个对象

              而程序运行过程中产生对象其实就是反射讲解的内容,

              所以,动态代理其实就是通过反射来生成一个代理对象

2 How to do ?

在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,

通过使用这个类和接口就可以生成动态代理对象。

JDK提供的代理只能针对接口做代理。

(1)Proxy类中的方法创建动态代理类对象 

他有一个方法:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

该方法用来产生一个object类型的代理对象                       

该对象并没有对应的.class文件,但在内存中有对应Class类型

 

实现 :              

        Object o = 

        java.lang.reflect.Proxy.

        newProxyInstance(ClassLoader loader,Class[] classes, InvocationHandler handler);

        

      参数说明:

        ClassLoader loader            类加载器  用于把.class文件(字节码文件)加载到内存中     通过getClassLoader()方法获取

        Class[] classes               目标接口的类型数组

        InvocationHandler handler   接口调用处理器  

(2)InvocationHandler接口

只有1个方法

 public Object invoke(Object proxy, Method method, Object[] args)

当代理对象调用父接口中定义的方法时 执行的就是该方法(被native标志的方法如 getClass等 不会)

 

(3)案例:

interface A{
    public void a();
    public void aa();
}

interface B{
    public void b();
    public void bb();
}

public class ProxyDemo {

    public ProxyDemo() {
    
    }

      public static void main(String[] args) {
        //1   获取ClassLoader
    //  需要生成一个类 就需要把这个类加载到方法区 谁来加载:ClassLoder
        ClassLoader loader = ProxyDemo.class.getClassLoader();
        
        //2 目标接口的类型数组
        Class[] interfaces = {A.class,B.class};
        
        //3 InvocationHandler
        InvocationHandler h = new InvocationHandler() {
            
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                System.out.println("invoking...");
                return null;
            }
        };
      }
       
       // o 实现了AB接口  
       Object o  =  Proxy.newProxyInstance(loader, interfaces, h);
        
      // 可以强转为父接口类型
    A a = (A)o;
    B b = (B)o;
        
    //调用接口中定义的方法
    a.a();
    b.b();
    a.aa();
    b.bb();

/*输出:
     invoking...
         invoking...
         invoking...
         invoking...
*/

o.toString();//invoking...
        
Class clazz = o.getClass();
System.out.println(clazz.getName());
//com.wei.proxy.$Proxy0
        
  /**
  * 代理对象调用大部分方法是实际调用的是 InvocationHandler的invoke方法
  * 但getClass等方法不是
*/
   }

}

3 InvocationHandler

InvocationHandler 接口是调用处理器  , 他只有1个方法

public Object invoke(Object proxy, Method method, Object[] args)

当代理对象调用父接口中定义的方法时 执行的就是该方法(被native标志的方法如 getClass等 不会)

invoke调用时机: 在调用代理对象所实现接口中的方法时

 

参数:

Object  proxy:代理对象,也就是Proxy.newProxyInstance()方法返回的对象,通常我们用不上它,而是提供一个该接口实现类的实例

Method method:表示当前被调用方法的反射对象

Object[] args:表示当前被调用方法的参数,当然若果没有参数的,所以args是一个零长数组。

借助这些参数我们可以完成更为复杂的操作,甚至返回一个结果

案例:

 1 interface MyFace{
 2     Object getObject(String s,int i);
 3 }
 4 
 5 public class InvocationHandlerDemo {
 6 
 7     public static void main(String[] args) {
 8         
 9         Object proxy = Proxy.newProxyInstance(InvocationHandlerDemo.class.getClassLoader(),
10                 new Class[]{MyFace.class},
11                 new InvocationHandler() {
12                     @Override
13                     public Object invoke(Object proxy, Method method, Object[] args)
14                             throws Throwable {
15                         
16                         System.out.println("invoking...");
17                         
18                                           //打印参数
19                         for (Object object : args) {
20                             System.out.println(object);
21                         }
22                         
23                                          //执行其他操作
24                         String s = (String) args[0];
25                         int i = (int) args[1];
26                         for(int j=1;j<=i;j++){
27                             System.out.println(j+"times for printing "+s);
28                         }
29                         
30                         return "result";
31                     }
32                 });
33         
34         MyFace f = (MyFace) proxy;
35         Object result = f.getObject("first", 10);
36         System.out.println(result);
37         
38     }
39     
40 }

 运行结果:

 

invoking...
first
10
1times for printing first
2times for printing first
3times for printing first
4times for printing first
5times for printing first
6times for printing first
7times for printing first
8times for printing first
9times for printing first
10times for printing first
Result

 

 

 

 4 代理工厂

动态代理帮我解决的问题一般不是为某些接口实提供实现,而是对接口的方法进行增强。

可以理解为在目标方法执行的前后等时机执行一些额外的操作,从而将业务方法和一些系统方法分离。

 1 public interface Waiter {
 2     public void serve();
 3 }
 4 
 5 
 6 public class ManWaiter implements Waiter {
 7 
 8     @Override
 9     public void serve() {
10         System.out.println("man waiter serving...");
11     }
12 }

上面提供了一个接口和他的一个实现

我们希望在serve方法执行前输出hello,在执行后输出bye bye

下面提供2个接口来规定前后的操作:

 1 //前置增强
 2 public interface BeforeAdvice {
 3 
 4     public void before();
 5     
 6 }
 7 //后置增强
 8 public interface AfterAdvice {
 9     public void after();
10 }

 

下面我们设计一个代理工厂来实现以下的功能:

1 设置目标对象 : 我们不为接口实现方法,而是增强该接口实现类的方法,

                我们自己提供一个实例,这也是不需要使用invoke方法第一个参数的原因

2 设置前置 后置增强

3 调用createProxy方法来生成代理对象  

       invoke方法:

        >执行BeforeAdvice的before

        >执行目标对象的目标方法

        >执行AfterAdvice的after

        >返回对象方法的结果

 4最后生成并返回代理对象

 

 1 import java.lang.reflect.InvocationHandler;
 2 import java.lang.reflect.Method;
 3 import java.lang.reflect.Proxy;
 4 
 5 public class ProxyFactory {
 6 
 7     private Object target = null;//目标对象
 8     private BeforeAdvice before;//前置增强
 9     private AfterAdvice  after;//后置增强
10 
11     private Object result ;//目标对象的返回值
12     
13     public ProxyFactory() {
14         super();
15     }
16     
17     
18     @SuppressWarnings("rawtypes")
19     public Object createProxy(){
20         
21         ClassLoader loader = ProxyFactory.class.getClassLoader();
22         Class [] interfaces = target.getClass().getInterfaces();
23 
24         InvocationHandler h = new InvocationHandler() {    
25             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
26                 
27                 if(before != null){
28                     before.before();
29                 }
30                 
31                 result =  method.invoke(target, args);
32                 
33                 if(after != null){
34                     after.after();
35                 }
36                 
37                 return result;
38             }
39         };
40         
41         Object proxyObject =  Proxy.newProxyInstance(loader, interfaces, h);
42         return proxyObject;
43     }
44     
45     public ProxyFactory(Object target, BeforeAdvice befor, AfterAdvice after) {
46         super();
47         this.target = target;
48         this.before = befor;
49         this.after = after;
50     }
51 
52     public void setTarget(Object target) {
53         this.target = target;
54     }
55 
56     public void setBefore(BeforeAdvice before) {
57         this.before = before;
58     }
59 
60     public void setAfter(AfterAdvice after) {
61         this.after = after;
62     }
63 }

测试:

public class Demo {

    public static void main(String[] args) {

        Waiter w = new ManWaiter();
        
        BeforeAdvice before = new BeforeAdvice() {
            public void before() {
                System.out.println("hello");
            }
        };
        
        AfterAdvice after = new AfterAdvice() {
            public void after() {
                System.out.println("bye bye ...");
            }
        };
        
        ProxyFactory factory = new ProxyFactory();
        
        factory.setTarget(w);
        factory.setBefore(before);
        factory.setAfter(after);
        
        Object o  = factory.createProxy();
        
        Waiter proxy = (Waiter)o;
        proxy.serve();
        
    }
}

输出:

 

hello
man waiter serving...
bye bye ...

若果有spring知识基础的话将会很容易理解上面的代码

这其实就是AOP的基础

借助这种思想,可以很容易的完成日志记录、事务控制等和业务逻辑没有关系的系统逻辑

这样的工厂不需要我们实现

Spring已经提供了功能完美的产品

 

 

 

cglib动态代理实现

Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:

CGLIB的核心类:
    net.sf.cglib.proxy.Enhancer – 主要的增强类
    net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
    net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
    Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。

net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;

第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。

 

 
public interface UserService {
    public String getName(int id);

    public Integer getAge(int id);
}

public class UserServiceImpl implements UserService {
    @Override
    public String getName(int id) {
        System.out.println("------getName------");
        return "Tom";
    }

    @Override
    public Integer getAge(int id) {
        System.out.println("------getAge------");
        return 10;
    }
}

 

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
        System.out.println(method.getName());
        Object o1 = methodProxy.invokeSuper(o, args);
        System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
        return o1;
    }
}

 

import com.meituan.hyt.test3.service.UserService;
import com.meituan.hyt.test3.service.impl.UserServiceImpl;
import net.sf.cglib.proxy.Enhancer;



public class Main2 {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);
        enhancer.setCallback(cglibProxy);

        UserService o = (UserService)enhancer.create();
        o.getName(1);
        o.getAge(1);
    }
}

 

运行结果:

 

++++++before CGLIB$getName$0++++++
getName
------getName------
++++++before CGLIB$getName$0++++++
++++++before CGLIB$getAge$1++++++
getAge
------getAge------
++++++before CGLIB$getAge$1++++++

 

AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。

不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。

AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。

总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。

还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

 

 

posted @ 2017-02-03 14:29  yweihainan  阅读(666)  评论(0编辑  收藏  举报