java代理模式

1.代理模式

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法

举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决.这就是代理思想在现实中的一个例子

用图表示如下:

代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象

1.1.静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.

下面举个案例来解释:
模拟保存动作,定义一个保存动作的接口:IUserDao.java,然后目标对象实现这个接口的方法UserDao.java,此时如果使用静态代理方式,就需要在代理对象(UserDaoProxy.java)中也实现IUserDao接口.调用的时候通过调用代理对象的方法来调用目标对象.
需要注意的是,代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法

代码示例:
接口:IUserDao.java

/**
 * 接口
 */
public interface IUserDao {

    void save();
}

目标对象:UserDao.java

/**
 * 接口实现
 * 目标对象
 */
public class UserDao implements IUserDao {
    public void save() {
        System.out.println("----已经保存数据!----");
    }
}

代理对象:UserDaoProxy.java

/**
 * 代理对象,静态代理
 */
public class UserDaoProxy implements IUserDao{
    //接收保存目标对象
    private IUserDao target;
    public UserDaoProxy(IUserDao target){
        this.target=target;
    }

    public void save() {
        System.out.println("开始事务...");
        target.save();//执行目标对象的方法
        System.out.println("提交事务...");
    }
}

测试类:App.java

/**
 * 测试类
 */
public class App {
    public static void main(String[] args) {
        //目标对象
        UserDao target = new UserDao();

        //代理对象,把目标对象传给代理对象,建立代理关系
        UserDaoProxy proxy = new UserDaoProxy(target);

        proxy.save();//执行的是代理的方法
    }
}

静态代理总结:
1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.
2.缺点:

  • 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.

如何解决静态代理中的缺点呢?答案是可以使用动态代理方式

1.2.动态代理

动态代理有以下特点:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理

JDK中生成代理对象的API
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

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

注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

  • ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
  • Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
  • InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

代码示例:
接口类IUserDao.java以及接口实现类,目标对象UserDao是一样的,没有做修改.在这个基础上,增加一个代理工厂类(ProxyFactory.java),将代理类写在这个地方,然后在测试类(需要使用到代理的代码)中先建立目标对象和代理对象的联系,然后代用代理对象的中同名方法

代理工厂类:ProxyFactory.java


/**
 * 创建动态代理对象
 * 动态代理不需要实现接口,但是需要指定接口类型
 */
public class ProxyFactory{

    //维护一个目标对象
    private Object target;
    public ProxyFactory(Object target){
        this.target=target;
    }

   //给目标对象生成代理对象
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("开始事务2");
                        //执行目标对象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("提交事务2");
                        return returnValue;
                    }
                }
        );
    }

}

测试类:App.java


/**
 * 测试类
 */
public class App {
    public static void main(String[] args) {
        // 目标对象
        IUserDao target = new UserDao();
        // 【原始的类型 class cn.itcast.b_dynamic.UserDao】
        System.out.println(target.getClass());

        // 给目标对象,创建代理对象
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        // class $Proxy0   内存中动态生成的代理对象
        System.out.println(proxy.getClass());

        // 执行方法   【代理对象】
        proxy.save();
    }
}

总结:
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理

1.3.Cglib代理

上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
  • Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

Cglib子类代理实现方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core-3.2.5.jar即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

代码示例:
目标对象类:UserDao.java

/**
 * 目标对象,没有实现任何接口
 */
public class UserDao {

    public void save() {
        System.out.println("----已经保存数据!----");
    }
}

Cglib代理工厂:ProxyFactory.java

/**
 * Cglib子类代理工厂
 * 对UserDao在内存中动态构建一个子类对象
 */
public class ProxyFactory implements MethodInterceptor{
    //维护目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //给目标对象创建一个代理对象
    public Object getProxyInstance(){
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();

    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开始事务...");

        //执行目标对象的方法
        Object returnValue = method.invoke(target, args);

        System.out.println("提交事务...");

        return returnValue;
    }
}

测试类:

/**
 * 测试类
 */
public class App {

    @Test
    public void test(){
        //目标对象
        UserDao target = new UserDao();

        //代理对象
        UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();

        //执行代理对象的方法
        proxy.save();
    }
}

在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理




另一位博主的介绍:

Java编程的目标是实现现实不能完成的,优化现实能够完成的,是一种虚拟技术。生活中的方方面面都可以虚拟到代码中。代理模式所讲的就是现实生活中的这么一个概念:中介

代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

代理模式包含如下角色:

ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。

RealSubject:真实主题角色,是实现抽象主题接口的类。

Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

实现动态代理的关键技术是反射。

静态代理

代理模式有几种,虚拟代理,计数代理,远程代理,动态代理。主要分为两类,静态代理和动态代理。静态代理比较简单,是由程序员编写的代理类,并在程序运行前就编译好的,而不是由程序动态产生代理类,这就是所谓的静态。

考虑这样的场景,管理员在网站上执行操作,在生成操作结果的同时需要记录操作日志,这是很常见的。此时就可以使用代理模式,代理模式可以通过聚合和继承两种方式实现:

  1. /**方式一:聚合式静态代理 
  2.  * @author Goser    (mailto:goskalrie@163.com) 
  3.  * @Since 2016年9月7日 
  4.  */  
  5. //1.抽象主题接口  
  6. public interface Manager {  
  7.     void doSomething();  
  8. }  
  9. //2.真实主题类  
  10. public class Admin implements Manager {  
  11.     public void doSomething() {  
  12.         System.out.println("Admin do something.");  
  13.     }  
  14. }  
  15. //3.以聚合方式实现的代理主题  
  16. public class AdminPoly implements Manager{  
  17.     private Admin admin;  
  18.      
  19.     public AdminPoly(Admin admin) {  
  20.         super();  
  21.         this.admin = admin;  
  22.     }  
  23.    
  24.     public void doSomething() {  
  25.         System.out.println("Log:admin操作开始");  
  26.         admin.doSomething();  
  27.         System.out.println("Log:admin操作结束");  
  28.     }  
  29. }  
  30. //4.测试代码  
  31.         Admin admin = new Admin();  
  32.         Manager m = new AdminPoly(admin);  
  33.         m.doSomething();  
  34. //方式二:继承式静态代理  
  35. //与上面的方式仅代理类和测试代码不同  
  36. //1.代理类  
  37. public class AdminProxy extends Admin {  
  38.     @Override  
  39.     public void doSomething() {  
  40.         System.out.println("Log:admin操作开始");  
  41.         super.doSomething();  
  42.         System.out.println("Log:admin操作开始");  
  43.     }  
  44. }  
  45. //2.测试代码  
  46.         AdminProxy proxy = new AdminProxy();  
  47.         proxy.doSomething();  
聚合实现方式中代理类聚合了被代理类,且代理类及被代理类都实现了同一个接口,可实现灵活多变。继承式的实现方式则不够灵活。

比如,在管理员操作的同时需要进行权限的处理,操作内容的日志记录,操作后数据的变化三个功能。三个功能的排列组合有6种,也就是说使用继承要编写6个继承了Admin的代理类,而使用聚合,仅需要针对权限的处理、日志记录和数据变化三个功能编写代理类,在业务逻辑中根据具体需求改变代码顺序即可。

动态代理

一般来说,对代理模式而言,一个主题类与一个代理类一一对应,这也是静态代理模式的特点。

但是,也存在这样的情况,有n各主题类,但是代理类中的“前处理、后处理”都是一样的,仅调用主题不同。也就是说,多个主题类对应一个代理类,共享“前处理,后处理”功能,动态调用所需主题,大大减小了程序规模,这就是动态代理模式的特点。

JDK动态代理

实现

  1. //1. 抽象主题  
  2. public interface Moveable {  
  3.     void move()  throws Exception;  
  4. }  
  5. //2. 真实主题  
  6. public class Car implements Moveable {  
  7.     public void move() throws Exception {  
  8.         Thread.sleep(new Random().nextInt(1000));  
  9.         System.out.println("汽车行驶中…");  
  10.     }  
  11. }  
  12. //3.事务处理器  
  13. public class TimeHandler implements InvocationHandler {  
  14.     private Object target;  
  15.      
  16.     public TimeHandler(Object target) {  
  17.         super();  
  18.         this.target = target;  
  19.     }  
  20.    
  21.     /** 
  22.      * 参数: 
  23.      *proxy 被代理的对象 
  24.      *method 被代理对象的方法 
  25.      *args 方法的参数 
  26.      * 返回: 
  27.      *Object 方法返回值 
  28.      */  
  29.     public Object invoke(Object proxy, Method method, Object[] args)  
  30.             throws Throwable {  
  31.         long startTime = System.currentTimeMillis();  
  32.         System.out.println("汽车开始行驶…");  
  33.         method.invoke(target, args);  
  34.         long stopTime = System.currentTimeMillis();  
  35.         System.out.println("汽车结束行驶…汽车行驶时间:" + (stopTime - startTime) + "毫秒!");  
  36.         return null;  
  37.     }  
  38.    
  39. }  
  40. //测试类  
  41. public class Test {  
  42.     public static void main(String[] args) throws Exception{  
  43.         Car car = new Car();  
  44.         InvocationHandler h = new TimeHandler(car);  
  45.         Class<?> cls = car.getClass();  
  46.         /** 
  47.          *loader 类加载器 
  48.          *interfaces 实现接口 
  49.          *h InvocationHandler 
  50.          */  
  51.         Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);  
  52.         m.move();  
  53.     }  
  54. }  
代码讲解:

测试代码中,Proxy.newProxyInstance()方法需要3个参数:类加载器(要进行代理的类)、被代理类实现的接口,事务处理器。所以先实例化Car,实例化InvocationHandler的子类TimeHandler,将各参数传入Proxy的静态方法newProxyInstance()即可获得Car的代理类,前面的静态代理,代理类是我们编写好的,而动态代理则不需要我们去编写代理类,是在程序中动态生成的。

JDK动态代理步骤

1.       创建一个实现InvocationHandler接口的类,它必须实现invoke()方法

2.       创建被代理的类及接口

3.       调用Proxy的静态方法,创建一个代理类

4.       通过代理调用方法

而为什么要进行如此操作,可以从Proxy和InvocationHandler的源码中找打答案。对源码不感兴趣的可以将下面的源码部分小节略过。

JDK动态代理原理与源码

newProxyInstance()方法的源码:

  1.     public static Object newProxyInstance(ClassLoader loader,  
  2.                                          Class<?>[] interfaces,  
  3.                                          InvocationHandler h)  
  4.         throws IllegalArgumentException{  
  5.         if (h == null) {  
  6.             throw new NullPointerException();  
  7.         }  
  8.         final Class<?>[] intfs = interfaces.clone();  
  9.         final SecurityManager sm = System.getSecurityManager();  
  10.         if (sm != null) {  
  11.             checkProxyAccess(Reflection.getCallerClass(), loader, intfs);  
  12.         }  
  13.         /*查找或生成指定的代理类*/  
  14.         Class<?> cl = getProxyClass0(loader, intfs);  
  15.         /*用指定的调用处理程序调用它的构造函数.*/  
  16.         try {  
  17.         //获得类的构造函数  
  18.             final Constructor<?> cons =cl.getConstructor(constructorParams);  
  19.             final InvocationHandler ih = h;  
  20.             if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {  
  21.               //当需要代理的类实现了一个非public的接口时,因为这样的接口需要特殊的权限,因此调用doPrivilege(native 修饰的方法)创建代理实例。  
  22.                 return AccessController.doPrivileged(newPrivilegedAction<Object>() {  
  23.                     public Object run() {  
  24.                         return newInstance(cons,ih);  
  25.                     }  
  26.                 });  
  27.             } else {  
  28.                 return newInstance(cons,ih);  
  29.             }  
  30.         } catch (NoSuchMethodException e) {  
  31.             throw new InternalError(e.toString());  
  32.         }  
  33. }  
可以看到,获得代理类的代码是

Class<?>cl = getProxyClass0(loader,intfs);

并由此获得代理类的构造函数,生成代理类的实例返回给该方法的调用者。

继续跟进getProxyClass0()方法:

  1. /** 生成代理类。调用该方法前必须使用checkproxyaccess方法执行权限检查。*/  
  2.     private static Class<?> getProxyClass0(ClassLoader loader,  
  3.                                           Class<?>... interfaces) {  
  4.     //检查实现的接口数,65535这个数字好特殊,端口数好像也是这个,这个数字是由虚拟机所决定的,2^16-1个  
  5.     if (interfaces.length > 65535) {  
  6.             throw new IllegalArgumentException("interface limit exceeded");  
  7.         }  
  8.         // 如果代理类已经通过实现给定接口的类加载器创建了,则返回缓存中的该类的副本;否则将通过ProxyClassFactory创建代理类  
  9.         return proxyClassCache.get(loader, interfaces);  
  10. }  
还是没有看到代理类是怎么生成的,只知道代理类是从 proxyClassCache中取得的,这个变量是与缓存相关的一个对象,查看该变量的声明与初始化:
  1. private static final WeakCache<ClassLoader, Class<?>[], Class<?>>  
  2.         proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());  
可以发现 proxyClassCache是个用来缓存代理类的类变量,大家知道类变量的特点是与类一一对应,在一个虚拟机中类只有一个,对应着在一个虚拟机中类变量也只有一个,且在此处,在Proxy类被加载的时候就赋值了。在赋值操作的参数中有ProxyClassFactory()这么一个构造函数,这个是动态代理中的关键:生成代理类的类文件字节码。继续跟进去,找到代理类的生成之处了:
  1. /** 根据给定的类加载器和接口数组生成代理类的工厂类*/  
  2. private static final class ProxyClassFactory  
  3.     implements BiFunction<ClassLoader,Class<?>[], Class<?>>  
  4. {  
  5.     // 所有代理类名称的前缀  
  6.     private static final String proxyClassNamePrefix = "$Proxy";  
  7.   
  8.     //用于生成唯一代理类名称的下一个序号  
  9.     private static final AtomicLong nextUniqueNumber = new AtomicLong();  
  10.   
  11.     @Override  
  12.     public Class<?> apply(ClassLoader loader,Class<?>[] interfaces) {  
  13.   
  14.         Map<Class<?>, Boolean>interfaceSet = new IdentityHashMap<>(interfaces.length);  
  15.         for (Class<?> intf : interfaces) {  
  16.             /* 验证类加载器将此接口的名称解析为实际对象的名称。*/  
  17.             Class<?> interfaceClass =null;  
  18.             try {  
  19.                 interfaceClass = Class.forName(intf.getName(),false, loader);  
  20.             } catch (ClassNotFoundException e) {  
  21.             }  
  22.             if (interfaceClass != intf) {  
  23.                 throw new IllegalArgumentException(  
  24.                     intf + " is not visible from classloader");  
  25.             }  
  26.             /* 验证类对象确实是一个接口。*/  
  27.             if (!interfaceClass.isInterface()) {  
  28.                 throw new IllegalArgumentException(  
  29.                    interfaceClass.getName() + " is not an interface");  
  30.             }  
  31.             /*确保接口唯一*/  
  32.             if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {  
  33.                 throw new IllegalArgumentException(  
  34.                     "repeated interface: " + interfaceClass.getName());  
  35.             }  
  36.         }  
  37.   
  38.         String proxyPkg = null;     // 代理类的包名  
  39.   
  40.         /*记录非公开代理接口的包,以便将代理类定义在同一个包中。确认所有非公共代理接口都在同一个包中。*/  
  41.         for (Class<?> intf : interfaces) {  
  42.             int flags = intf.getModifiers();  
  43.             if (!Modifier.isPublic(flags)) {  
  44.                 String name =intf.getName();  
  45.                 int n = name.lastIndexOf('.');  
  46.                 String pkg = ((n == -1) ? "" : name.substring(0, n + 1));  
  47.                 if (proxyPkg == null) {  
  48.                     proxyPkg = pkg;  
  49.                 } else if (!pkg.equals(proxyPkg)) {  
  50.                     throw new IllegalArgumentException(  
  51.                         "non-public interfaces fromdifferent packages");  
  52.                 }  
  53.             }  
  54.         }  
  55.   
  56.         if (proxyPkg == null) {  
  57.             // 如果没有非公开的代理接口,使用com.sun.proxy作为包名  
  58.             proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";  
  59.         }  
  60.   
  61.         /* 生成代理类名的序号*/  
  62.         long num = nextUniqueNumber.getAndIncrement();  
  63.         //生成全类名  
  64.         String proxyName = proxyPkg + proxyClassNamePrefix + num;  
  65.   
  66.         /*生成代理类字节码 */  
  67.         byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces);  
  68.         try {  
  69.             return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);  
  70.         } catch (ClassFormatError e) {  
  71.             throw new IllegalArgumentException(e.toString());  
  72.         }  
  73.     }  
ProxyClassFactory中,可以看到产生代理类的具体逻辑,大致上是,根据传递的被代理类及其实现的接口生成代理类的字节码加载到缓存中,但是加载到缓存中只是一个.java文件也不能用,所以底层还有编译等操作。到这里,可以大致的看清JDK中动态代理的面孔了,实现的步骤为:

1.      创建代理类的源码;

2.      对源码进行编译成字节码;

3.      将字节码加载到内存;

4.      实例化代理类对象并返回给调用者;

底层的代码我们看不到,但是我们可以查看其生成的字节码:

  1. //获得字节码的测试方法  
  2.     byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy1", Car.class.getInterfaces());   
  3.         FileOutputStream out = null;   
  4.         try {   
  5.             out = new FileOutputStream(System.getProperty("user.dir") + "\\$Proxy1.class");   
  6.             out.write(classFile);   
  7.             out.flush();   
  8.         } catch (Exception e) {   
  9.             e.printStackTrace();   
  10.         } finally {   
  11.             try {   
  12.                 out.close();   
  13.             } catch (IOException e) {   
  14.                 e.printStackTrace();   
  15.             }   
  16.         }  
  17.    
  18. //生成的字节码:  
  19. importcn.com.goser.proxy.imooc.staticproxy.Moveable;  
  20. importjava.lang.reflect.InvocationHandler;  
  21. importjava.lang.reflect.Method;  
  22. importjava.lang.reflect.Proxy;  
  23. import java.lang.reflect.UndeclaredThrowableException;  
  24.    
  25. public final class $Proxy1 extends Proxy  
  26.   implements Moveable  
  27. {  
  28.   private static Method m1;  
  29.   private static Method m3;  
  30.   private static Method m0;  
  31.   private static Method m2;  
  32.    
  33.   public $Proxy1(InvocationHandler paramInvocationHandler)  
  34.     throws  
  35.   {  
  36.     super(paramInvocationHandler);  
  37.   }  
  38.    
  39.   public final boolean equals(Object paramObject)  
  40.     throws  
  41.   {  
  42.     try  
  43.     {  
  44.       return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();  
  45.     }  
  46.     catch (RuntimeException localRuntimeException)  
  47.     {  
  48.       throw localRuntimeException;  
  49.     }  
  50.     catch (Throwable localThrowable)  
  51.     {  
  52.     }  
  53.     throw new UndeclaredThrowableException(localThrowable);  
  54.   }  
  55.    
  56.   public final void move()  
  57.     throws Exception  
  58.   {  
  59.     try  
  60.     {  
  61.       this.h.invoke(this, m3, null);  
  62.       return;  
  63.     }  
  64.     catch (Exception localException)  
  65.     {  
  66.       throw localException;  
  67.     }  
  68.     catch (Throwable localThrowable)  
  69.     {  
  70.     }  
  71.     throw new UndeclaredThrowableException(localThrowable);  
  72.   }  
  73.    
  74.   public final int hashCode()  
  75.     throws  
  76.   {  
  77.     try  
  78.     {  
  79.       return ((Integer)this.h.invoke(this, m0, null)).intValue();  
  80.     }  
  81.     catch (RuntimeException localRuntimeException)  
  82.     {  
  83.       throw localRuntimeException;  
  84.     }  
  85.     catch (Throwable localThrowable)  
  86.     {  
  87.     }  
  88.     throw new UndeclaredThrowableException(localThrowable);  
  89.   }  
  90.    
  91.   public final String toString()  
  92.     throws  
  93.   {  
  94.     try  
  95.     {  
  96.       return (String)this.h.invoke(this, m2, null);  
  97.     }  
  98.     catch (RuntimeException localRuntimeException)  
  99.     {  
  100.       throw localRuntimeException;  
  101.     }  
  102.     catch (Throwable localThrowable)  
  103.     {  
  104.     }  
  105.     throw new UndeclaredThrowableException(localThrowable);  
  106.   }  
  107.    
  108.   static  
  109.   {  
  110.     try  
  111.     {  
  112.       m1 = Class.forName("java.lang.Object").getMethod("equals"new Class[] { Class.forName("java.lang.Object") });  
  113.       m3 = Class.forName("cn.com.goser.proxy.imooc.staticproxy.Moveable").getMethod("move"new Class[0]);  
  114.       m0 = Class.forName("java.lang.Object").getMethod("hashCode"new Class[0]);  
  115.       m2 = Class.forName("java.lang.Object").getMethod("toString"new Class[0]);  
  116.       return;  
  117.     }  
  118.     catch (NoSuchMethodExceptionlocalNoSuchMethodException)  
  119.     {  
  120.       throw new NoSuchMethodError(localNoSuchMethodException.getMessage());  
  121.     }  
  122.     catch (ClassNotFoundExceptionlocalClassNotFoundException)  
  123.     {  
  124.     }  
  125.     throw new NoClassDefFoundError(localClassNotFoundException.getMessage());  
  126.   }  
  127. }  
生成的字节码比较长,但是在字节码中最关键的信息是代理类的声明:

public final class $Proxy1 extends Proxy

可以看到生成的代理类是继承了Proxy类的,这就是说明了为什么使用JDK动态代理不能实现继承式动态代理,原因是Java不允许多继承,而生成的代理类本身就已经继承了Proxy类。

至此,JDK的动态代理的使用及底层原理分析完毕,揭下动态代理的神秘面纱,果然是枚美女。

至于最底层的native方法是怎么动态生成代理类的字节码我们也可以简单的模拟一下,先分析下模拟的步骤:首先要生成一段代理类的源码,然后将源码编译后生成代理类的实例返回给调用者。依据此步骤开始编写我们的模拟代码:

  1. /** 
  2.  * JDK java.lang.reflect.Proxy的模拟 
  3.  * @author Goser    (mailto:goskalrie@163.com) 
  4.  * @Since 2016年9月7日 
  5.  */  
  6. public class Proxy {  
  7.     private static final String  RT = "\r\n";  
  8.     public static Object newProxyInstance() throws Exception{  
  9.         //声明一段源码  
  10.         String sourceCode =  
  11.         "packagecn.com.goser.proxy.jdk.simulate;"+ RT +  
  12.         "importcn.com.goser.proxy.imooc.staticproxy.Admin;" + RT +  
  13.         "importcn.com.goser.proxy.imooc.staticproxy.Manager;" + RT +  
  14.         "//以聚合方式实现的代理主题" + RT +  
  15.         "public class $Proxy0 implementsManager{" + RT +  
  16.         "   privateAdmin admin;" + RT +  
  17.         "   public$Proxy0(Admin admin) {" + RT +  
  18.         "       super();" + RT +  
  19.         "       this.admin= admin;" + RT +  
  20.         "   }" + RT +  
  21.         "   publicvoid doSomething() {" + RT +  
  22.         "       System.out.println(\"Log:admin操作开始\");" + RT +  
  23.         "       admin.doSomething();" + RT +  
  24.         "       System.out.println(\"Log:admin操作结束\");" + RT +  
  25.         "   }" + RT +  
  26.         "}";  
  27.         String filename = System.getProperty("user.dir") + "/src/main/java/cn/com/goser/proxy/jdk/simulate/$Proxy0.java";  
  28.         File file = new File(filename);  
  29.         //使用org.apache.commons.io.FileUtils.writeStringToFile()将源码写入磁盘  
  30.         //编写到处,可以运行一下程序,可以在当前目录中看到生成的.java文件  
  31.         FileUtils.writeStringToFile(file,sourceCode);  
  32.         //获得当前系统中的编译器  
  33.         JavaCompiler complier = ToolProvider.getSystemJavaCompiler();  
  34.         //获得文件管理者  
  35.         StandardJavaFileManager fileMgr =complier.getStandardFileManager(nullnullnull);  
  36.         Iterable its =fileMgr.getJavaFileObjects(filename);  
  37.         //编译任务  
  38.         CompilationTask task = complier.getTask(null, fileMgr, nullnullnull, its);  
  39.         //开始编译,执行完可在当前目录下看到.class文件  
  40.         task.call();  
  41.         fileMgr.close();  
  42.         //load到内存  
  43.         ClassLoader loader = ClassLoader.getSystemClassLoader();  
  44.         Class cls = loader.loadClass("cn.com.goser.proxy.jdk.simulate.$Proxy0");  
  45.         //生成代理类对象  
  46.         Constructor ct = cls.getConstructor(Admin.class);  
  47.         return ct.newInstance(new Admin());  
  48.     }  
  49. }  
  50. class test{  
  51.     public static void main(String[] args) throws Exception {  
  52.         Manager m = (Manager)Proxy.newProxyInstance();  
  53.         m.doSomething();  
  54.     }  
  55. }  
运行测试代码,结果和手工编写的结果一致,完成了JDK中动态代理的实现模拟。

cglib动态代理

前面分析到,因为Java只允许单继承,而JDK生成的代理类本身就继承了Proxy类,因此,使用JDK实现的动态代理不能完成继承式的动态代理,但是我们可以使用cglib来实现继承式的动态代理。

大名鼎鼎的spring中就含有cglib动态代理,在此也以Spring中自带的cglib完成动态代理的实现:

  1. //1.具体主题  
  2. public class Train{  
  3.     public void move(){  
  4.         System.out.println("火车行驶中…");  
  5.     }  
  6. }  
  7. //2.生成代理  
  8. public class CGLibProxy implements MethodInterceptor {  
  9.     private Enhancer enhancer = new Enhancer();  
  10.     public Object getProxy(Class<?> clazz){  
  11.         enhancer.setSuperclass(clazz);  
  12.         enhancer.setCallback(this);  
  13.         return enhancer.create();  
  14.     }  
  15.     /** 
  16.      * 拦截所有目标类方法的调用 
  17.      * 参数: 
  18.      * obj目标实例对象 
  19.      *method 目标方法的反射对象 
  20.      * args方法的参数 
  21.      * proxy代理类的实例 
  22.      */  
  23.     public Object intercept(Object obj, Method method, Object[] args,  
  24.             MethodProxy proxy) throws Throwable {  
  25.         //代理类调用父类的方法  
  26.         System.out.println("日志开始");  
  27.         proxy.invokeSuper(obj, args);  
  28.         System.out.println("日志结束");  
  29.         return null;  
  30.     }  
  31. }  
  32. //3.测试  
  33. public class Test {  
  34.     public static void main(String[] args) {  
  35.         CGLibProxy proxy = new CGLibProxy();  
  36.         Train t = (Train) proxy.getProxy(Train.class);  
  37.         t.move();  
  38.     }  
  39. }  
小结

动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样对每一个方法或方法组合进行处理。Proxy 很美很强大,但是仅支持 interface 代理。Java 的单继承机制注定了这些动态代理类们无法实现对 class 的动态代理。好在有cglib为Proxy提供了弥补。class与interface的区别本来就模糊,在java8中更是增加了一些新特性,使得interface越来越接近class,当有一日,java突破了单继承的限制,动态代理将会更加强大。


转载自:http://www.cnblogs.com/cenyu/p/6289209.html


http://blog.csdn.net/goskalrie/article/details/52458773


http://www.cnblogs.com/whoislcj/p/5693980.html

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值