定义一个公共接口和公共方法,java
package proxy; public interface Work { void sayHello(); String getName(); }
建立一个被代理类,实现公共接口和方法;jvm
package proxy; public class People implements Work { @Override public void sayHello() { System.out.println("my name is A"); } @Override public String getName() { return "A"; } }
建立一个代理类,实现公共接口和方法,这样它们就具备了相同的方法。 ide
代理类中,须要持有一个被代理类对象,能够当作代理类的成员变量,在代理类的构造方法中传入并赋值。函数
package proxy; public class StaticProxyPeople implements Work { People A = new People(); public StaticProxyPeople(People A) { this.A = A; } @Override public void sayHello() { System.out.println("代理方法开始:"); A.sayHello(); System.out.println("代理方法结束。"); } @Override public String getName() { String name = A.getName(); name = "代理处理过的name:" + name; return name; } }
此时代理类和被代理类拥有相同的方法,而后在代理类的方法中,能够调用被代理类的方法,达到操做被代理类的目的。而在调用方法先后,又能够定义本身的特殊逻辑。性能
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { People people = new People(); StaticProxyPeople staticProxyPeople = new StaticProxyPeople(people); staticProxyPeople.sayHello(); System.out.println(staticProxyPeople.getName()); } } 输出结果: 代理方法开始: my name is A 代理方法结束。 代理处理过的name:A
这样子当外界但愿调用被代理类的方法时,能够建立一个代理类的对象,经过对代理类对象的方法的调用,达到操做被代理类对象的目的。this
缺点:一个代理类,只能针对一个被代理类进行代理。spa
在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler接口、另外一个则是Proxyle类,这一个类和接口是实现咱们动态代理所必须用到的。代理
一、InvocationHandlercode
举例:对象
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class WrokHandler implements InvocationHandler { private Object subject; // 这个就是咱们要代理的真实对象,也就是真正执行业务逻辑的类 public WrokHandler(Object subject) { this.subject = subject; } /** * proxy: 指代JDK动态生成的最终代理对象 * method: 指代的是咱们所要调用真实对象的某个方法的Method对象 * args: 指代的是调用真实对象某个方法时接受的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("调用方法:" + method.getName()+ "以前"); // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用 result = method.invoke(subject, args);// 须要指定被代理对象和传入参数 System.out.println("返回值:" + result); System.out.println("调用方法:" + method.getName()+ "以后"); return result; } } package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class SleepHandler implements InvocationHandler { private Object subject; // 这个就是咱们要代理的真实对象,也就是真正执行业务逻辑的类 public SleepHandler(Object subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("SleepHandler.invoke 调用方法:" + method.getName() + "以前"); // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用 result = method.invoke(subject, args);// 须要指定被代理对象和传入参数 System.out.println("SleepHandler.invoke 返回值:" + result); System.out.println("SleepHandler.invoke 调用方法:" + method.getName() + "以后"); return result; } }
解析:
每个代理类都必需要实现InvocationHandler这个接口,
同时,也要持有一个被代理类真实对象。
代理类实现了InvocationHandler接口后,会重写invoke方法。
三个参数分别是:
proxy: 指代JDK动态生成的最终代理对象
method: 指代的是咱们所要调用真实对象的某个方法的Method对象
args: 指代的是调用真实对象某个方法时接受的参数
而在invoke方法中,咱们经过method.invoke(subject, args); 来调用真实被代理对象的方法。
简单来讲,就是当定义了以上WorkHandler后,对代理类对象方法的调用,都会由invoke方法来处理,才会传达到被代理对象。
2.Proxy
举例:
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { // 被代理的对象 People A = new People(); // 建立代理对象,代理对象持有一个被代理对象 InvocationHandler handler = new WrokHandler(A); // 得到被代理类的,类加载器Classloader // 做用: // 负责将 Class 加载到 JVM 中 // 审查每一个类由谁加载(父优先的等级加载机制) // 将 Class 字节码从新解析成 JVM 统一要求的对象格式 ClassLoader loader = A.getClass().getClassLoader(); // 得到被代理类 实现的全部接口 Class[] interfaces = A.getClass().getInterfaces(); // 须要指定类装载器、一组接口及调用处理器生成动态代理类实例 Work proxy = (Work) Proxy.newProxyInstance(loader, interfaces, handler); System.out.println("动态代理对象的类型:" + proxy.getClass().getName()); proxy.sayHello(); System.out.print(proxy.getName()); InvocationHandler handler1 = new SleepHandler(A); Sleep proxy1 = (Sleep) Proxy.newProxyInstance(loader, interfaces, handler1); System.out.println("动态代理对象的类型:" + proxy.getClass().getName()); proxy1.goToSleep(); proxy1.getUp(); } } 输出结果: 动态代理对象的类型:$Proxy0 调用方法:sayHello以前 my name is A 返回值:null 调用方法:sayHello以后 调用方法:getName以前 返回值:A 调用方法:getName以后 A动态代理对象的类型:$Proxy0 SleepHandler.invoke 调用方法:goToSleep以前 i am go to sleep SleepHandler.invoke 返回值:null SleepHandler.invoke 调用方法:goToSleep以后 SleepHandler.invoke 调用方法:getUp以前 i am get up SleepHandler.invoke 返回值:null SleepHandler.invoke 调用方法:getUp以后
解析:
代理类一样须要持有一个真实代理对象
当咱们但愿建立一个代理类实例时,须要调用 Proxy.newProxyInstance(loader, interfaces, handler);方法
三个参数:
loader 被代理对象的类加载器,这里存有真实对象的父类,子类关系,和其余类的优先加载关系等等。
interfaces 被代理对象实现的全部接口,JDK提供的动态代理,说白了,就是让代理类也去实现被代理类的全部接口,重写被代理类的全部方法来实现的。
handler 咱们自定义的代理类实例,它实现了InvocationHandler接口。用来处理Proxy中全部方法的调用
首先解释一下JDK动态代理的大体流程,JDK主要会作如下工做:
整理一下逻辑:
代理类实例建立以前,须要传入一个自定义的handler实例,而自定义的handler类须要实现InvocationHandler接口,重写invoke方法,在重写的invoke方法中,增长自定义逻辑。
代理类实例建立时,是Proxy.newInstance(loader, interface[], handler)方法根据传入的被代理类的类加载器信息,实现的全部接口,在jvm中动态的生成字节码文件和.class文件。
代理类实例建立后,又须要强转类型,向上造型为被代理类的某个接口类型,强制转换的是被代理类实现的某个接口类型,就能够调用这个接口的方法。若是强转的类型不是被代理类实现过的,就会有异常。
问题:
JDK是如何生成最终的代理类的,其实最根本的是经过动态生成代理类的字节码,而后经过这些字节码实现代理类的生成。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); public static byte[] generateProxyClass(final String name, Class[] interfaces) { ProxyGenerator gen = new ProxyGenerator(name, interfaces); // 这里动态生成代理类的字节码 final byte[] classFile = gen.generateClassFile(); // 若是saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上 if (saveGeneratedFiles) { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() { public Void run() { try { FileOutputStream file = new FileOutputStream(dotToSlash(name) + ".class"); file.write(classFile); file.close(); return null; } catch (IOException e) { throw new InternalError( "I/O exception saving generated file: " + e); } } }); } // 返回代理类的字节码 return classFile; }
代理类实例是怎么调用hanler的invoke方法的?咱们来看一下代理类的class文件
public final void hello(String paramString) { try { this.h.invoke(this, m3, new Object[] { paramString }); //就是调用咱们自定义的InvocationHandlerImpl的 invoke方法: return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }
注意这里的this.h.invoke中的h,它是类Proxy中的一个属性
protected InvocationHandler h;
由于这个代理类继承了Proxy,因此也就继承了这个属性,而这个属性值就是咱们定义的
InvocationHandler handler = new InvocationHandlerImpl(realSubject);
同时咱们还发现,invoke方法的第一参数在底层调用的时候传入的是this,也就是最终生成的代理对象ProxySubject,这是JVM本身动态生成的,而不是咱们本身定义的代理对象。
JDK的动态代理经过ProxyGenerator.generateProxyClass()生成代理类的字节码,由Proxy.defineClass0()加载,能够看到这个代理类 extends Proxy implements Interface1, ... {...}。
而在代理类中,与Proxy有关的只有Proxy的变量protected InvocationHandler h;这个InvocationHandler实例的引用,在调用接口方法时实际调用的是super.h.invoke(this, method, args)。
缺点:使用JDK动态代理,目标类必须实现的某个接口,若是某个类没有实现接口则不能生成代理对象。
(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它能够在运行期扩展Java类与实现Java接口,通俗说cglib能够在运行时动态生成字节码。
使用cglib完成动态代理,大概的原理是:cglib继承被代理的类,重写方法,织入通知,动态生成字节码并运行,由于是继承因此final类和方法是没有办法动态代理的。具体实现以下
建立被代理类:
package proxy; public class Person { public void sayHello() { System.out.println("my name is Person"); } public String getName() { return "Person"; } }
建立代理类:
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 CglibProxyIntercepter implements MethodInterceptor { /* * sub:cglib生成的代理对象,method:被代理对象方法,objects:方法入参,methodProxy:代理方法 */ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("执行前..."); // 调用方法 Object result = methodProxy.invokeSuper(proxy, args); System.out.println("执行后..."); return result; } // 返回目标对象的代理对象 public Object newProxy(Object target) { // 加强类,用于生成代理类的class文件 Enhancer enhancer = new Enhancer(); // 设置代理类的父类为被代理类 enhancer.setSuperclass(target.getClass()); // 设置代理类的回调方法,即这里指定了哪一个类,就会执行哪一个类的intercept方法 enhancer.setCallback(this); // 设置被代理类的加载器,保证生成类的顺序和关系 enhancer.setClassLoader(target.getClass().getClassLoader()); // 生成代理类并返回实例 return enhancer.create(); } }
解析:
全部的代理类,都须要实现MethodIntercpet接口,重写 intercept()方法,在方法中编写自定义逻辑。
且经过methodProxy.invokeSuper()方法执行被代理类方法。
Enhancer是一个加强类,经过设置被代理类为superClass,并指定由哪一个方法拦截器来成为回调函数callBack,在调用creat()方法,动态生成代理类实例。
public class Test { public static void main(String[] args) { Person test = new Person(); CglibProxyIntercepter methodIntercpt= new CglibProxyIntercepter(); Person proxy2 = (Person) methodIntercpt.newProxy(test); proxy2.sayHello(); proxy2.getName(); } } 输出结果: 执行前... my name is Person 执行后... 执行前... 执行后...
代理类实例也须要强制转型,才可使用。能够转型为被代理类类型,和被代理类的接口类型,父类型。由于代理类是被代理类的子类。
下面的源码是粘贴别人的,咱们来看一下,生成一个代理类会生成三个class文件
public class PersonService$$EnhancerByCGLIB$$eaaaed75 extends PersonService implements Factory { private boolean CGLIB$BOUND; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0;//拦截器 private static final Method CGLIB$setPerson$0$Method;//被代理方法 private static final MethodProxy CGLIB$setPerson$0$Proxy;//代理方法 private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$finalize$1$Method; private static final MethodProxy CGLIB$finalize$1$Proxy; private static final Method CGLIB$equals$2$Method; private static final MethodProxy CGLIB$equals$2$Proxy; private static final Method CGLIB$toString$3$Method; private static final MethodProxy CGLIB$toString$3$Proxy; private static final Method CGLIB$hashCode$4$Method; private static final MethodProxy CGLIB$hashCode$4$Proxy; private static final Method CGLIB$clone$5$Method; private static final MethodProxy CGLIB$clone$5$Proxy; static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class localClass1 = Class.forName("com.demo.proxy.cglib.PersonService$$EnhancerByCGLIB$$eaaaed75");//代理类 Class localClass2;//被代理类PersionService Method[] tmp95_92 = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$finalize$1$Method = tmp95_92[0]; CGLIB$finalize$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$1"); Method[] tmp115_95 = tmp95_92; CGLIB$equals$2$Method = tmp115_95[1]; CGLIB$equals$2$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2"); Method[] tmp135_115 = tmp115_95; CGLIB$toString$3$Method = tmp135_115[2]; CGLIB$toString$3$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3"); Method[] tmp155_135 = tmp135_115; CGLIB$hashCode$4$Method = tmp155_135[3]; CGLIB$hashCode$4$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$4"); Method[] tmp175_155 = tmp155_135; CGLIB$clone$5$Method = tmp175_155[4]; CGLIB$clone$5$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5"); tmp175_155; Method[] tmp223_220 = ReflectUtils.findMethods(new String[] { "setPerson", "()V" }, (localClass2 = Class.forName("com.demo.proxy.cglib.PersonService")).getDeclaredMethods()); CGLIB$setPerson$0$Method = tmp223_220[0]; CGLIB$setPerson$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "setPerson", "CGLIB$setPerson$0"); tmp223_220; return; }
咱们经过代理类的源码能够看到,代理类会得到全部在父类继承来的方法,而且会有MethodProxy与之对应,好比 Method CGLIB$setPerson$0$Method、MethodProxy CGLIB$setPerson$0$Proxy;
//代理方法(methodProxy.invokeSuper会调用) final void CGLIB$setPerson$0() { super.setPerson(); } //被代理方法(methodProxy.invoke会调用,这就是为何在拦截器中调用methodProxy.invoke会死循环,一直在调用拦截器) public final void setPerson() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { //调用拦截器 var10000.intercept(this, CGLIB$setPerson$0$Method, CGLIB$emptyArgs, CGLIB$setPerson$0$Proxy); } else { super.setPerson(); } }
调用过程:代理对象调用this.setPerson方法->调用拦截器->methodProxy.invokeSuper->CGLIB$setPerson$0->被代理对象setPerson方法