28、设计模式之动态代理

在25章讲过代理模式,这里再开一章来详细讲解代理模式,因为在spring的aop核心技术就是动态代理,有必要把动态代理机制理解透彻:


 代理模式的特征是代理类与委托类(被代理类)有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类的对象本身并不真正实现服务,而是通过调用委托类对象的相关方法,来实现功能,故还是由委托类完成核心的操作。 

按照代理的创建时期,代理类可以分为两种。 
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 
动态代理:在程序运行时,运用反射机制动态创建而成。 
静态代理(如上图)每一个代理类只能为一个接口(委托类)服务,若PreBuild()和as.PostBuild()都是相同的代码,就会造成很多重复代码,这不是java语言所提倡的,解决这个问题就是使用动态代理。  
 
动态代理涉及到一个接口:InvocationHandler(调用处理器)
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;    //这里的proxy指的被代理的对象,调用委托类的方法
动态代理涉及到一个类:Proxy   是完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类;
protected InvocationHandler h;      //引用了InvocationHandler接口
private static class ProxyAccessHelper {     //内部类
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException{   // 生成实现类
//Class<?>[] interfaces:得到全部的接口 
动态代理类的字节码文件(.class)在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。 
下面通过两个例子来了解如何使用动态代理:
public interface Interface1 {
        public void anyMethod();
}

public class Impl1 implements Interface1{
	public void  anyMethod(){
		System.out.println("实现委托类功能");
	}
}

public class ProxyImpl1 implements InvocationHandler{
	private  Object target;  //委托类,确保执行时是同一target;
	public Object createProxy(Object target) {
		this.target = target;     //确保是同一target
		//利用反射机制完成
		//Proxy.newProxyInstance(loader, interfaces, h);
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(),this); 
		//实现接口而来的this调用处理器	
	}
       //jvm底层会将这个方法  完完全全的与它要代理的 方法绑定
	 //犹如:new B().fun() 执行了new BProxy().fun()一样;
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	Object result=null;  
        System.out.println("委托类方法"+method.getName()+"执行前");  
        result=method.invoke(target, args);    //调用method的invoke方法
        System.out.println("动态生成的代理类:"+proxy.getClass().toString());  
        return result;  
	}
}
 测试:
Impl1 i = new Impl1();
System.out.println("委托类对象:"+i.toString());
         
ProxyImpl1 pi = new ProxyImpl1();
//要用接口接收
Interface1 ip = (Interface1) pi.createProxy(new Impl1()); 
//用Impl接收会报com.sun.proxy.$Proxy0 cannot be cast to org.demo.api.Impl1
//Impl1 ip2 = (Impl1) pi.createProxy(new Impl1()); 
ip.anyMethod();

输出:
委托类对象:org.demo.api.Impl1@1dd3812
委托类方法anyMethod执行前
实现委托类功能
动态生成的代理类:class com.sun.proxy.$Proxy0

最后生成了一$Proxy0的这么一个类(不是对象,对象是有@符号,用toString也表明是一个类), 如果改为System.out.println("动态生成的代理类:"+proxy.toString());  会报

Exception in thread "main" java.lang.StackOverflowError:at com.sun.proxy.$Proxy0.toString(Unknown Source)当应用程序递归太深而发生堆栈溢出时,抛出该错误。就是说传进来的这个对象找不到,而只能知道它属于$Proxy0类;

找遍了计算机也没找到$Proxy0.class文件,也不知道里面到底是什么内容。。

 

 

JDK的动态代理实现接口(interface1),如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。cglib是针对 类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

修改委托类(没有实现interface1接口):

 

public class Impl1 {
	public void  anyMethod(){
		System.out.println("实现委托类功能");
	}
}

 代理类要实现MethodInterceptor 接口:  添加cglib.jar 或者用spring.core.jar也可以,只是里面有些参数意义不是很明确;

 

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

//import org.springframework.cglib.proxy.Enhancer;
//import org.springframework.cglib.proxy.MethodInterceptor;
//import org.springframework.cglib.proxy.MethodProxy;

public class ProxyImpl1 implements MethodInterceptor {
	private  Object target;  //委托类,确保执行时是同一target;
	public Object createProxy(Object target) {
		//同样创建代理对象
		 this.target = target;
		 Enhancer enhancer = new Enhancer();  
	        enhancer.setSuperclass(this.target.getClass());  
	        // 回调方法  
	        enhancer.setCallback(this);  
	        // 创建代理对象  
	        return enhancer.create();  
	}

	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		 System.out.println("委托类方法"+method.getName()+"执行前");  
		 proxy.invokeSuper(obj, args);  //与jvm相似
		 System.out.println("动态生成的代理类,是个内部类?:"+obj.getClass().toString());  
		return null;  //返回为null与jvm有点不一样
	}

//	public Object intercept(Object arg0, Method arg1, Object[] arg2,
//			MethodProxy arg3) throws Throwable {
//		 System.out.println("委托类方法"+arg1.getName()+"执行前");  
//		 arg3.invokeSuper(arg0, arg2);  //与jvm相似
//		 System.out.println("动态生成的代理类,是个内部类?:"+arg0.getClass().toString()); 
//		return null;
//	}
}

 测试:

 

public static void main(String[] args) {
         Impl1 i = new Impl1();
         System.out.println("委托类对象:"+i.toString());
	 ProxyImpl1 pi = new ProxyImpl1();
	 //要用委托类接收
	 Impl1 ip2 = (Impl1) pi.createProxy(new Impl1()); 
	 ip2.anyMethod();
	}
输出:
委托类对象:org.demo.api.Impl1@153f67e
委托类方法anyMethod执行前
实现委托类功能
动态生成的代理类,是个子类:class org.demo.api.Impl1$$EnhancerByCGLIB$$5bfa5f1a

测试中要用委托类来接收,因为这个例子中没有实现接口,也就没有接口;如果最先实现了接口会是怎么样呢?

测试发现,也是实现了动态代理。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值