java动态代理详解(一)

(一).什么是动态代理?

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


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

动态代理的优势就是实现无侵入式的代码扩展,比如 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来实现。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值