zoukankan      html  css  js  c++  java
  • javase基础回顾(三) 动态代理

    动态代理是大型框架中经常用到的经典的技术之一,博主在理解spring的控制反转(依赖注入)的思想时回头着重复习了一下java的动态代理。

    在说动态代理之前我们先简单说一说代理是用来干什么的,用于什么样的业务场景然后在引入静态代理和动态代理。

    代理模式一般涉及到的角色有
    –抽象角色:声明真实对象和代理对象的共同接口
    –代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
    –真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

    在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

    代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。

    一、静态代理

    下面来看具体代码

    抽象角色:

    public interface Subject {
        
        public void doSomething();
    }

    真实角色:

    public class RealSubject implements Subject {
        @Override
        public void doSomething() {
            
            System.out.println("do something!");
        }
    }

    代理角色:

    public class PoxySubject implements Subject{
        private RealSubject subject;
        
        @Override
        public void doSomething() {
            this.doPreThing();
            
            if (null == subject) {
                
                subject = new RealSubject();
            }
            
            subject.doSomething();
            this.dopostThing();
        }
        
        public void doPreThing() {
            System.out.println("pre things");
        }
        
        public void dopostThing() {
            System.out.println("post things");
        }
    }

    测试类(main方法)

    public class Client {
        /**
         * 静态代理模式 作用是为其他对象提供一种代理以控制对这个对象的访问。
         * 在本例中的作用是我们不仅可以完成doSomething()方法,而且还可以控制在这个方法前后我们再另外做一些事情。
         * @param args
         */
        public static void main(String[] args) {
            Subject subject = new PoxySubject();
            subject.doSomething();
        }
    }

    运行main方法:

    pre things
    do something!
    post things

    我们发现通过这种方式,我们隐藏了真实角色而是通过代理角色完成了真实角色所做的事情。并且我们可以在代理角色中做一些手脚实现一些我们自己想实现的方法。这就是静态代理的作用。但是很快我们发现其中的局限性。

    如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这就是静态代理的局限性,这个问题可以通过Java的动态代理类来解决。

    二、动态代理

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

    (1)Interface InvocationHandler:该接口中仅定义了一个方法
    -public object invoke(Object obj,Method method, Object[] args)
    在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。


    (2)Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容

    -protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。
    -static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
    -static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)

    接下来我们用动态代理实现之前的例子

    抽象角色:

    public interface Subject {
        public void doSomething();
    }

    真实角色:

    public class RealSubject implements Subject {
    
        public void doSomething() {
            System.out.println("from real subject");
        }
    }

    实现代理角色的类:

    public class DynamicSubject implements InvocationHandler {
        
        private Object sub;
        
        public DynamicSubject(Object obj) {
            this.sub = obj;
        }
     
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("调用之前" + method);
            
            method.invoke(sub, args);
            
            System.out.println("调用之后" + method);
            
            return null;
        }    
    }

    测试类(main方法):

    public class Client {
        
        public static void main(String[] args) {
            RealSubject sub = new RealSubject();
            InvocationHandler hanlder = new DynamicSubject(sub);
            
            //返回代理类的一个实例
            Subject subject =(Subject)Proxy.newProxyInstance(hanlder.getClass().getClassLoader(), sub.getClass().getInterfaces(), hanlder);
            subject.doSomething();
        }
    }

    运行结果:

    调用之前public abstract void com.wenge.dynamicporxy.Subject.doSomething()
    from real subject
    调用之后public abstract void com.wenge.dynamicporxy.Subject.doSomething()

    我们还是实现了静态代理所实现的功能,而且我们的程序有了更好的可扩展性。我们可以通过这种方式代理不止一个类,而是多个只要这个类实现了上层接口,更为关键的是这一切是动态生成的,显然这是利用了java的反射技术。这样的话动态代理就可以做很多事情了,最经典的就是spring框架中依赖注入,有兴趣的童鞋可以深入去看看源代码。

    所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

    实现动态代理的步骤:

    1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
    2.创建被代理的类以及接口
    3.通过Proxy的静态方法
    newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理
    4.通过代理调用方法

    通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。

    欢迎指正讨论

  • 相关阅读:
    January 25th, 2018 Week 04th Thursday
    January 24th, 2018 Week 04th Wednesday
    January 23rd, 2018 Week 04th Tuesday
    January 22nd, 2018 Week 04th Monday
    January 21st, 2018 Week 3rd Sunday
    January 20th, 2018 Week 3rd Saturday
    January 19th, 2018 Week 3rd Friday
    January 18th, 2018 Week 03rd Thursday
    January 17th, 2018 Week 03rd Wednesday
    January 16th, 2018 Week 03rd Tuesday
  • 原文地址:https://www.cnblogs.com/Vdiao/p/6694287.html
Copyright © 2011-2022 走看看