首页 > 编程语言 > 详细

Java动态代理

时间:2017-03-23 01:39:51      阅读:307      评论:0      收藏:0      [点我收藏+]

代理模式
  代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 


按照代理的创建时期,代理类可以分为两种:
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 
动态代理:在程序运行时,运用反射机制动态创建而成。 

 

首先看一下静态代理: 

技术分享

            静态代理UML图

1、Count.java 

 1  /** 
 2  * 定义一个账户接口 
 3  *  
 4  * @author Administrator 
 5  *  
 6  */  
 7 public interface Count {  
 8     // 查看账户方法  
 9     public void queryCount();  
10   
11     // 修改账户方法  
12     public void updateCount();  
13   
14 } 

2、CountImpl.java 

/** 
 * 委托类(包含业务逻辑) 
 *  
 * @author Administrator 
 *  
 */  
public class CountImpl implements Count {  
  
    @Override  
    public void queryCount() {  
        System.out.println("查看账户方法...");  
  
    }  
  
    @Override  
    public void updateCount() {  
        System.out.println("修改账户方法...");  
  
    }  
  
}

3、CountProxy.java

/** 
 * 这是一个代理类(增强CountImpl实现类) 
 *  
 * @author Administrator 
 *  
 */  
public class CountProxy implements Count {  
    private CountImpl countImpl;  
  
    /** 
     * 覆盖默认构造器 
     *  
     * @param countImpl 
     */  
    public CountProxy(CountImpl countImpl) {  
        this.countImpl = countImpl;  
    }  
  
    @Override  
    public void queryCount() {  
        System.out.println("事务处理之前");  
        // 调用委托类的方法;  
        countImpl.queryCount();  
        System.out.println("事务处理之后");  
    }  
  
    @Override  
    public void updateCount() {  
        System.out.println("事务处理之前");  
        // 调用委托类的方法;  
        countImpl.updateCount();  
        System.out.println("事务处理之后");  
  
    }  
}

4、TestCount.java

/** 
 *测试Count类 
 *  
 * @author Administrator 
 *  
 */  
public class TestCount {  
    public static void main(String[] args) {  
        CountImpl countImpl = new CountImpl();  
        CountProxy countProxy = new CountProxy(countImpl);  
        countProxy.updateCount();  
        countProxy.queryCount();  
    }  
}  

  观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。 

 

再来看一下动态代理: 


  技术分享

            动态代理UML图

java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

  1. Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method, Object[] args)。在实际使用时,第一个参数obj一般是指代理 类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。 这个抽 象方法在代理类中动态实现。
  2. Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject。
  3. Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。
  4. Static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个 代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
  5. Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用 (可使用被代理类的在Subject接口中声明过的方法)。

在使用动态代理类时,我们必须实现InvocationHandler, 以上面的代码为例:

1、Subject.java

public interface Subject {
    abstract  public  void request();
}

2、RealSubject.java

public  class RealSubject implements Subject {
    public RealSubject() {}

    public  void request() {
        System.out.println( " From real subject. " );
    }

}

3、DynamicSubject.java

public  class DynamicSubject implements InvocationHandler {
    private Object sub;
    public DynamicSubject() {}

    public DynamicSubject(Object obj) {
        sub = obj;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println( " before calling "  + method);
        method.invoke(sub,args);

        System.out.println( " after calling "  + method);
        return  null ;
    }
}

4、Client.java

public class Client {
    static public void main(String[] args) throws Throwable {
        Subject rs = new RealSubject(); // 在这里指定被代理类
        InvocationHandler ds = new DynamicSubject(rs);
        Class cls = rs.getClass();

        // 以下是一次性生成代理
        Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), ds);
        Class c = Proxy.getProxyClass(cls.getClassLoader(), cls.getInterfaces());
        subject.request();
        System.out.print(c);

        //下面是打印动态生成的class文件
        String path = "/Users/zhangzhihao/$Proxy0.class";
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",RealSubject.class.getInterfaces());
        FileOutputStream out = null;

        try {
            out = new FileOutputStream(path);
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5、$Proxy0.java

public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m3;
    private static Method m0;
    private static Method m2;

    public $Proxy0(InvocationHandler var1) {
        super(var1);
    }

    public final boolean equals(Object var1) {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void request() {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("proxy.JDKDynamicProxy.Subject").getMethod("request", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

 对于JDK 的Proxy,有以下几点: 

    1)Interface:对于JDK proxy,业务类是需要一个Interface的,这也是一个缺陷

    2)Proxy,Proxy 类是动态产生的,这个类在调用Proxy.newProxyInstance(targetCls.getClassLoader, targetCls.getInterface,InvocationHander)之后,会产生一个Proxy类的实例。实际上这个Proxy类也是存在的,不仅仅是类的实例。这个Proxy类可以保存到硬盘上。

    3) Method:对于业务委托类的每个方法,现在Proxy类里面都不用静态显示出来

    4) InvocationHandler: 这个类在业务委托类执行时,会先调用invoke方法。invoke方法再执行相应的代理操作,可以实现对业务方法的再包装

 

顺便再加一下cglib的动态代理原理:

  JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 

1、BookFacade

public interface BookFacade {  
    public void addBook();  
}

2、BookFacadeImpl.java

public class BookFacadeImpl {
    public void addBook() {
        System.out.println("Add a book!");
    }
}

3、BookFacadeCglib.java

public class BookFacadeCglib implements MethodInterceptor {

    private Object target;

    /**
     * 生成实例
     * @param target
     * @return
     */
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Before add a book!");
        methodProxy.invokeSuper(o, objects);
        System.out.println("After add a book!");
        return null;
    }
}

4、Client.java

public class Client {
    static public void main(String[] args) throws Throwable {
        BookFacadeCglib cglib=new BookFacadeCglib();
        BookFacadeImpl bookCglib=(BookFacadeImpl)cglib.getInstance(new BookFacadeImpl());
        bookCglib.addBook();
    }
}

代理对象的生成过程由Enhancer类实现,大概步骤如下:
1、生成代理类Class的二进制字节码;
2、通过Class.forName加载二进制字节码,生成Class对象;
3、通过反射机制获取实例构造,并初始化代理类对象。

 

参考文章:

http://blog.csdn.net/hintcnuie/article/details/10954631

http://blog.csdn.net/yakoo5/article/details/9099197

http://blog.csdn.net/zhangerqing/article/details/42504281/

http://www.cnblogs.com/chinajava/p/5880870.html

http://www.cnblogs.com/chinajava/p/5880887.html

Java动态代理

原文:http://www.cnblogs.com/FishAndWater/p/6602496.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!