JDK动态代理(接口的代理)

  本文基于动态代理的基本思路,实现对接口的动态代理,使用接口函数完成你想完成的任务。其实主要是为了理解在使用mybatis的时候只有dao层接口函数,却可以在service层用来实现其配置文件中sql语句的功能,恍惚给人一种接口被执行的阴影,下面主要为了揭开此面纱。

1.动态代理简介

 定义:为其他对象提供一种代理以控制对这个对象的访问。
       大多时候理解的动态代理是实现InvocationHandler接口,重写其invoke方法,然后在调用被代理的方法的时候,统一跳到invoke方法,在实现InvocationHandler的时候,在invoke方法加上自己想做的处理,然后反射调用此前被代理的方法。完成了一次基本的动态代理。这种实现与接口被代理略微有一点不同,本质上是相通的, 下面以实例分析。


2.动态代理简单实例

     1.首先定义一个接口,源于JDK动态代理的原因, 必须实现接口,,,否则可以使用CGLIB    
public interface IAnimal {  
    public void say();  
}
    2.一个对接口的实现类,主要就是基于此实现类对类中的方法调用的时候,使用动态代理,跳到InvocationHandler,统一地做一些想做的事情。
public class AnimalImpl implements IAnimal{  
  
    @Override  
    public void say() {  
        System.out.println("i'm a deaf");  
    }  
  
}  
      3.动态代理的关键类,invoke方法,做你想做的事,此处需要具体反射的基本知识。
public class AnimalHandler implements InvocationHandler {  
  
    private Object obj;  
      
    public AnimalHandler(Object obj){  
        this.obj=obj;  
    }  
      
      
    @Override  
    public Object invoke(Object proxy, Method method, Object[] args)  
            throws Throwable {  
          
        System.out.println("before");  
        Object result = method.invoke(obj, args);  
        System.out.println("after");  
        return result;  
    }  
  
}  
        4.随便定义一个测试类,使用main方法测试此实例。
public static void main(String[] args){  
        IAnimal p = new AnimalImpl();  
        AnimalHandler handler = new AnimalHandler(p);  
          
        IAnimal proxy = (IAnimal)Proxy.newProxyInstance(p.getClass().getClassLoader(), p.getClass().getInterfaces(), handler);  
        proxy.say();  
    }  
          上面完成了一个最基本的动态代理,拥有全部的所用到的类与步骤,在理解Mybatis困惑的地方就是没有对接口的实现类,却直接使用了动态代理,也就是在实现InvocationHandler的类中直接将接口绑定了具体的handler中的对象上,属于阉割版本的代理,实质上都是一样的。

3.对接口的动态代理


    1.这就是本文要对其代理的接口
package com.yzh.DynamicProxy;
 public interface Iplay {
   public void paly();
}
     2.实现invocationHandler的类,也即是直接对接口绑定的类,类似于同时实现了基本动态代理的两步
package com.yzh.DynamicProxy;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class PlayProxy implements InvocationHandler {
   
private Class inter_face;

PlayProxy(Class inter_face){
this.inter_face = inter_face;
}
//空构造函数
PlayProxy(){

}
//代理执行方法
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("play");
//do what you want to do
System.out.println("test");
return null;
}
    //一种代理方式
public static Object bind(Class inter_face){
PlayProxy p = new PlayProxy();
return Proxy.newProxyInstance(inter_face.getClassLoader(), new Class[]{inter_face}, p);
}

}
    此类用三种方法将接口直接绑定到实现的代理类,即构造方法将接口传入,空的构造函数,重新定义一个专门实现绑定的静态方法。实质上三者的绑定也是殊途同归,终究还是和最上面基本实现动态代理的过程相同。
 3.简单测试
package com.yzh.DynamicProxy;

import java.lang.reflect.Proxy;

public class Test {

public static void main(String[] args) {
//利用invocationHandler中定义的绑定方法实现动态代理
Iplay i = (Iplay)PlayProxy.bind(Iplay.class);
i.paly();
       //创建代理时添加绑定关系
PlayProxy p = new PlayProxy();
Iplay pl = (Iplay)Proxy.newProxyInstance(Iplay.class.getClassLoader(), new Class[]{Iplay.class}, p);
pl.paly();
//利用构造函数实现绑定关系
PlayProxy pp = new PlayProxy(Iplay.class);
Iplay ph = (Iplay)Proxy.newProxyInstance(Iplay.class.getClassLoader(), new Class[]{Iplay.class}, pp);
   ph.paly();
}

}
总结:其实jdk动态代理中invocationHandler仅仅实现的是根据不同业务对方法添加不同的上下文逻辑,也即是拦截器的具体做法(这正是spring的AOP编程理论基础,恰好springMVC的拦截器其实是和过滤器原理一样,不是使用动态代理,而是在调用具体方法前执行所添加的拦截器,只是执行先后顺序的关系)具体要绑定什么样的业务类,反射什么样的方法,都是可以具体设定的,以构造方法加入或者外部方法加入都没有影响。接口才是知道最终调用什么方法,发射什么方法的源头,这也是JDK必须实现接口的原因。
      接口被代理的原理一样,只是没有实现类,不可以实例化,所以不得不传入.class文件。
      观察具体的类被动态代理,其实也是传入的.class文件,因为Proxy.newProxyInstance接受的前两个参数也是业务类.getClass().getClassLoader(),原理相同,只是传入的先后顺序不同而已。


猜你喜欢

转载自blog.csdn.net/ccityzh/article/details/66018397