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

    简介

    代理模式即Proxy Pattern,23种java常用设计模式之一。其定义为:对其他对象提供一种代理以控制对这个对象的访问。


    UML类图


    静态代理

    目标接口

    public interface Subject {
    
        public void execute();
        
    }

     目标实现类

    public class RealSubject implements Subject {
    
        private String a;
    
        public RealSubject(String a) {
            this.a = a;
        }
    
        @Override
        public void execute() {
            System.out.println("do biz, " + a);
        }
    
    }

     代理类

    public class StaticProxy implements Subject {
    
        private Subject subject;
    
        public StaticProxy(Subject subject) {
            this.subject = subject;
        }
    
        @Override
        public void execute() {
            System.out.println("before executing");
            subject.execute();
            System.out.println("after executing");
        }
    
    }

     测试类

    public class StaticProxyTest {
    
        public static void main(String[] args) {
            Subject subject = new RealSubject("hello");
            StaticProxy proxy = new StaticProxy(subject);
            proxy.execute();
        }
        
    }

    运行结果输出

    before executing
    do biz, hello
    after executing

    通过这种方法,利用代理类在目标类执行核心方法前后添加了相应辅助逻辑。

    但值得注意的是,当在代码阶段规定这种代理关系,StaticProxy类通过编译器编译成.class文件,当系统运行时,此.class已经存在了。这种静态的代理模式固然在访问无法访问的资源,增强现有的接口业务功能方面有很大的优点,但是大量使用这种静态代理,会使我们系统内类的规模增大,且不易维护;并且由于StaticProxy和RealSubject的功能本质上是相同的,StaticProxy只是起到了中介的作用,这种代理在系统中的存在,会导致系统结构比较臃肿和松散。


    JDK动态代理

    JDK从1.3版本起自带的动态代理机制由java.lang.reflect.Proxy实现,使用时必须创建一个实现了java.lang.reflect.InvocationHandler接口的动态代理类,该接口只有一个方法:

    Object invoke(Object proxy,
                  Method method,
                  Object[] args)
                  throws Throwable

     参数:

     proxy - 在其上调用方法的代理实例method - 对应于在代理实例上调用的接口方法的 Method 实例。 

     Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。

     args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。

    动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法。如下图所示: 

    再来看Proxy如何创建出一个代理对象,常用的方法为:

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

    参数:
    loader - 定义代理类的类加载器
    interfaces - 代理类要实现的接口列表
    h - 指派方法调用的调用处理程序该方法返回的对象是实现了参数interfaces中指明的接口的子类对象,因此强制要求目标类必须是接口。

    接下来通过示例代码演示动态代理的使用方式

    目标接口
    public interface Subject {
    
        public void doBiz();
        
    }

     目标实现类

    public class RealSubject implements Subject {
    
        private String a;
    
        public RealSubject(String a) {
            this.a = a;
        }
    
        @Override
        public void doBiz() {
            System.out.println("do biz, " + a);
        }
    
    }

     动态代理类

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class DynamicProxy implements InvocationHandler {
    
        private Object target;
    
        public DynamicProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("before executing");
            Object result = method.invoke(target, args);
            System.out.println("after executing");
            return result;
        }
    
    }

     测试类

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    public class DynamicProxyTest {
    
        public static void main(String[] args) {
            Subject subject = new RealSubject("hello");
            InvocationHandler handler = new DynamicProxy(subject);
            Subject proxy = (Subject) Proxy.newProxyInstance(
                    Subject.class.getClassLoader(), new Class[] { Subject.class },
                    handler);  // 由于第二个参数传入了Subject.class,因此返回的代理对象实现了该接口,可以转换为Subject对象
            System.out.println("proxy = " + proxy.getClass().getName());
            if (proxy instanceof Proxy) {
                System.out.println("proxy implements java.lang.reflect.Proxy");
            }
            if (proxy instanceof Subject) {
                System.out.println("proxy implements Subject");
            }
            proxy.doBiz();
        }
    
    }

     运行结果输出

    proxy = com.sun.proxy.$Proxy0
    proxy implements java.lang.reflect.Proxy
    proxy implements Subject
    before executing
    do biz, hello
    after executing

    第一行输出为代理对象的名称,第二、三行表明代理对象实现了java.lang.reflect.Proxy接口和Subject接口,后面的打印结果与静态代理时一致。

     所以JDK动态代理有个显著的特点(限制):某个类必须有实现的接口,而生成的代理类也只能代理某个类接口定义的方法。下面介绍的Cglib动态代理就不存在此类限制。


    Cglib动态代理

     Cglib是一个第三方类库,用于创建动态代理,包括Spring AOP在内多个框架都内部集成了Cglib,而它底层也利用了ASM操纵字节码。Cglib类库的框架图如下:

    使用Cglib创建动态代理与JDK原生的动态代理不同之处在于它并不强制要求目标类为接口,因此可以对于普通Java类进行代理。

    使用时需要在pom.xml配置两个依赖:

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
    </dependency>
    <dependency>
        <groupId>asm</groupId>
        <artifactId>asm</artifactId>
        <version>3.3.1</version>
    </dependency>

    动态代理类需要实现net.sf.cglib.proxy.MethodInterceptor接口(等价于JDK动态代理中的InvocationHandler接口),其中只有一个方法:

    public Object intercept(Object object, Method method, Object[] args,
    MethodProxy proxy) throws Throwable

    参数:

     object - 被代理的对象。

     method - 被代理对象的方法。

     args - 包含传入代理实例上方法调用的参数值的对象数组。

     proxy - 代理对象。

    接下来通过示例代码演示Cglib动态代理的使用方式

    目标类

    public class RealSubject {
    
        private String a;
    
        public RealSubject(String a) {
            this.a = a;
        }
    
        public void doBiz() {
            System.out.println("do biz, " + a);
        }
    
    }

     代理类

    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CglibProxy implements MethodInterceptor {
    
        @Override
        public Object intercept(Object object, Method method, Object[] args,
                MethodProxy proxy) throws Throwable {
            System.out.println("before executing");
            Object result = proxy.invokeSuper(object, args);
            System.out.println("after executing");
            return result;
        }
    
    }

     目标类工厂

    import net.sf.cglib.proxy.Enhancer;
    
    public class RealSubjectFactory {
    
        public static RealSubject getInstance(CglibProxy proxy) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(RealSubject.class);
            enhancer.setCallback(proxy);
            RealSubject subject = (RealSubject) enhancer.create(
                    new Class[] { String.class }, new Object[] { "hello" });   // 返回值是RealSubject的子类对象;如果目标类的构造方法不含参数,则这儿无需传入create()方法的参数 
    return subject; } }

     测试类

    public class CglibProxyTest {
    
        public static void main(String[] args) {
            CglibProxy proxy = new CglibProxy();
            RealSubject subject = RealSubjectFactory.getInstance(proxy);
            subject.doBiz();
        }
    
    }

     运行结果输出

    before executing
    do biz, hello
    after executing

    输出结果与静态代理一致。


     Javassist动态代理

    此外,利用javassist字节码生成框架也可以以类似的方式实现动态代理,使用时需要在pom.xml配置依赖:

    <dependency>
        <groupId>org.javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.18.1-GA</version>
    </dependency>

    接下来直接展示示例代码。

    目标接口

    public interface Subject {
    
        public void doBiz();
        
    }

    目标实现类

    public class RealSubject implements Subject {
    
        private String a;
    
        public RealSubject(String a) {
            this.a = a;
        }
    
        @Override
        public void doBiz() {
            System.out.println("do biz, " + a);
        }
    
    }

    代理类

    import java.lang.reflect.Method;
    
    import javassist.util.proxy.MethodHandler;
    
    public class JavassistProxy implements MethodHandler {
    
        private Object target;
    
        public JavassistProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Method method2,
                Object[] args) throws Throwable {
            System.out.println("before executing");
            Object result = method.invoke(target, args);
            System.out.println("after executing");
            return result;
        }
        
    }

    测试类

    import javassist.util.proxy.ProxyFactory;
    import javassist.util.proxy.ProxyObject;
    
    public class JavassistProxyTest {
    
        public static void main(String[] args) throws InstantiationException,
                IllegalAccessException {
            Subject subject = new RealSubject("hello");
            ProxyFactory proxyFactory = new ProxyFactory();
            proxyFactory.setInterfaces(new Class[] { Subject.class });
            Subject proxy = (Subject) proxyFactory.createClass().newInstance();
            ((ProxyObject) proxy).setHandler(new JavassistProxy(subject));
            proxy.doBiz();
        }
    
    }

    运行结果输出

    before executing
    do biz, hello
    after executing

    输出结果与静态代理一致。


    REFERENCES

    [1] http://jnb.ociweb.com/jnb/jnbNov2005.html

    [2] http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html

    [3] http://www.cnblogs.com/xiaoluo501395377/p/3383130.html

    [4] http://blog.csdn.net/dreamrealised/article/details/12885739

    [5] http://blog.csdn.net/jackiehff/article/details/8621517

    [6] https://dzone.com/articles/cglib-missing-manual

    [7] http://www.360doc.com/content/14/0801/14/1073512_398598312.shtml

    [8] http://javatar.iteye.com/blog/814426


    为尊重原创成果,如需转载烦请注明本文出处:http://www.cnblogs.com/fernandolee24/p/6182924.html ,特此感谢

  • 相关阅读:
    android 近百个源码项目【转】
    503 Service Temporarily Unavailable
    linux 复制文件夹内所有文件到另一个文件夹
    国甲魔方(1)
    大雁展翅,轮回,国甲,国丙,速龙,傲龙,御龙这些魔方有什么区别
    77教师一定要看的15部电影【附下载地址】
    教师一定要看的15部电影
    孩子
    为什么我的 app:actionViewClass="android.widget.SearchView"和app:showAsAction="ifRoom|collapseActionView"才有
    大数据征信 是伪命题?还是金融行业的救世主?
  • 原文地址:https://www.cnblogs.com/fernandolee24/p/6182924.html
Copyright © 2011-2022 走看看