Java动态代理

1、简介

在java常用的设计模式中有一种模式叫:代理模式----通过一个代理类负责处理被代理类(委托类)的调用,如果你了解或使用过Spring的切面(AOP),可以更好的理解何为代理,通俗的讲就是在一个类的方法执行的前后加入操作(如进入和完成方法打印日志),实际上Spring正是使用java的动态代理来实现切面的;一般代理类和委托类实现同样的接口,代理类通过调用委托类对象的方法真正的实现服务,并且可以在实现的前后增加需要的处理。

从代理创建时期区分,java的代理可以分为两种:
静态代理:代理类在程序运行前就已经存在;
动态代理:代理类在程序的运行期间才创建;

2、静态代理
静态代理方式更好理解,我们先简单实用测试代码说明,以手机生产为例:一般手机厂商都不是通过自己整机生产手机,而是由代工厂代理生产;
/**
 * 手机生产接口
 * @author hwz
 *
 */
public interface PhoneBuilder {

    public void buildAPhone(); 
}
/**
 * 手机生产厂商
 * @author hwz
 *
 */
public class PhoneVendor implements PhoneBuilder{

    @Override
    public void buildAPhone() {
        //设计和生产手机
        System.out.println("design and build a phone");
    }
}
/**
 * 代理工厂类
 * @author hwz
 *
 */
public class PhoneBuilderProxyFactory implements PhoneBuilder {

    private PhoneBuilder phoneBuilder;//代理类一把通过聚合持有委托类的一个实例

    public PhoneBuilderProxyFactory(PhoneBuilder phoneBuilder) {
        this.phoneBuilder = phoneBuilder;
    }

    @Override
    public void buildAPhone() {

        phoneBuilder.buildAPhone();
        //组装手机
        System.out.println("phone  assemble and produce...");
    }
}
/**
 * 静态代理测试
 * @author hwz
 *
 */
public class StaticProxyTest {

    public static void main(String[] args) {

        PhoneVendor realBuilder = new PhoneVendor();

        PhoneBuilderProxyFactory proxyBuilder = new PhoneBuilderProxyFactory(realBuilder);

        proxyBuilder.buildAPhone();
    }
}

可以看出,静态代理的优点可以解耦调用方和委托类,在不修改委托类的源码情况下增加处理,但是静态代理的代理类代码是必须在程序编译运行前编写好。

3、动态代理
简单介绍完静态代理,下面重点介绍java动态代理:
顾名思义,动态代理的代理类是在程序运行中动态创建的,也就是说代理类并不是由我们通过代码定义的,
下面我们通过以下需求来详细说明:
对所有被调用的外部类的方法全部在方法开始前打印入参日志,方法执行完成后打印返回结果日志
先按照静态代理的方式实现:
/**
 * 一个简单的外部接口
 * @author hwz
 */
public interface OneOuterInterface {
    /**
     * 方法一:翻转字符串
     * @param str
     * @return
     */
    public String methodOne(String str);
    /**
     * 方法二:计算两个数的乘积
     * @param a
     * @param b
     * @return
     */
    public int methodSecond(int a, int b);
}
/**
 * 简单的外部类
 * @author hwz
 *
 */
public class OneOuterClass implements OneOuterInterface {

    @Override
    public String methodOne(String str) {
        return new StringBuilder(str).reverse().toString();
    }

    @Override
    public int methodSecond(int a, int b) {
        return a*b;
    }
}
/**
 * 代理类
 * @author hwz
 *
 */
public class InnerProxyClass implements OneOuterInterface {

    private OneOuterInterface outer;

    public InnerProxyClass(OneOuterInterface outer) {
        this.outer = outer;
    }

    @Override
    public String methodOne(String str) {
        System.out.println("Enter methodOne,str=" + str);
        String result = outer.methodOne(str);
        System.out.println("Leave methodOne,result=" + result);
        return result;
    }

    @Override
    public int methodSecond(int a, int b) {
        System.out.println("Enter methodSecond,a=" + a + ",b=" + b);
        int result = outer.methodSecond(a, b);
        System.out.println("Leave methodSecond,result=" + result);
        return result;
    }
}
/**
 * 测试代码
 * @author hwz
 *
 */
public class ProxyOuterClassTest {

    public static void main(String[] args) {

        InnerProxyClass proxy = new InnerProxyClass(new OneOuterClass());
        proxy.methodOne("abcdef");
        proxy.methodSecond(2, 3);
    }
}

通过这样的静态代理方式,实现简单,也容易理解,但是对于这个增加日志打印的需求,这种实现方式明显不好,每个外部接口都要实现一个代理类,接口中的每个方法都要增加日记打印语句,这样的方式重复工作量大,繁琐,谁都不愿意这样实现;
通过动态代理方式就可以高效的实现上面的需求:
动态代理类的字节码在程序运行中由java的反射机制生成,具有更好的扩展性;java.lang.reflect包下Proxy类和InvocationHandler接口提供实现动态代理的方式:

public interface InvocationHandler {
  
    Object invoke(Object proxy, Method method, Object[] args);
  
}

invoke参数:
proxy  :  生成的代理类实例对象
method : 实际要调用的方法
args : 方法调用时的参数
InvocationHandler 接口可以理解为“调用处理器”,但是具体的实现有程序员决定;

Proxy类,是实际完成代理的操作类,由这个类的方法动态生成代理对象,使用如下方法:

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

loader:类加载器
interfaces:需要代理的类的全部接口
handler:上面InvocationHandler的实例

下面我们通过动态代理来实现上面的需求(打印日志):
/**
 * 代理处理器
 * @author hwz
 *
 */
public class OutInterfaceInvokeHandler implements InvocationHandler {

    private Object proxyObj;

    public Object proxy(Object proxyObj) {
        this.proxyObj = proxyObj;
        return Proxy.newProxyInstance(proxyObj.getClass().getClassLoader(), proxyObj.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        StringBuilder builder = new StringBuilder();
        if (args != null) {
            for (int i=0; i<args.length; ++i) {
                builder.append(",arg" + i + "=" + args[i].toString());
            }
        }
        System.out.println("Enter " + method.toString() + ",args:" + builder.toString());
        //此处proxy是生成代理类的实例
        Object result = method.invoke(proxyObj, args);

        System.out.println("Leave " + method + ",result=" + result.toString());
        return result;
    }
}
/**
 * 测试代码
 * @author hwz
 *
 */
public class ProxyOuterClassTest {

    public static void main(String[] args) {

        OneOuterInterface outer = new OneOuterClass();
        OutInterfaceInvokeHandler handler = new OutInterfaceInvokeHandler();
        OneOuterInterface proxy = (OneOuterInterface) handler.proxy(outer);

        proxy.methodOne("abcdef");
        proxy.methodSecond(2, 4);
    }
}
注意:InvocationHandler.invoke方法的第一个入参是动态生成代理类的实例对象引用,直接使用这个proxy调用方法会发生无限循环调用

动态代理方式能可以更加高效的实现需求,不需要每个类都写一个代理类;
通过以上的示例代码可以看出,java的动态代理是必须基于接口实现的,当然这也非常符合java编程的规范;



发布了50 篇原创文章 · 获赞 8 · 访问量 11万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章