-
2018-12-28 13:46:28
前段时间去学习了一下RPC原理,其中提到了动态代理。特意去学习了一下,为了防止以后忘记,写下这篇博客做个记录,有不对的地方欢迎指正。
java中的常见的动态代理如Spring AOP。关于动态代理首先需要了解代理模式,根据代理类的创建时间又可以分为静态代理和动态代理。1、代理模式
代理模式是常见的设计模式之一,Java我们通常通过new一个对象然后调用其对应的方法来访问我们需要的服务。代理模式则是通过创建代理类(proxy)的方式来访问服务,代理类通常会持有一个委托类对象,代理类不会自己实现真正服务,而是通过调用委托类对象的相关方法,来提供服务,所以其实我们调用的还是委托类的服务,但是中间隔了一个代理类。这么做是有好处的,我们可以在访问服务之前或者之后加一下我们需要的操作。例如Spring的面向切面编程,我们可以在切入点之前执行一些操作,切入点之后执行一些操作。这个切入点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。
图片来源:大话设计模式2、静态代理
再说动态代理之前先说一下静态代理。静态代理就是程序员在编写代码的时候就已经把代理类的源码写好了,编译后就会生成.class文件。
简单代码实现
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。我这举一个租客通过中介租房子的例子。
(1)首先创建一个person接口,这个接口就是租客和中介的公共接口,这个接口有一个rentHouse()方法。public interface Person { //租房 public void rentHouse(); }
(2)创建租客Renter类,实现上述接口
public class Renter implements Person{ @Override public void rentHouse() { System.out.println("租客租房成功!"); } }
(3)创建中介类RenterProxy,同样实现Person接口,但是还另外持有一个租客类对象
public class RenterProxy implements Person{ private Person renter; public RenterProxy(Person renter){ this.renter = renter; } @Override public void rentHouse() { System.out.println("中介找房东租房,转租给租客!"); renter.rentHouse(); System.out.println("中介给租客钥匙,租客入住!"); } }
(4)新建测试类测试
public class StaticProxyTest { public static void main(String[] args) { Person renter = new Renter(); RenterProxy proxy = new RenterProxy(renter); proxy.rentHouse(); } }
运行结果:
中介找房东租房,转租给租客!
租客租房成功!
中介给租客钥匙,租客入住!3、动态代理
代理类在程序运行时创建的代理方式被成为动态代理。在静态代理中,代理类(RenterProxy)是自己已经定义好了的,在程序运行之前就已经编译完成。而动态代理是在运行时根据我们在Java代码中的“指示”动态生成的。动态代理相较于静态代理的优势在于可以很方便的对代理类的所有函数进行统一管理,如果我们想在每个代理方法前都加一个方法,如果代理方法很多,我们需要在每个代理方法都要写一遍,很麻烦。而动态代理则不需要。
代码实现
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
(1)和静态代理相同,首先定义一个person接口public interface Person { //租房 public void rentHouse(); }
(2)创建被代理的类
public class Renter implements Person{ @Override public void rentHouse() { System.out.println("租客租房成功!"); } }
(3)创建RenterInvocationHandler类,这个类实现了InvocationHandler接口,并持有一个被代理类的对象,InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。然后通过反射在invoke方法中执行代理类的方法。在代理过程中,在执行代理类的方法前或者后可以执行自己的操作,这就是spring aop的主要原理。
public class RenterInvocationHandler<T> implements InvocationHandler{ //被代理类的对象 private T target; public RenterInvocationHandler(T target){ this.target = target; } /** * proxy:代表动态代理对象 * method:代表正在执行的方法 * args:代表调用目标方法时传入的实参 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //代理过程中插入其他操作 System.out.println("租客和中介交流"); Object result = method.invoke(target, args); return result; } }
(4)创建动态代理对象
public class ProxyTest { public static void main(String[] args) { //创建被代理的实例对象 Person renter = new Renter(); //创建InvocationHandler对象 InvocationHandler renterHandler = new RenterInvocationHandler<Person>(renter); //创建代理对象,代理对象的每个执行方法都会替换执行Invocation中的invoke方法 Person renterProxy = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),new Class<?>[]{Person.class}, renterHandler); renterProxy.rentHouse(); //也可以使用下面的方式创建代理类对象,Proxy.newProxyInstance其实就是对下面代码的封装 /*try { //使用Proxy类的getProxyClass静态方法生成一个动态代理类renterProxy Class<?> renterProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class<?>[]{Person.class}); //获取代理类renterProxy的构造器,参数为InvocationHandler Constructor<?> constructor = renterProxyClass.getConstructor(InvocationHandler.class); //使用构造器创建一个代理类实例对象 Person renterProxy = (Person)constructor.newInstance(renterHandler); renterProxy.rentHouse(); // } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ } }
执行结果:
租客和中介交流
租客租房成功!原理分析:
点击查看Proxy类newProxyInstance方法源码,重要部分我做了注释public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, interfaces); } /* * Look up or generate the designated proxy class. */ //生成动态代理类 Class<?> cl = getProxyClass0(loader, interfaces); /* * Invoke its constructor with the designated invocation handler. */ try { //获取构造器参数是InvocationHandler类,constructorParams是Proxy的静态成员变量 // private final static Class[] constructorParams ={ InvocationHandler.class }; final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } } private static Object newInstance(Constructor<?> cons, InvocationHandler h) { try { //使用构造器创建一个代理类实例对象 return cons.newInstance(new Object[] {h} ); } catch (IllegalAccessException | InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString()); } } }
Class<?> cl = getProxyClass0(loader, interfaces);这行代码生成了一个代理类,这个类缓存在java虚拟机中。可以通过下列代码将其打印出来。
byte[] classByte = ProxyGenerator.generateProxyClass("$Proxy0", new Class<?>[] { Person.class }); String path = "D:/Eclipse/Workspace/Demo/bin/$Proxy0.class"; try (FileOutputStream fos = new FileOutputStream(path)) { fos.write(classByte); fos.flush(); } catch (Exception e) { e.printStackTrace(); }
将class文件反编译:
public final class $Proxy0 extends Proxy implements Person { private static Method m1; private static Method m3; private static Method m0; private static Method m2; //通过带InvocationHandler参数的构造器生成代理类对象时,将我们写的RenterInvocationHandler对象传进来 //父类持有:protected InvocationHandler h public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void rentHouse() throws { try { //执行代理类的rentHouse方法时,实际上是执行InvocationHandler类的invoke方法,也就是我们自己写的 //RenterInvocationHandler类的invoke方法,在此方法中我们通过method.invoke(target, args)反射,再 //执行具体类的rentHouse方法 this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); //获取到了rentHouse方法的方法名 m3 = Class.forName("test.father.Person").getMethod("rentHouse", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
java自动生成了一个$Proxy0代理类,这个类在内存中,所以可以通过反射获取这个类的构造方法,然后创建的代理类实例对象。分析这个类源码,可以知道当调用代理类对象的rentHouse方法时的大概流程为:调用RenterInvocationHandler类的invoke方法,而RenterInvocationHandler类的invoke方法中又用反射调用了被代理类的rentHouse方法。
RenterInvocationHandler可以看成是中间类,它持有被代理对象,把外部对invoke的调用转为对被代理对象的调用。而代理类通过持有中间类,调用中间类的invoke方法,来达到调用被代理类的方法的目的4、CGLIB代理
动态代理需要被代理类实现接口,如果被代理类没有实现接口,那么这么实现动态代理?这时候就需要用到CGLib了。这种代理方式就叫做CGlib代理。
Cglib代理也叫作子类代理,他是通过在内存中构建一个子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,然后加入自己需要的操作。因为使用的是继承的方式,所以不能代理final 类。
代码实现
导入cglib的jar包,另外Spring的核心包中已经包括了Cglib功能,也可以导入spring-core-3.2.5.jar
(1)创建被代理类UserServicepublic class UserService { public void getName(){ System.out.println("张三!"); } }
不能为final
(2)创建代理工厂类ProxyFactorypublic class ProxyFactory<T> implements MethodInterceptor { private T target; public ProxyFactory(T target) { this.target = target; } // 创建代理对象 public Object getProxyInstance() { // 1.cglib工具类 Enhancer en = new Enhancer(); // 2.设置父类 en.setSuperclass(this.target.getClass()); // 3.设置回调函数 en.setCallback(this); return en.create(); } //拦截方法 @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("开始事务..."); // 执行目标对象的方法 Object result = method.invoke(target, args); System.out.println("提交事务..."); return result; } }
动态代理与CGLib动态代理都是实现Spring AOP的基础。如果加入容器的目标对象有实现接口,用动态代理,如果目标对象没有实现接口,用Cglib代理。
更多相关内容 -
一个简单的java动态代理的实例
2014-05-15 19:52:24一个简单的java动态代理的实例 -
Java JDK动态代理实现原理实例解析
2020-08-19 00:41:01主要介绍了Java JDK动态代理实现原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 -
Java动态代理静态代理实例分析
2020-08-19 12:38:48主要介绍了Java动态代理静态代理实例分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 -
java 动态代理 简单实例
2011-12-24 13:52:40java 动态代理 简单实例 新手看看 -
JAVA设计模式之代理模式实例
2017-07-09 12:54:08JAVA设计模式之代理模式实例 -
java动态代理实例(jdk动态代理和cglib)
2013-08-24 09:22:52实现java动态代理的两个实例,jdk动态代理和cglib -
java动态代理实例aop
2017-04-20 21:22:29aop java -
java反射和动态代理实例
2019-04-04 01:11:16NULL 博文链接:https://changaiqing.iteye.com/blog/1487140 -
Java反射和动态代理实例
2013-08-29 21:08:14几个Java反射和动态代理的小例子。可以学习如何通过Java的反射机制实例化对象、调用对象的方法、操作对象的私有成员变量、改变数组中的某项的值、改变数组大小等;可以学习Java的动态代理模式、学习Java工厂模式以及... -
java 动态代理实例(JDK代理与CGLIB代理)
2013-08-09 14:52:30附件为java 动态代理实例,有全码,包括测试代码。 代码少,注释全。 对理解代理非常不错。 -
java动态代理实例
2009-01-21 17:37:29java动态代理实例 要想理解拦截器的知识 最好要先理解java动态代理这块 -
Java两种动态代理JDK动态代理和CGLIB动态代理
2018-08-07 15:33:35JDK动态代理 cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是...目录
代理模式
代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以增强B,在调用B的方法前后都做些其他的事情。Spring AOP就是使用了动态代理完成了代码的动态“织入”。
使用代理好处还不止这些,一个工程如果依赖另一个工程给的接口,但是另一个工程的接口不稳定,经常变更协议,就可以使用一个代理,接口变更时,只需要修改代理,不需要一一修改业务代码。从这个意义上说,所有调外界的接口,我们都可以这么做,不让外界的代码对我们的代码有侵入,这叫防御式编程。代理其他的应用可能还有很多。
上述例子中,类A写死持有B,就是B的静态代理。如果A代理的对象是不确定的,就是动态代理。动态代理目前有两种常见的实现,jdk动态代理和cglib动态代理。
JDK动态代理
jdk动态代理是jre提供给我们的类库,可以直接使用,不依赖第三方。先看下jdk动态代理的使用代码,再理解原理。
首先有个“明星”接口类,有唱、跳两个功能:
package proxy; public interface Star { String sing(String name); String dance(String name); }
再有个明星实现类“刘德华”:
package proxy; public class LiuDeHua implements Star { @Override public String sing(String name) { System.out.println("给我一杯忘情水"); return "唱完" ; } @Override public String dance(String name) { System.out.println("开心的马骝"); return "跳完" ; } }
明星演出前需要有人收钱,由于要准备演出,自己不做这个工作,一般交给一个经纪人。便于理解,它的名字以Proxy结尾,但他不是代理类,原因是它没有实现我们的明星接口,无法对外服务,它仅仅是一个wrapper。
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class StarProxy implements InvocationHandler { // 目标类,也就是被代理对象 private Object target; public void setTarget(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 这里可以做增强 System.out.println("收钱"); Object result = method.invoke(target, args); return result; } // 生成代理类 public Object CreatProxyedObj() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } }
上述例子中,方法CreatProxyedObj返回的对象才是我们的代理类,它需要三个参数,前两个参数的意思是在同一个classloader下通过接口创建出一个对象,该对象需要一个属性,也就是第三个参数,它是一个InvocationHandler。需要注意的是这个CreatProxyedObj方法不一定非得在我们的StarProxy类中,往往放在一个工厂类中。上述代理的代码使用过程一般如下:
1、new一个目标对象
2、new一个InvocationHandler,将目标对象set进去
3、通过CreatProxyedObj创建代理对象,强转为目标对象的接口类型即可使用,实际上生成的代理对象实现了目标接口。
Star ldh = new LiuDeHua(); StarProxy proxy = new StarProxy(); proxy.setTarget(ldh); Object obj = proxy.CreatProxyedObj(); Star star = (Star)obj;
Proxy(jdk类库提供)根据B的接口生成一个实现类,我们成为C,它就是动态代理类(该类型是 $Proxy+数字 的“新的类型”)。生成过程是:由于拿到了接口,便可以获知接口的所有信息(主要是方法的定义),也就能声明一个新的类型去实现该接口的所有方法,这些方法显然都是“虚”的,它调用另一个对象的方法。当然这个被调用的对象不能是对象B,如果是对象B,我们就没法增强了,等于饶了一圈又回来了。
所以它调用的是B的包装类,这个包装类需要我们来实现,但是jdk给出了约束,它必须实现InvocationHandler,上述例子中就是StarProxy, 这个接口里面有个方法,它是所有Target的所有方法的调用入口(invoke),调用之前我们可以加自己的代码增强。
看下我们的实现,我们在InvocationHandler里调用了对象B(target)的方法,调用之前增强了B的方法。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 这里增强 System.out.println("收钱"); Object result = method.invoke(target, args); return result; }
所以可以这么认为C代理了InvocationHandler,InvocationHandler代理了我们的类B,两级代理。
整个JDK动态代理的秘密也就这些,简单一句话,动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。
下面看下动态代理类到底如何调用的InvocationHandler的,为什么InvocationHandler的一个invoke方法能为分发target的所有方法。C中的部分代码示例如下,通过反编译生成后的代码查看,摘自链接地址。Proxy创造的C是自己(Proxy)的子类,且实现了B的接口,一般都是这么修饰的:
public final class XXX extends Proxy implements XXX
一个方法代码如下:
public final String SayHello(String paramString) { try { return (String)this.h.invoke(this, m4, new Object[] { paramString }); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); }
可以看到,C中的方法全部通过调用h实现,其中h就是InvocationHandler,是我们在生成C时传递的第三个参数。这里还有个关键就是SayHello方法(业务方法)跟调用invoke方法时传递的参数m4一定要是一一对应的,但是这些对我们来说都是透明的,由Proxy在newProxyInstance时保证的。留心看到C在invoke时把自己this传递了过去,InvocationHandler的invoke的第一个方法也就是我们的动态代理实例类,业务上有需要就可以使用它。(所以千万不要在invoke方法里把请求分发给第一个参数,否则很明显就死循环了)
C类中有B中所有方法的成员变量
private static Method m1; private static Method m3; private static Method m4; private static Method m2; private static Method m0;
这些变量在static静态代码块初始化,这些变量是在调用invocationhander时必要的入参,也让我们依稀看到Proxy在生成C时留下的痕迹。
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]); m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } }
从以上分析来看,要想彻底理解一个东西,再多的理论不如看源码,底层的原理非常重要。
jdk动态代理类图如下
cglib动态代理
我们了解到,“代理”的目的是构造一个和被代理的对象有同样行为的对象,一个对象的行为是在类中定义的,对象只是类的实例。所以构造代理,不一定非得通过持有、包装对象这一种方式。
通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是cglib的思想。根据里氏代换原则(LSP),父类需要出现的地方,子类可以出现,所以cglib实现的代理也是可以被正常使用的。
先看下代码
package proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CglibProxy implements MethodInterceptor { // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中 public Object CreatProxyedObj(Class<?> clazz) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { // 这里增强 System.out.println("收钱"); return arg3.invokeSuper(arg0, arg2); } }
从代码可以看出,它和jdk动态代理有所不同,对外表现上看CreatProxyedObj,它只需要一个类型clazz就可以产生一个代理对象, 所以说是“类的代理”,且创造的对象通过打印类型发现也是一个新的类型。不同于jdk动态代理,jdk动态代理要求对象必须实现接口(三个参数的第二个参数),cglib对此没有要求。
cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“$父类方法名$”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。
这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。
C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。
这里有个疑问就是intercept的四个参数,为什么我们使用的是arg3而不是arg1?
@Override public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { System.out.println("收钱"); return arg3.invokeSuper(arg0, arg2); }
因为如果我们通过反射 arg1.invoke(arg0, ...)这种方式是无法调用到父类的方法的,子类有方法重写,隐藏了父类的方法,父类的方法已经不可见,如果硬调arg1.invoke(arg0, ...)很明显会死循环。
所以调用的是cglib开头的方法,但是,我们使用arg3也不是简单的invoke,而是用的invokeSuper方法,这是因为cglib采用了fastclass机制,不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用。
fastclass基本原理是,给每个方法编号,通过编号找到方法执行避免了通过反射调用。
对比JDK动态代理,cglib依然需要一个第三者分发请求,只不过jdk动态代理分发给了目标对象,cglib最终分发给了自己,通过给method编号完成调用。cglib是继承的极致发挥,本身还是很简单的,只是fastclass需要另行理解。
参考
https://blog.csdn.net/jiankunking/article/details/52143504
http://www.php.cn/java-article-407212.html
https://www.cnblogs.com/chinajava/p/5880887.html
https://rejoy.iteye.com/blog/1627405
测试
public static void main(String[] args) { int times = 1000000; Star ldh = new LiuDeHua(); StarProxy proxy = new StarProxy(); proxy.setTarget(ldh); long time1 = System.currentTimeMillis(); Star star = (Star)proxy.CreatProxyedObj(); long time2 = System.currentTimeMillis(); System.out.println("jdk创建时间:" + (time2 - time1)); CglibProxy proxy2 = new CglibProxy(); long time5 = System.currentTimeMillis(); Star star2 = (Star)proxy2.CreatProxyedObj(LiuDeHua.class); long time6 = System.currentTimeMillis(); System.out.println("cglib创建时间:" + (time6 - time5)); long time3 = System.currentTimeMillis(); for (int i = 1; i <= times; i++) { star.sing("ss"); star.dance("ss"); } long time4 = System.currentTimeMillis(); System.out.println("jdk执行时间" + (time4 - time3)); long time7 = System.currentTimeMillis(); for (int i = 1; i <= times; i++) { star2.sing("ss"); star2.dance("ss"); } long time8 = System.currentTimeMillis(); System.out.println("cglib执行时间" + (time8 - time7)); }
经测试,jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk,所以比较适合单例模式。另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。spring默认使用jdk动态代理,如果类没有接口,则使用cglib。
-
使用java动态代理技术实现简单的AOP切面编程实例
2017-03-05 18:15:21对应的博客链接:http://blog.csdn.net/JQ_AK47/article/details/60469034#t12 -
Java动态代理
2021-09-24 10:43:00动态代理技术是整个java技术中最重要的一个技术,它是学习java框架的基础,不会动态代理技术,那么在学习Spring这些框架时是学不明白的。 动态代理技术就是用来产生一个对象的代理对象的。在开发中为什么需要为一...一、代理的概念
动态代理技术是整个java技术中最重要的一个技术,它是学习java框架的基础,不会动态代理技术,那么在学习Spring这些框架时是学不明白的。
动态代理技术就是用来产生一个对象的代理对象的。在开发中为什么需要为一个对象产生代理对象呢?
举一个现实生活中的例子:歌星或者明星都有一个自己的经纪人,这个经纪人就是他们的代理人,当我们需要找明星表演时,不能直接找到该明星,只能是找明星的代理人。比如刘德华在现实生活中非常有名,会唱歌,会跳舞,会拍戏,刘德华在没有出名之前,我们可以直接找他唱歌,跳舞,拍戏,刘德华出名之后,他干的第一件事就是找一个经纪人,这个经纪人就是刘德华的代理人(代理),当我们需要找刘德华表演时,不能直接找到刘德华了(刘德华说,你找我代理人商谈具体事宜吧!),只能是找刘德华的代理人,因此刘德华这个代理人存在的价值就是拦截我们对刘德华的直接访问!
这个现实中的例子和我们在开发中是一样的,我们在开发中之所以要产生一个对象的代理对象,主要用于拦截对真实业务对象的访问。那么代理对象应该具有什么方法呢?代理对象应该具有和目标对象相同的方法。所以在这里明确代理对象的两个概念:
1、代理对象存在的价值主要用于拦截对真实业务对象的访问。
2、代理对象应该具有和目标对象(真实业务对象)相同的方法。刘德华(真实业务对象)会唱歌,会跳舞,会拍戏,我们现在不能直接找他唱歌,跳舞,拍戏了,只能找他的代理人(代理对象)唱歌,跳舞,拍戏,一个人要想成为刘德华的代理人,那么他必须具有和刘德华一样的行为(会唱歌,会跳舞,会拍戏),刘德华有什么方法,他(代理人)就要有什么方法,我们找刘德华的代理人唱歌,跳舞,拍戏,但是代理人不是真的懂得唱歌,跳舞,拍戏的,真正懂得唱歌,跳舞,拍戏的是刘德华,在现实中的例子就是我们要找刘德华唱歌,跳舞,拍戏,那么只能先找他的经纪人,交钱给他的经纪人,然后经纪人再让刘德华去唱歌,跳舞,拍戏。
二、java中的代理
使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过代理类来调用目标方法,代理类会将所有的方法调用,分派到目标对象上反射执行,还可以在分派过程中添加"前置通知"和后置处理(是在invoke方法中添加,如在调用目标方法前校验权限,在调用完目标方法后打印日志等)等功能。
使用动态代理的五大步骤1.通过实现InvocationHandler接口来自定义自己的InvocationHandler(主要是编写invoke方法);
2.通过Proxy.getProxyClass获得动态代理类
3.通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)
4.通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入5.通过代理对象调用目标方法
2.1、"java.lang.reflect.Proxy"类介绍
现在要生成某一个对象的代理对象,这个代理对象通常也要编写一个类来生成,所以首先要编写用于生成代理对象的类。在java中如何用程序去生成一个对象的代理对象呢,java在JDK1.5之后提供了一个"java.lang.reflect.Proxy"类,通过"Proxy"类提供的一个newProxyInstance方法用来创建一个对象的代理对象,如下所示:static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
newProxyInstance方法用来返回一个代理对象,这个方法总共有3个参数:
ClassLoader loader:用来指明生成代理对象使用哪个类装载器 Class<?>[]
interfaces:用来指明生成哪个对象的代理对象(代理类和目标类实现相同的接口) InvocationHandler h :
用来指明产生的这个代理对象要做什么事情。(这个InvocationHandler 就是我们自己定义的InvocationHandler
对象,里边有我们编写invok方法)newProxyInstance方法生成代理对象过程:
a. 通过Proxy.getProxyClass(ProxyGenerator.generateProxyClass(proxyName, interfaces);)获得动态代理类的class字节码内容。
b.把字节码通过传入的类加载器加载到虚拟机中,然后通过反射机制获得代理类的构造方法(方法签名为getConstructor(InvocationHandler.class)),生成代理类对象public final void dance(String paramString) { try { h.invoke(this, m4, new Object[] { paramString }); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void sing(String paramString) { try { h.invoke(this, m3, new Object[] { paramString }); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } /// static{ m3 = Class.forName("com.sun.proxy.$Proxy0").getMethod("sing", new Class[] { Class.forName("java.lang.String") }); m4 = Class.forName("com.sun.proxy.$Proxy0").getMethod("dance", new Class[] { Class.forName("java.lang.String") }); m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m6 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getInvocationHandler", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m7 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getProxyClass", new Class[] { Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;") }); m12 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getClass", new Class[0]); m14 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notifyAll", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m9 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", new Class[0]); m13 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notify", new Class[0]); m8 = Class.forName("com.sun.proxy.$Proxy0").getMethod("newProxyInstance", new Class[] { Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"), Class.forName("java.lang.reflect.InvocationHandler") }); m11 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", new Class[] { Long.TYPE }); m5 = Class.forName("com.sun.proxy.$Proxy0").getMethod("isProxyClass", new Class[] { Class.forName("java.lang.Class") }); m10 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", new Class[] { Long.TYPE, Integer.TYPE }); }
dance(String paramString) 与之相呼应:代理类和目标类实现相同的接口
h.invoke(this, m4, new Object[] { paramString }); //m4:代理类的方法,和目标类的方法,一一对应。
m4 = Class.forName(“com.sun.proxy.$Proxy0”).getMethod(“dance”, newClass[] { Class.forName(“java.lang.String”) }); 与之相呼应: 这里调用的是我们自定义的InvocationHandler 对象h的invoke方法,所以就实现了可以拦截、添加前置后置处理。这也说明了代理逻辑 和 动态代理本身是代码分离的,程序员只需要关注好自己的代理逻辑就行,动态代理本身就交给jdk本身去处理。在jdk动态代理中,美中不足就是整个设计都是针对接口做的代理,如果是普通的类,我们无法通过这个方式代理对象(通过生成的代理类也知道没有接口是不行的),但是我们知道 通过拼接字节码生成新的类自由度是十分大的,这也就启示我们 设计不管是针对接口类还是普通类的代理类 是完全可行的,比如cglib框架就是通过拼接字节码来实现非接口类的代理。 后面会介绍如何 实现这种操作,我在自己的Simplify-Core项目中已经尝试通过asm框架(字节码读写框架)实现代理操作。
-
计算机后端-Java-Java核心基础-第30章 动态代理与Java8新特性 20. Stream的实例化.avi
2022-05-23 00:25:49计算机后端-Java-Java核心基础-第30章 动态代理与Java8新特性 20. Stream的实例化.avi -
Java JDK 动态代理(AOP)使用及实现原理分析
2019-05-08 21:28:06二、Java 动态代理类 三、JDK的动态代理怎么使用? 四、动态代理怎么实现的? 五、结论 一、什么是代理? 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为...目录
一、什么是代理?
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
代理模式UML图:
简单结构示意图:
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。
二、Java 动态代理类
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
(1)Interface InvocationHandler:该接口中仅定义了一个方法
public object invoke(Object obj,Method method, Object[] args)
在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。
(2)Proxy:该类即为动态代理类,其中主要包含以下内容:
protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。
static Class getProxyClass (ClassLoaderloader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
所谓DynamicProxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个DynamicProxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
在使用动态代理类时,我们必须实现InvocationHandler接口
通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。
动态代理步骤:
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
4.通过代理调用方法三、JDK的动态代理怎么使用?
1、需要动态代理的接口:
package jiankunking; /** * 需要动态代理的接口 */ public interface Subject { /** * 你好 * * @param name * @return */ public String SayHello(String name); /** * 再见 * * @return */ public String SayGoodBye(); }
2、需要代理的实际对象
package jiankunking; /** * 实际对象 */ public class RealSubject implements Subject { /** * 你好 * * @param name * @return */ public String SayHello(String name) { return "hello " + name; } /** * 再见 * * @return */ public String SayGoodBye() { return " good bye "; } }
3、调用处理器实现类(有木有感觉这里就是传说中的AOP啊)
package jiankunking; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 调用处理器实现类 * 每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象 */ public class InvocationHandlerImpl implements InvocationHandler { /** * 这个就是我们要代理的真实对象 */ private Object subject; /** * 构造方法,给我们要代理的真实对象赋初值 * * @param subject */ public InvocationHandlerImpl(Object subject) { this.subject = subject; } /** * 该方法负责集中处理动态代理类上的所有方法调用。 * 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行 * * @param proxy 代理类实例 * @param method 被调用的方法对象 * @param args 调用参数 * @return * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在代理真实对象前我们可以添加一些自己的操作 System.out.println("在调用之前,我要干点啥呢?"); System.out.println("Method:" + method); //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用 Object returnValue = method.invoke(subject, args); //在代理真实对象后我们也可以添加一些自己的操作 System.out.println("在调用之后,我要干点啥呢?"); return returnValue; } }
4、测试
package jiankunking; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * 动态代理演示 */ public class DynamicProxyDemonstration { public static void main(String[] args) { //代理的真实对象 Subject realSubject = new RealSubject(); /** * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用. * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法 */ InvocationHandler handler = new InvocationHandlerImpl(realSubject); ClassLoader loader = realSubject.getClass().getClassLoader(); Class[] interfaces = realSubject.getClass().getInterfaces(); /** * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 */ Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler); System.out.println("动态代理对象的类型:"+subject.getClass().getName()); String hello = subject.SayHello("jiankunking"); System.out.println(hello); // String goodbye = subject.SayGoodBye(); // System.out.println(goodbye); } }
5、输出结果如下:
演示demo下载地址:javajdk动态代理演示demo_jdk动态代理-Java代码类资源-CSDN下载
四、动态代理怎么实现的?
从使用代码中可以看出,关键点在:
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
通过跟踪提示代码可以看出:当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用。
也就是说,当代码执行到:
subject.SayHello("jiankunking")这句话时,会自动调用InvocationHandlerImpl的invoke方法。这是为啥呢?
==============横线之间的是代码跟分析的过程,不想看的朋友可以直接看结论===================
以下代码来自:JDK1.8.0_92
既然生成代理对象是用的Proxy类的静态方newProxyInstance,那么我们就去它的源码里看一下它到底都做了些什么?
/** * Returns an instance of a proxy class for the specified interfaces * that dispatches method invocations to the specified invocation * handler. * * <p>{@code Proxy.newProxyInstance} throws * {@code IllegalArgumentException} for the same reasons that * {@code Proxy.getProxyClass} does. * * @param loader the class loader to define the proxy class * @param interfaces the list of interfaces for the proxy class * to implement * @param h the invocation handler to dispatch method invocations to * @return a proxy instance with the specified invocation handler of a * proxy class that is defined by the specified class loader * and that implements the specified interfaces * @throws IllegalArgumentException if any of the restrictions on the * parameters that may be passed to {@code getProxyClass} * are violated * @throws SecurityException if a security manager, <em>s</em>, is present * and any of the following conditions is met: * <ul> * <li> the given {@code loader} is {@code null} and * the caller's class loader is not {@code null} and the * invocation of {@link SecurityManager#checkPermission * s.checkPermission} with * {@code RuntimePermission("getClassLoader")} permission * denies access;</li> * <li> for each proxy interface, {@code intf}, * the caller's class loader is not the same as or an * ancestor of the class loader for {@code intf} and * invocation of {@link SecurityManager#checkPackageAccess * s.checkPackageAccess()} denies access to {@code intf};</li> * <li> any of the given proxy interfaces is non-public and the * caller class is not in the same {@linkplain Package runtime package} * as the non-public interface and the invocation of * {@link SecurityManager#checkPermission s.checkPermission} with * {@code ReflectPermission("newProxyInPackage.{package name}")} * permission denies access.</li> * </ul> * @throws NullPointerException if the {@code interfaces} array * argument or any of its elements are {@code null}, or * if the invocation handler, {@code h}, is * {@code null} */ @CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //检查h 不为空,否则抛异常 Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * 获得与指定类装载器和一组接口相关的代理类类型对象 */ Class<?> cl = getProxyClass0(loader, intfs); /* * 通过反射获取构造函数对象并生成代理类实例 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } //获取代理对象的构造方法(也就是$Proxy0(InvocationHandler h)) final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } //生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法 return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
我们再进去getProxyClass0方法看一下:
/** * Generate a proxy class. Must call the checkProxyAccess method * to perform permission checks before calling this. */ private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache.get(loader, interfaces); }
真相还是没有来到,继续,看一下 proxyClassCache
/** * a cache of proxy classes */ private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
奥,原来用了一下缓存啊
那么它对应的get方法啥样呢?
/** * Look-up the value through the cache. This always evaluates the * {@code subKeyFactory} function and optionally evaluates * {@code valueFactory} function if there is no entry in the cache for given * pair of (key, subKey) or the entry has already been cleared. * * @param key possibly null key * @param parameter parameter used together with key to create sub-key and * value (should not be null) * @return the cached value (never null) * @throws NullPointerException if {@code parameter} passed in or * {@code sub-key} calculated by * {@code subKeyFactory} or {@code value} * calculated by {@code valueFactory} is null. */ public V get(K key, P parameter) { Objects.requireNonNull(parameter); expungeStaleEntries(); Object cacheKey = CacheKey.valueOf(key, refQueue); // lazily install the 2nd level valuesMap for the particular cacheKey ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); if (valuesMap == null) { //putIfAbsent这个方法在key不存在的时候加入一个值,如果key存在就不放入 ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); if (oldValuesMap != null) { valuesMap = oldValuesMap; } } // create subKey and retrieve the possible Supplier<V> stored by that // subKey from valuesMap Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { // supplier might be a Factory or a CacheValue<V> instance V value = supplier.get(); if (value != null) { return value; } } // else no supplier in cache // or a supplier that returned null (could be a cleared CacheValue // or a Factory that wasn't successful in installing the CacheValue) // lazily construct a Factory if (factory == null) { factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { // successfully installed Factory supplier = factory; } // else retry with winning supplier } else { if (valuesMap.replace(subKey, supplier, factory)) { // successfully replaced // cleared CacheEntry / unsuccessful Factory // with our Factory supplier = factory; } else { // retry with current supplier supplier = valuesMap.get(subKey); } } } }
我们可以看到它调用了 supplier.get(); 获取动态代理类,其中supplier是Factory,这个类定义在WeakCach的内部。
来瞅瞅,get里面又做了什么?
public synchronized V get() { // serialize access // re-check Supplier<V> supplier = valuesMap.get(subKey); if (supplier != this) { // something changed while we were waiting: // might be that we were replaced by a CacheValue // or were removed because of failure -> // return null to signal WeakCache.get() to retry // the loop return null; } // else still us (supplier == this) // create new value V value = null; try { value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { if (value == null) { // remove us on failure valuesMap.remove(subKey, this); } } // the only path to reach here is with non-null value assert value != null; // wrap value with CacheValue (WeakReference) CacheValue<V> cacheValue = new CacheValue<>(value); // try replacing us with CacheValue (this should always succeed) if (valuesMap.replace(subKey, this, cacheValue)) { // put also in reverseMap reverseMap.put(cacheValue, Boolean.TRUE); } else { throw new AssertionError("Should not reach here"); } // successfully replaced us with new CacheValue -> return the value // wrapped by it return value; } }
发现重点还是木有出现,但我们可以看到它调用了valueFactory.apply(key, parameter)方法:
/** * A factory function that generates, defines and returns the proxy class given * the ClassLoader and array of interfaces. */ private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // prefix for all proxy class names private static final String proxyClassNamePrefix = "$Proxy"; // next number to use for generation of unique proxy class names private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } /* * Verify that the Class object actually represents an * interface. */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } } }
通过看代码终于找到了重点:
//生成字节码 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
那么接下来我们也使用测试一下,使用这个方法生成的字节码是个什么样子:
package jiankunking; import sun.misc.ProxyGenerator; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * 动态代理演示 */ public class DynamicProxyDemonstration { public static void main(String[] args) { //代理的真实对象 Subject realSubject = new RealSubject(); /** * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用. * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法 */ InvocationHandler handler = new InvocationHandlerImpl(realSubject); ClassLoader loader = handler.getClass().getClassLoader(); Class[] interfaces = realSubject.getClass().getInterfaces(); /** * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 */ Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler); System.out.println("动态代理对象的类型:"+subject.getClass().getName()); String hello = subject.SayHello("jiankunking"); System.out.println(hello); // 将生成的字节码保存到本地, createProxyClassFile(); } private static void createProxyClassFile(){ String name = "ProxySubject"; byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Subject.class}); FileOutputStream out =null; try { out = new FileOutputStream(name+".class"); System.out.println((new File("hello")).getAbsolutePath()); out.write(data); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if(null!=out) try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }
可以看一下这里代理对象的类型:
我们用jd-jui 工具将生成的字节码反编译:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import jiankunking.Subject; public final class ProxySubject extends Proxy implements Subject { private static Method m1; private static Method m3; private static Method m4; private static Method m2; private static Method m0; public ProxySubject(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final boolean equals(Object paramObject) { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String SayGoodBye() { try { return (String)this.h.invoke(this, m3, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String SayHello(String paramString) { try { return (String)this.h.invoke(this, m4, new Object[] { paramString }); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]); m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
这就是最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口,也就是说:
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
这里的subject实际是这个类的一个实例,那么我们调用它的:
public final String SayHello(String paramString)
就是调用我们定义的InvocationHandlerImpl的 invoke方法:
======================横线之间的是代码跟分析的过程,不想看的朋友可以直接看结论=====================================
五、结论
到了这里,终于解答了:
subject.SayHello("jiankunking")这句话时,为什么会自动调用InvocationHandlerImpl的invoke方法?
因为JDK生成的最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口,
在实现Subject接口方法的内部,通过反射调用了InvocationHandlerImpl的invoke方法。
包含生成本地class文件的demo:
javajdk动态代理演示demo2-Java代码类资源-CSDN下载
GitHub - jiankunking/DynamicProxyDemo: java jdk 动态代理
通过分析代码可以看出Java 动态代理,具体有如下四步骤:- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
个人微信公众号:
作者:jiankunking 出处:衣舞晨风的博客_CSDN博客-C#,Java,Sql Server领域博主
本文参考过:
-
动态代理原理实例Demo
2013-12-29 11:05:08java动态代理原理剖析的demo,其中诠释了动态代理的原理,并有对应的实际应用。 -
Java编程中使用动态代理实现AOP功能(附项目设计实例)
2013-08-29 00:40:00本文详细介绍了Java编程中使用动态代理实现AOP功能,AOP是OOP的延续,意思是面向切面编程。 从这篇文章可以大体理解spring AOP的原理。 -
Javaweb安全学习——Java动态代理
2022-04-05 02:10:38Java 动态代理 Java的java.lang.reflect包下提供了一个Proxy类和InvocationHandler接口,可以生成JDK动态代理类或对象来完成程序无侵入式扩展(即不通过继承接口编写实现类来完成功能拓展)。 Java动态代理主要使用... -
Java中动态代理的介绍及实例
2012-02-07 11:37:18Word格式的Java中动态代理的介绍及实例,有详细注释。 -
Java实现动态代理示例
2018-08-15 10:38:16首先定义一个接口 ...本文中的动态代理是通过JDK动态代理机制实现的,必须要先实现业务接口,即IStar接口;然后根据反射机制找到对应的被代理的类,就可以在动态生成的代理类中调用业务实现类的同名方法。 -
Java 注解+动态代理的一个实例
2011-09-22 09:01:02学习下动态代理模式(关于代理的基础只是有很多帖子都有我就不重复了),做了个注解动态代理的例子, 给那些学习注解或动态代理的初学者。 -
Java面向对象系列[v1.0.0][使用反射生成动态代理]
2020-12-20 21:35:00在Java的java.lang.reflect包里有个Proxy类和一个InvocationHandler接口,通过使用他们可以生成JDK动态代理类或动态代理对象 使用Proxy和InvocationHandler创建动态代理 Proxy提供了用于创建动态代理类和代理对象的... -
Java动态代理简单实例:老板与秘书
2017-02-26 22:38:41Java动态代理是Java中比较晦涩难懂的一个部分,虽然看了一些别人的博客之后觉得自己懂一点了,但是事非经过不知难,自己写的时候发现了一些之前没在意的点。 本文用尽可能简单的例子来说明Java动态代理的机制。 例子... -
Java静态代理和动态代理实例及区别说明
2019-09-21 21:13:291 什么是代理 代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。举个例子,如A对象有若干个方法,这时A对象对B对象进行委托授权,B对象便成了A对象的代理方,因此B对象便可对A对象进行访问并调用A... -
678.676.JAVA基础教程_动态代理与Java8新特性-Stream的实例化(678).rar
2021-11-09 10:59:49678.676.JAVA基础教程_动态代理与Java8新特性-Stream的实例化(678).rar -
轻松学,Java 中的代理模式及动态代理
2017-06-29 22:08:55前几天我写了《秒懂,Java 注解 (Annotation)你可以这样学》,因为注解其实算反射技术中的一部分,然后我想了一下,反射技术中还有个常见的概念就是动态代理,于是索性再写一篇关于动态代理的博文好了。... -
java设计模式(工厂模式,动态代理,责任链模式……)实例源码
2012-11-14 01:37:08java设计模式(工厂模式,动态代理,责任链模式……)实例源码 -
Java动态代理ReflectProxyDemo
2015-12-11 17:00:59Java基于反射机制写的动态代理操作实例。