zoukankan      html  css  js  c++  java
  • 代理模式

    定义:为另一个对象提供一个替身或占位符以控制这个对象的访问。

    转载:http://haolloyin.blog.51cto.com/1177454/333257/

    Java设计模式(七) Spring AOP JDK动态代理 vs. Cglib

    1、JDK自带的动态代理(代理必须实现接口)

    理解

    动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

    我们知道,所谓代理,就是需要代理类和被代理类有相同的对外接口或者说成服务,所以代理类一般都必须实现了所有被代理类已实现的接口,因为接口就是制定了一系列对外服务的标准。
     
    正因为动态代理有这样灵活的特性,所以我们在设计动态代理类(DynamicProxy)时不用显式地让它实现与真实主题类(RealSubject)相同的接口(interface),而是把这种实现推迟到运行时。
     
    为了能让DynamicProxy类能够在运行时才去实现RealSubject类已实现的一系列接口并执行接口中相关的方法操作,需要让DynamicProxy类实现JDK自带的java.lang.reflect.InvocationHandler接口,该接口中的invoke()方法能够让DynamicProxy实例在运行时调用被代理类的“对外服务”,即调用被代理类需要对外实现的所有接口中的方法,也就是完成对真实方法的调用,Java帮助文档中称这些真实方法为处理程序。
     
    按照上面所述,我们肯定必须先把被代理类RealSubject已实现的所有interface都加载到JVM中,不然JVM怎么能够找到这些方法呢?明白了这个道理,那么我们就可以创建一个被代理类的实例,获得该实例的类加载器ClassLoader。
     
    所谓的类加载器ClassLoader,就是具有某个类的类定义,即类的内部相关结构(包括继承树、方法区等等)。
     
    更重要的是,动态代理模式可以使得我们在不改变原来已有的代码结构的情况下,对原来的“真实方法”进行扩展、增强其功能,并且可以达到控制被代理对象的行为的目的。请详看下面代码中的DynamicProxy类,其中必须实现的invoke()方法在调用被代理类的真实方法的前后都可进行一定的特殊操作。这是动态代理最明显的优点。
     
    虽然都是根据自己看了书之后的理解说了这么多,不知道能不能让人明白,这里先给出动态代理的类图吧,如下:

    具体代码实现如下:
    1. import java.lang.reflect.InvocationHandler;  
    2. import java.lang.reflect.Method;  
    3. import java.lang.reflect.Proxy;  
    4.  
    5. //抽象主题类,这里不能用abstract抽象类,一定要是interface  
    6. interface AbstractSubject {  
    7.     public abstract void request();  
    8. }  
    9.  
    10. // 真实主题类,即被代理类  
    11. class RealSubject implements AbstractSubject {  
    12.     public void request() {  
    13.         System.out.println("RealSubject's request() ...");  
    14.     }  
    15. }  
    16.  
    17. // 动态代理类,实现InvocationHandler接口  
    18. class DynamicProxy implements InvocationHandler {  
    19.  
    20.     // 被代理类的实例  
    21.     Object obj = null;  
    22.  
    23.     // 将被代理者的实例传进动态代理类的构造函数中  
    24.     public DynamicProxy(Object obj) {  
    25.         this.obj = obj;  
    26.     }  
    27.  
    28.     /**  
    29.      * 覆盖InvocationHandler接口中的invoke()方法  
    30.      *   
    31.      * 更重要的是,动态代理模式可以使得我们在不改变原来已有的代码结构  
    32.      * 的情况下,对原来的“真实方法”进行扩展、增强其功能,并且可以达到  
    33.      * 控制被代理对象的行为,下面的before、after就是我们可以进行特殊  
    34.      * 代码切入的扩展点了。  
    35.      */ 
    36.     public Object invoke(Object proxy, Method method, Object[] args)  
    37.             throws Throwable {  
    38.         /*  
    39.          * before :doSomething();  
    40.          */ 
    41.         Object result = method.invoke(this.obj, args);  
    42.           
    43.         /*  
    44.          * after : doSomething();  
    45.          */ 
    46.         return result;  
    47.     }  
    48. }  
    49.  
    50. // 测试类  
    51. public class Client {  
    52.     public static void main(String[] args) {  
    53.  
    54.         // 被代理类的实例  
    55.         AbstractSubject realSubject = new RealSubject();  
    56.  
    57.         // 获得被代理类的类加载器,使得JVM能够加载并找到被代理类的内部结构,以及已实现的interface  
    58.         ClassLoader loader = realSubject.getClass().getClassLoader();  
    59.  
    60.         // 获得被代理类已实现的所有接口interface,使得动态代理类的实例  
    61.         Class<?>[] interfaces = realSubject.getClass().getInterfaces();  
    62.  
    63.         // 用被代理类的实例创建动态代理类的实例,用于真正调用处理程序  
    64.         InvocationHandler handler = new DynamicProxy(realSubject);  
    65.  
    66.         /*  
    67.          * loader : 被代理类的类加载器  
    68.          * interfaces :被代理类已实现的所有接口,而这些是动态代理类要实现的接口列表  
    69.          * handler : 用被代理类的实例创建动态代理类的实例,用于真正调用处理程序  
    70.          *   
    71.          * return :返回实现了被代理类所实现的所有接口的Object对象,即动态代理,需要强制转型  
    72.          */ 
    73.         //获得代理的实例  
    74.         AbstractSubject proxy = (AbstractSubject) Proxy.newProxyInstance(  
    75.                 loader, interfaces, handler);  
    76.  
    77.         proxy.request();  
    78.         //打印出该代理实例的名称  
    79.         System.out.println(proxy.getClass().getName());  
    80.     }  
     
     
    测试结果:
    RealSubject's request() ...

    DesignPattern.proxy.dynamicProxy.$Proxy0 

    为什么JDK动态代理只能代理实现了接口的类

    可以打印出代理类的字节码,从该类的声明中可以看到,继承了Proxy类,并实现了目标接口。验证了上文中的论点——所有生成的动态代理类都是Proxy类的子类。同时也解释了为什么JDK动态代理只能代理实现了接口的类——Java不支持多继承,代理类已经继承了Proxy类,无法再继承其它类。

    2.cglib(是直接修改字节码,可以用继承实现代理)

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

    public interface BookFacade {  
        public void addBook();  
    }  

    /** 
     * 这个是没有实现接口的实现类 
     *  
     * @author student 
     *  
     */  
    public class BookFacadeImpl1 {  
        public void addBook() {  
            System.out.println("增加图书的普通方法...");  
        }  
    }  

    import java.lang.reflect.Method;  

    import net.sf.cglib.proxy.Enhancer;  
    import net.sf.cglib.proxy.MethodInterceptor;  
    import net.sf.cglib.proxy.MethodProxy;  

    /** 
     * 使用cglib动态代理 
     *  
     * @author student 
     *  
     */  
    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(this.target.getClass());  
            // 回调方法  
            enhancer.setCallback(this);  
            // 创建代理对象  
            return enhancer.create();  
        }  

        @Override  
        // 回调方法  
        public Object intercept(Object obj, Method method, Object[] args,  
                MethodProxy proxy) throws Throwable {  
            System.out.println("事物开始");  
            proxy.invokeSuper(obj, args);  
            System.out.println("事物结束");  
            return null;  
        }  
    }  

    import net.battier.dao.impl.BookFacadeImpl1;  
    import net.battier.proxy.BookFacadeCglib;  

    public class TestCglib {  

        public static void main(String[] args) {  
            BookFacadeCglib cglib=new BookFacadeCglib();  
            BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());  
            bookCglib.addBook();  
        }  
    }  

  • 相关阅读:
    java多线程练习题 类
    java练习题在一个文件里面输入内容在另一个文件里面可以查看
    java练习题输入流姓名学号信息
    java 异常处理2
    java 处理异常练习题
    java get银行练习题
    java 练习题 求梯形的面积和周长
    java get正确写类的练习题 猫
    GUID 全局唯一标识符
    oracle 建表 练习2
  • 原文地址:https://www.cnblogs.com/fanguangdexiaoyuer/p/5917213.html
Copyright © 2011-2022 走看看