Java基础加强——动态代理

代理模式:

  为其他对象提供一种代理以控制对这个对象的访问。

  代理模式主要分为两类:

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

 1.静态代理相对好理解一些,来看下面的代码:

    接口 Count.java

/**
 * 账户接口
 * @author jiangbei01
 *
 */
public interface Count {
    void add();
    void update();
}

    实现类 CountImpl

package cn.test;

public class CountImpl implements Count {

    @Override
    public void add() {
        System.out.println("真实对象.add");
    }

    @Override
    public void update() {
        System.out.println("真实对象.update");
    }

}

    代理对象CountProxy.java

package cn.test;

public class CountProxy implements Count {

    //持有真实对象引用
    private CountImpl countImpl;
    //覆盖默认构造器
    public CountProxy(CountImpl countImpl) {
        super();
        this.countImpl = countImpl;
    }

    @Override
    public void add() {
        System.out.println("代理对象执行add前");
        countImpl.add();
        System.out.println("代理对象执行add后");
    }

    @Override
    public void update() {
        System.out.println("代理对象执行update前");
        countImpl.update();
        System.out.println("代理对象执行update后");
    }

}

    看看测试代码:

public class Test01 {

    public static void main(String[] args) {
        CountImpl countImpl = new CountImpl();
        CountProxy countProxy = new CountProxy(countImpl);
        countProxy.add();
        System.out.println("===========================");
        countProxy.update();
    }
}

    运行结果也容易得出:

代理对象执行add前
真实对象.add
代理对象执行add后
===========================
代理对象执行update前
真实对象.update
代理对象执行update后

  容易看出静态代理缺点:每个代理只能为一个接口服务,并且重复代码量大!

2.动态代理:java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。 

  每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,

  当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

  推荐博客:http://www.cnblogs.com/xiaoluo501395377/p/3383130.html

  代理类处理的逻辑很简单:在调用某个方法前及方法后做一些额外的业务。换一种思路就是:

  在触发(invoke)真实角色的方法之前或者之后做一些额外的业务。那么,为了构造出具有通用性和简单性的代理类,

  可以将所有的触发真实角色动作交给一个触发的管理器,让这个管理器统一地管理触发。这种管理器就是Invocation Handler。

  推荐文档:http://www.360doc.com/content/14/0801/14/1073512_398598312.shtml

  我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

  Proxy类提供的方法:

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

其中最后一个参数 InvocationHandler h 得到InvocationHandler接口的子类实例 

  ClassLoader loader用来指明生成代理对象使用哪个类装载器,(Proxy.class.getClassLoader())

  Class<?>[] interfaces用来指明生成哪个对象的代理对象,通过接口指定,

  InvocationHandler h用来指明产生的这个代理对象要做什么事情。

  下面来看一个例子:

  先定义一个接口:Person.java

public interface Person {

    String sing(String musicName);
    String speak(String text);
}

  给出一个实现类:薛之谦 XueZhiQian.java

public class XueZhiQian implements Person {

    @Override
    public String sing(String musicName) {
        System.out.println("薛之谦唱了 "+musicName);
        return "歌曲唱完,谢谢各位!";
    }

    @Override
    public String speak(String text) {
        System.out.println("薛之谦说了 "+text);
        return "话已讲完,谢谢各位!";
    }

}

    生成代理对象的代理类:XueZhiQianProxy.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class XueZhiQianProxy {

    //要代理的对象
    private Person xzq = new XueZhiQian();
    
    public Person getProxy(){
        return (Person)Proxy.newProxyInstance(XueZhiQianProxy.class.getClassLoader(), 
                xzq.getClass().getInterfaces(), 
                new InvocationHandler() {
                    
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if(method.getName().equals("sing")){
                            System.out.println("找薛之谦唱歌需要先交2W!");
                            return method.invoke(xzq, args);
                        }
                        if(method.getName().equals("speak")){
                            System.out.println("找薛之谦唱歌需要先交10W!");
                            return method.invoke(xzq, args);
                        }
                        return null;
                    }
                });
    }
}

    测试代码:

public class Test01 {

    public static void main(String[] args) {
        XueZhiQianProxy proxy = new XueZhiQianProxy();
        Person p = proxy.getProxy();
        String s1 = p.sing("演员");
        System.out.println(s1);
        String s2 = p.speak("段子");
        System.out.println(s2);
    }
}

    测试结果:

找薛之谦唱歌需要先交2W!
薛之谦唱了 演员
歌曲唱完,谢谢各位!
找薛之谦唱歌需要先交10W!
薛之谦说了 段子
话已讲完,谢谢各位!

 

  

posted @ 2017-05-08 22:00  ---江北  阅读(278)  评论(0编辑  收藏  举报
TOP