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

java动态代理详解(一)

2016-10-11 15:22 344 查看
(一).什么是动态代理?

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

(二).为什么要用动态代理?

动态代理的优势就是实现无侵入式的代码扩展,比如 Spring的AOP的拦截功能是由java中的动态代理来实现的。

动态代理可以用在记录方法的日志,AOP,权限等等,而不侵入原来类的代码。

(三).java中动态代理的实现

在java中如果要为一个类创建动态代理,这个类必须实现接口,然后还有两个重要的东西:Proxy和InvocationHandler。Proxy类是创建动态代理的主要类。它的newProxyInstance()静态方法返回一个代理对象的实例,而InvocationHandler是该被代理类的事物处理器。具体看代码。

1.创建一个的接口,并创建该接口的实现类,

package com.my.proxyTest;

//创建一个接口
public interface UserService {
public void addUser(String userName);

}

package com.my.proxyTest;

public class UserServiceImpl implements UserService {
@Override
public void addUser(String userName) {
System.out.println("增加的用户名为:"+userName);
}

}

2.通过实现InvocationHandler接口创建自己的调用处理器

package com.my.proxyTest;

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

public class UserServiceHandler implements InvocationHandler{

public Object target;//用Object接收,可以传进来任意类型的对象。

public UserServiceHandler(Object obj){
target=obj;
}

/**
* proxy:代理类
* method:被代理类的方法
* args:该方法的参数数组
*/
@Override
//这里的invoke方法不是显示调用,而是java创建的代理对象$Proxy0.class调用
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("开始添加用户。。。");//在原来的对象方法执行前面,执行这段代码

Object obj=method.invoke(target,args);//传入被代理对象,也就是目标对象
return obj;
}

}


3.写测试类,并为对象创建代理对象

package com.my.proxyTest;

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

public class Client {

public static void main(String[] args) {

UserService userService=new UserServiceImpl();
UserServiceHandler h=new UserServiceHandler(userService);

//把$Proxy0强制转换成UserService
UserService userServiceProxy=(UserService)Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
h);

System.out.println(userServiceProxy instanceof Proxy); //输出为true

//输出:userServiceProxy的Class类是:class $Proxy0
//这里可以看出userServiceProxy的Class类是$Proxy0。
//这个$Proxy0类继承了Proxy,实现了UserServiceImpl接口
System.out.println("userServiceProxy的Class类是:"+userServiceProxy.getClass().toString());

System.out.print("<span style="font-size: 14px;">userServiceProxy</span>中的属性有:");

Field[] field=userServiceProxy.getClass().getDeclaredFields();
for(Field f:field){
System.out.print(f.getName()+", ");
}

System.out.print("\n"+"<span style="font-family: Arial, Helvetica, sans-serif;">userServiceProxy</span>中的方法有:");

Method[] method=userServiceProxy.getClass().getDeclaredMethods();

for(Method m:method){
System.out.print(m.getName()+", ");
}

System.out.println("\n"+"<span style="font-family: Arial, Helvetica, sans-serif;">userServiceProxy</span><span style="font-family: Arial, Helvetica, sans-serif;">的父类是:"+userServiceProxy.getClass().getSuperclass());  </span>

System.out.print("\n"+"<span style="font-family: Arial, Helvetica, sans-serif;">userServiceProxy</span>实现的接口是:");

Class<?>[] interfaces=userServiceProxy.getClass().getInterfaces();

for(Class<?> i:interfaces){
System.out.print(i.getName()+", ");
}

System.out.println("\n\n"+"运行结果为:");

userServiceProxy.addUser("张三");
}

}

输出结果:

true

userServiceProxy的Class类是:class $Proxy0
userServiceProxy中的属性有:m3, m1, m0, m2, 
userServiceProxy中的方法有:addUser, hashCode, equals, toString, 
userServiceProxy的父类是:class java.lang.reflect.Proxy

userServiceProxy实现的接口是:com.my.proxyTest.UserService, 

运行结果为:

开始添加用户。。。

增加的用户名为:张三。可以发现在没有改动原来类的基础上,增加了原来类的业务逻辑。这就是java动态代理运用。

(四)分析源码

先看看Proxy类的主要代码

Proxy构造方法

private final static Class[] constructorParams ={ InvocationHandler.class }; //定义参数是<span style="font-family: Arial, Helvetica, sans-serif;">InvocationHandler的数组</span>

protected InvocationHandler h;

// 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用
private Proxy() {
}

// 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用
protected Proxy(InvocationHandler h) {
this.h = h;
}

Proxy静态方法newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException {
// 检查 h 不为空,否则抛异常
if (h == null) {
throw new NullPointerException();
}

// 获得与指定类装载器和一组接口相关的代理类类型对象
Class cl = getProxyClass(loader, interfaces);

// 通过反射获取构造函数对象并生成代理类实例
try {
Constructor cons = cl.getConstructor(constructorParams);
//cons即是形参为InvocationHandler类型的构造方法,通过构造方法创建代理类实例,
//此时需将调用处理器对象作为参数被传入
return (Object) cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) { throw new InternalError(e.toString());     }
catch (IllegalAccessException e) { throw new InternalError(e.toString());     }
catch (InstantiationException e) { throw new InternalError(e.toString());     }
catch (InvocationTargetException e) { throw new InternalError(e.toString());     }
}


类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据:

public static byte[] generateProxyClass(final String name, Class[] interfaces)


我们可以import sun.misc.ProxyGenerator,调用 generateProxyClass方法产生binary data,然后写入文件,最后通过反编译工具来查看内部实现原理。 反编译后的userServiceProxy.java Proxy静态方法newProxyInstance

public final class $Proxy0 extends Proxy implements Subject {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;

static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });

m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);

m3 = Class.forName("***.RealSubject").getMethod("request",
new Class[0]);

m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);

} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
} //static

public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}

@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

public final void request() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}



总结

一个典型的动态代理创建对象过程可分为以下四个步骤:

1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);

2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类

Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});

3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型

Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入

Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
生成的ProxySubject继承Proxy类实现UserServiceImpl接口,实现的UserServiceImpl的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))

java的用jdk实现动态代理有个美中不足,它只能实现继承接口的类。对于那些没有实现任何接口类无法使用动态代理,这时候就需要用到第三方的cglib来实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: