您的位置:首页 > 编程语言 > Java开发

Java动态代理

2016-06-17 17:43 507 查看
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编程的规范;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息