欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

spring框架之动态代理类——附:声明类型与实例类型(不一致问题)

程序员文章站 2022-05-23 19:31:54
...

动态代理类实现方式

第一种:jdk实现(面向接口)

第一步:实现InvocationHandler接口

该接口是java反射包里面定义的。获取代理类的原理是通过Proxy类的newProxyInstance方法通过反射机制获取目标类的字节码类加载器,还有接口类型。

第二步:实现接口中的invoke方法。

第三步:获取一个代理类getproxy

该方法是需要定义接口的,获取的时候也是要通过声明接口类型,实例化的代理对象是目标类的兄弟。

附:声明类型与实例类型不一致问题

在测试的时候,我通过实例化1个工厂类,去获取2个不同的目标类的代理类,代码如下:


//使用一个工厂代理类,就能创建多个代理类对象
		ServiceProxyFactory factory = new ServiceProxyFactory();
		//ServiceProxyFactory factory2 = new ServiceProxyFactory(); 备用工厂
		//创建目标类实例(2个)
		BookServiceIfac bookservice = new BookServiceImpl();  
		PlayServiceIfac playService = new PlayServiceImpl();
		
		BookServiceIfac proxy1 = (BookServiceIfac) factory.getProxy(bookservice);
		PlayServiceIfac proxy2 = (PlayServiceIfac) factory.getProxy(playService);
		proxy1.borrowBook("九阳神功"); 
		proxy2.playGame("tom");
		proxy2.playToy("marry");

此时上面的代码看上去应该没什么问题,而且编译器也不会报检查错误,但在运行时,就出现了如下的错误
object is not an instance of declaring class(该对象不是一个声明类的对象)

其实只要改正一下代码的位置就能解决了,代码如下:


//使用一个工厂代理类,就能创建多个代理类对象
		ServiceProxyFactory factory = new ServiceProxyFactory();
		//ServiceProxyFactory factory2 = new ServiceProxyFactory();  //备用工厂
		//创建目标类实例(2个)
		BookServiceIfac bookservice = new BookServiceImpl();  
		PlayServiceIfac playService = new PlayServiceImpl();
		
		BookServiceIfac proxy1 = (BookServiceIfac) factory.getProxy(bookservice);
		proxy1.borrowBook("九阳神功"); 

		PlayServiceIfac proxy2 = (PlayServiceIfac) factory.getProxy(playService);
		proxy2.playGame("tom");
		proxy2.playToy("marry");

总结:
一定要在获取该声明类型的代理类时使用方法,不然使用同一个工厂创建另外的代理类对象时,声明就会改变,此时如果再使用之前的代理类方法,则会报 该实例不是声明的对象类型的错误 。

但是,如果想要避免这错误,就要实例化2个工厂对象(也就是上面代码注释掉的备用工厂),但这样就跟静态代理时创建方式一样了,虽然不用创建多个工厂类。

第二种:cglib实现(面向对象–继承)

第一步:导入 asm cglib 包
(注意版本问题:使用asm-3.2.jar时,cglib的版本一定要3.0以下,如cglib-2.2.jar)

第二步:实现 MethodInterceptor接口

第三步:定义生成代理对象的方法getproxy

其中需要创建一个放大器对象(Enhancer
1.设置父类对象(即为目标类)
2.设置回调函数(当我们调用代理对象的方法时后台调用的是回调对象的intercept方法)
3.调用放大器方法创建代理类对象并作为返回值

该方法不需要定义接口,获取的时候声明为目标类就行了,实例化的代理对象是继承目标类的。