JDK的动态代理为什么必须要使用接口与使用CGLIB动态代理

一.JDK的动态代理为什么必须要使用接口

JDK的代理Proxy必须要使用接口,才可以实现对方法的拦截。为什么呢?先让我们看一个JDK动态代理的示例:

接口类:

public
interface
IPerson {

public
void
sayHi(String nm);

}

接口实现类:

public
class
Person  implements IPerson{

public Person(){//构造

}

private String
name;

public Person(String name){//构造

this.name=name;

}

public
void
sayHi(String str){

System.err.println(name+" Hello:"+str);

}

}

使用JDK代理Person类的方法:

@Test

public
void
testJdkProxy() throws Exception{

final Dog dog =
new Dog();

//以下必须返回接口,因为Proxy内部,会根据接口创建一个IAnimal的子类,

//创建的这个子类是Dog类的兄弟关系。子类可以转换成父类,但对于平行的两个转换将失败,

//这就是为什么必须要使用接口的原因

IAnimal dog2 = (IAnimal)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),

dog.getClass().getInterfaces(),

newInvocationHandler() {

public Objectinvoke(Object proxy, Method method, Object[] args)

throws Throwable {

System.err.println("Before...");

Object o =method.invoke(dog, args);

return o;

}

});

dog2.eat();

boolean boo = Proxy.isProxyClass(dog2.getClass());

System.err.println("是否是被代理对象:"+boo+","+dog2.getClass());

}

代码说明:Proxy返回的对象必须要强转为Ianimal的类型。如果要转成Dog类型则会抛出ClassCastException。

Proxy代理可以使用以下简图加以说明:

上图中,最关键,也是最重要的部分就是Proxy在接收到Person类的实例以后,创建了IPerson类的一个子类取名为$Proxy$,此时$Proxy$类与Person类为同级兄弟关系。所以如果使用以下代码将会抛出ClassCastException:

Person person = (Person)Proxy.newInstance(…..);

但使用接口就不会出现异常,即:

IPerson peson  = (IPerson)Proxy.newInstance(…..);

1、为了更加清楚为什么会抛出转换异常,我再做如下测试:

声明一个IAnimal接口类:

public
interface
IAnimal {

public Dog(String food){   
public void eat();

}

实现IAnimal接口的Dog类:

public
class
Dog implements IAnimal {

private String
food;

this.food=food;

}

public
void
eat() {

System.err.println("小狗吃:"+food);

}

}

实现IAnimal接口的Cat类:

public
class
Cat implements IAnimal {

private String
food;

public Cat(String food){

this.food=food;

}

public
void
eat() {

System.err.println("小猫吃:"+food);

}

}

测试是否可以将Dog类的实例转换成Cat,转换时,将会抛出ClassCastException:

IAnimal dog = new Dog("骨头");

IAnimal cat = new Cat("小鱼");

Dog dog2 = (Dog) cat;//cat与dog是兄弟关系不能转换成功,抛出ClassCastException

    dog = cat;     //此时将转换成功,因为dog是IAnimal类型,可以指向自己的子类

2、然后再测试使用反射

IAnimal dog = new Dog("骨头");

IAnimal cat = new Cat("小鱼");

boolean boo = dog.getClass()==cat.getClass();

System.err.println(boo);

Method m = dog.getClass().getMethod("eat");//从dog中获取

m.invoke(cat);//不成功,说明反射从哪儿获取方法,就应该调用谁的实例

//但如果是从最高接口中获取到的方法,则可以执行,如下:

Method m2 = IAnimal.class.getMethod("eat");

m2.invoke(dog);//执行成功

System.err.println("---");

m.invoke(cat);//执行成功

上例中:

Methodm = dog.getClass.getMethod(“eat”);

m.invoke(dog);只可以执行dog的方法,如果填入cat则会执行不成功。因为Dog类拥有自己的字节码。

而如果修改成:

Method m = IAnimal.class.getMethod(“eat”);

m.invoke(dog);

m.invoke(cat);//两个调用都可以执行成功因为,Dog和Cat拥有相同的父类接口,而IAnimal字节码只有一份。

二.使用CGLIB动态代理

Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类。使用CGLIB即使被代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:

使用CGLIB需要导入以下两个jar文件:

asm.jar– CGLIB的底层实现。

cglib.jar– CGLIB的核心jar包。

CGLIB的核心类:

net.sf.cglib.proxy.Enhancer– 主要的增强类

net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现

net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:

Objecto = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。

费话少说,上代码:

1、使用CGLIB的代理:

以下测试代理一个没有实现任何接口的Person类:

@Test

public
void
testProxy1() throws Exception {

final Person p1 =
new Person();             //Person类没有实现任何接口

Enhancer en = new Enhancer();               //声明增加类实例

en.setSuperclass(Person.class);             //设置被代理类字节码,CGLIB根据字节码生成被代理类的子类

en.setCallback(new MethodInterceptor() {    //设置回调函数,即一个方法拦截

public Object intercept(Object target, Method method,

Object[] args, MethodProxy proxy) throws Throwable {

Object o = method.invoke(p1,args);    //注意参数p1,仍然为外部声明的源对象,且Method为JDK的Method反射

System.err.println("After...");

return o;

}

});

Person p = (Person) en.create();             //通过create方法返回Person类的代理

System.err.println(p.getClass());//被代理的对象

p.sayHi("Hello");

}

2、以下测试代理一个拥有接口的类:

IAnimal是接口,Dog是实现类,具体代码如下:

@Test

public
void
testProxy2() throws Exception {

final Dog dog =
new Dog();            
//声明被代理对象

Enhancer en = new Enhancer();         
//声明CGLIB增强类

en.setSuperclass(IAnimal.class);      
//设置接口类,也可以设置成dog实现类,会影响create返回的对象

en.setCallback(new MethodInterceptor() {

public Object intercept(Object target, Method method,

Object[] args, MethodProxy proxy) throws Throwable {

System.err.println("Before...");

Object o = method.invoke(dog, args);

System.err.println("After...");

return o;

}

});

//Dog dog2 = (Dog)en.create();//必须转型为接口,否则抛出ClassCastException

IAnimal dog2 = (IAnimal)en.create();

dog2.eat();

}

说明:

由于上例中,设置了en.setSuperclass(IAnimal.class),所以en.create()方法,返回的对象,必须要转换成IAnimal接口。如果转换成Dog则会抛出ClassCastException。

3、将CGLIB再做一个简单的包装:

class CglibProxy
implements MethodInterceptor{

private Object
srcTarget;

private CglibProxy(Object o){

this.srcTarget = o;

}

@SuppressWarnings("unchecked")

public
static
<T>T proxyTarget(T t){

Enhancer en= new Enhancer();

en.setSuperclass(t.getClass());

en.setCallback(new CglibProxy(t));

T tt = (T) en.create();

return tt;

}

@Override

public Object intercept(Object obj, Method method, Object[]args,

MethodProxy proxy) throws Throwable {

System.err.println("拦截前...");

Object o = method.invoke(srcTarget, args);

System.err.println("拦截后....");

return o;

}

}

包装以后的调用代码如下,主要是快速的实现获取被代理类:

Person p = CglibProxy.proxyTarget(new Person());

p.sayHi("HJello");

IAnimal dog = CglibProxy.proxyTarget(new Dog());

dog.eat();

4、使用静态方法代理一个没有接口的对象

以下代码,包含在一个测试方法或是main方法中运行:

final Person
src = new Person();

//直接使用静态方法代理一个对象

Person p = (Person) Enhancer.create(Person.class,newMethodInterceptor(){

public Object intercept(Object proxyedObj, Method method, Object[]args,

MethodProxy proxy) throws Throwable {

System.err.println("Hello");

//使用原生的方法调用,注意里面的src

//Object oo= method.invoke(src,
args);

//使用MethodProxy调用父类的代码,同样有效

Object oo = proxy.invokeSuper(proxyedObj, args);

return oo;

}

});

System.err.println(p.getClass());

p.abc();

时间: 2024-10-09 00:12:05

JDK的动态代理为什么必须要使用接口与使用CGLIB动态代理的相关文章

Java进阶之 JDK动态代理与Cglib动态代理

一.动态代理概述: 与静态代理对照(关于静态代理的介绍 可以阅读上一篇:JAVA设计模式之 代理模式[Proxy Pattern]), 动态代理类的字节码是在程序运行时由Java反射机制动态生成. 注意: 1.AspectJ是采用编译时生成AOP代理类,具有更好的性能,但是需要使用特定的编译器进行处理 2.Spring AOP采用运行时生成AOP代理类,无需使用特定编译器进行处理,但是性能相对于AspectJ较差 二.JDK动态代理 [对有实现接口的对象做代理] 1.JDK动态代理中 需要了解的

JDK动态代理和CGLIB动态代理

转载自http://www.itzhai.com/java-dong-tai-dai-li-zhi-jdk-dong-tai-dai-li-he-cglib-dong-tai-dai-li-mian-xiang-qie-mian-bian-cheng-aop-yuan-li.html 静态代理 静态代理相对来说比较简单,无非就是聚合+多态: 参考:设计模式笔记 – Proxy 代理模式 (Design Pattern) 动态代理 我们知道,通过使用代理,可以在被代理的类的方法的前后添加一些处理方

深入探索spring技术内幕(六): JDK动态代理和cglib生成代理

[ JDK生成代理 ] JDK中给我们提供了一个Proxy类可以动态的给我们生成代理. 假定我们要做一个权限管理系统, 需要控制用户对某一个方法的访问. 如果user为null, 那么不让用户访问save方法. ① 接口类: PersonService public interface PersonService { public void save(); } ② 实现类: PersonServiceImpl public class PersonServiceImpl implements P

关于JDK动态代理和CGLIB动态代理

1. 代理模式 一句话总结:为其他对象提供一种代理以控制对这个对象的访问.千篇一律的介绍:代理模式是常用的java设计模式,他的特征是代理类与委托类(或目标类)有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务. 按照代理的创建时期,代理类可以分为两种. 静态代理:由程序员创建或特定工具

Java代理之(jdk静态代理/jdk动态代理/cglib动态代理/aop/aspectj)

一.概念 代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道.如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法.如果公司卖多少钱,代理商也卖多少钱,那么代理商就赚不了钱.所以代理商在调用公司的卖方法后,加上自己的利润然后再把产品卖给客户.而客户部直接跟公司打交道,或者客户根本不知道公司的存在,然而客户最终却买到了产品. 专业点说:代理模式是对象的结构型模式,代码模式给某一个对象提供代理,并由代理对象控制原对象

Java之代理(jdk静态代理,jdk动态代理,cglib动态代理,aop,aspectj)

一.概念 代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道.如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法.如果公司卖多少钱,代理商也卖多少钱,那么代理商就赚不了钱.所以代理商在调用公司的卖方法后,加上自己的利润然后再把产品卖给客户.而客户部直接跟公司打交道,或者客户根本不知道公司的存在,然而客户最终却买到了产品. 专业点说:代理模式是对象的结构型模式,代码模式给某一个对象提供代理,并由代理对象控制原对象

Spring AOP中的JDK和CGLib动态代理哪个效率更高?

一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理,另一种是CGLib的方式. 自Java 1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了Spring的很多地方. JDK动态代理主要涉及java.lang.reflect包下边的两个类:Proxy和InvocationHandler.其中,Invoc

jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)

代理模式是一种很常见的模式,关于底层原理网上看到很多的有关的讲解,但看了一些都觉得比较粗略,很多时候把底层代码copy下来也不大讲解,感觉不如自己详细的写上一篇.本文将以非常详细的说明来分析cglib动态代理底层的实现原理,篇幅较长,但是每个核心方法代码中每步都有说明.还请耐心阅读 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency&

Java动态代理、CGLIB动态代理

开篇 Java 的代理就是客户类不再直接和委托类打交道, 而是通过一个中间层来访问, 这个中间层就是代理.为啥要这样呢, 是因为使用代理有 2 个优势: 可以隐藏委托类的实现 可以实现客户与委托类之间的解耦, 在不修改委托类代码的情况下能够做一些额外的处理 我们举个很常见的例子: 工厂会生产很多的玩具, 但是我们买玩具都是到商店买的, 而不是到工厂去买的, 工厂怎么生产我们并不关心, 我们只知道到商店可以买到自己想要的玩具,并且,如果我们需要送人的话商店可以把这些玩具使用礼品盒包装.这个工厂就是