原创做品,能够转载,可是请标注出处地址http://www.cnblogs.com/V1haoge/p/5860749.htmlhtml
一、动态代理(Dynamic Proxy)
代理分为静态代理和动态代理,静态代理是在编译时就将接口、实现类、代理类一古脑儿所有手动完成,但若是咱们须要不少的代理,每个都这么手动的去建立实属浪费时间,并且会有大量的重复代码,此时咱们就能够采用动态代理,动态代理能够在程序运行期间根据须要动态的建立代理类及其实例,来完成具体的功能。
其实方法直接调用就能够完成功能,为何还要加个代理呢?
缘由是采用代理模式能够有效的将具体的实现与调用方进行解耦,经过面向接口进行编码彻底将具体的实现隐藏在内部。
二、代理实现的通常模式
其实代理的通常模式就是静态代理的实现模式:首先建立一个接口(JDK代理都是面向接口的),而后建立具体实现类来实现这个接口,在建立一个代理类一样实现这个接口,不一样之处在于,具体实现类的方法中须要将接口中定义的方法的业务逻辑功能实现,而代理类中的方法只要调用具体类中的对应方法便可,这样咱们在须要使用接口中的某个方法的功能时直接调用代理类的方法便可,将具体的实现类隐藏在底层。
第一步:定义总接口Iuser.javajava
1 package ceshi1; 2 public interface Iuser { 3 void eat(String s); 4 }
第二步:建立具体实现类UserImpl.java编程
1 package ceshi1; 2 public class UserImpl implements Iuser { 3 @Override 4 public void eat(String s) { 5 System.out.println("我要吃"+s); 6 } 7 }
第三步:建立代理类UserProxy.java数组
1 package ceshi1; 2 public class UserProxy implements Iuser { 3 private Iuser user = new UserImpl(); 4 @Override 5 public void eat(String s) { 6 System.out.println("静态代理前置内容"); 7 user.eat(s); 8 System.out.println("静态代理后置内容"); 9 } 10 }
第四步:建立测试类ProxyTest.javaide
1 package ceshi1; 2 public class ProxyTest { 3 public static void main(String[] args) { 4 UserProxy proxy = new UserProxy(); 5 proxy.eat("苹果"); 6 } 7 }
运行结果:测试
1 静态代理前置内容 2 我要吃苹果 3 静态代理后置内容
三、JDK动态代理的实现
JDK动态代理的思惟模式与以前的通常模式是同样的,也是面向接口进行编码,建立代理类将具体类隐藏解耦,不一样之处在于代理类的建立时机不一样,动态代理须要在运行时因需实时建立。
第一步:定义总接口Iuser.javathis
1 package ceshi1; 2 public interface Iuser { 3 void eat(String s); 4 }
第二步:建立具体实现类UserImpl.java编码
1 package ceshi1; 2 public class UserImpl implements Iuser { 3 @Override 4 public void eat(String s) { 5 System.out.println("我要吃"+s); 6 } 7 }
第三步:建立实现InvocationHandler接口的代理类spa
1 package ceshi1; 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Method; 4 public class DynamicProxy implements InvocationHandler { 5 private Object object;//用于接收具体实现类的实例对象 6 //使用带参数的构造器来传递具体实现类的对象 7 public DynamicProxy(Object obj){ 8 this.object = obj; 9 } 10 @Override 11 public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { 12 System.out.println("前置内容"); 13 method.invoke(object, args); 14 System.out.println("后置内容"); 15 return null; 16 } 17 }
第四步:建立测试类ProxyTest.java代理
1 package ceshi1; 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Proxy; 4 public class ProxyTest { 5 public static void main(String[] args) { 6 Iuser user = new UserImpl(); 7 InvocationHandler h = new DynamicProxy(user); 8 Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[]{Iuser.class}, h); 9 proxy.eat("苹果"); 10 } 11 }
运行结果为:
1 动态代理前置内容 2 我要吃苹果 3 动态代理后置内容
四、经过上面的动态代理实例咱们来仔细分析研究一下动态代理的实现过程
(1)首先我要说的就是接口,为何JDK的动态代理是基本接口实现的呢?
由于经过使用接口指向实现类的实例的多态实现方式,能够有效的将具体的实现与调用之间解耦,便于后期修改与维护。
再具体的说就是咱们在代理类中建立一个私有成员变量(private修饰),使用接口来指向实现类的对象(纯种的多态体现,向上转型的体现),而后在该代理类中的方法中使用这个建立的实例来调用实现类中的相应方法来完成业务逻辑功能。
这么提及来,我以前说的“将具体实现类彻底隐藏”就不怎么正确了,能够改为,将具体实现类的细节向调用方彻底隐藏(调用方调用的是代理类中的方法,而不是实现类中的方法)。
这就是面向接口编程,利用java的多态特性,实现程序代码的解耦。
(2)建立代理类的过程
若是你了解静态代理,那么你会发现动态代理的实现其实与静态代理相似,都须要建立代理类,可是不一样之处也很明显,建立方式不一样!
不一样之处体如今静态代理咱们知根知底,咱们知道要对哪一个接口、哪一个实现类来建立代理类,因此咱们在编译前就直接实现与实现类相同的接口,直接在实现的方法中调用实现类中的相应(同名)方法便可;而动态代理不一样,咱们不知道它何时建立,也不知道要建立针对哪一个接口、实现类的代理类(由于它是在运行时因需实时建立的)。
虽然两者建立时机不一样,建立方式也不相同,可是原理是相同的,不一样之处仅仅是:静态代理能够直接编码建立,而动态代理是利用反射机制来抽象出代理类的建立过程。
让咱们来分析一下以前的代码来验证一下上面的说辞:
第一点:静态代理须要实现与实现类相同的接口,而动态代理须要实现的是固定的Java提供的内置接口(一种专门提供来建立动态代理的接口)InvocationHandler接口,由于java在接口中提供了一个能够被自动调用的方法invoke,这个以后再说。
第二点:private Object object;
public UserProxy(Object obj){this.object = obj;}
这几行代码与静态代理之中在代理类中定义的接口指向具体实现类的实例的代码殊途同归,经过这个构造器能够建立代理类的实例,建立的同时还能将具体实现类的实例与之绑定(object指的就是实现类的实例,这个实例须要在测试类中建立并做为参数来建立代理类的实例),实现了静态代理类中private Iuser user = new UserImpl();一行代码的做用相近,这里为何不是相同,而是相近呢,主要就是由于静态代理的那句代码中包含的实现类的实例的建立,而动态代理中实现类的建立须要在测试类中完成,因此此处是相近。
第三点:invoke(Object proxy, Method method, Object[] args)方法,该方法是InvocationHandler接口中定义的惟一方法,该方法在调用指定的具体方法时会自动调用。其参数为:代理实例、调用的方法、方法的参数列表
在这个方法中咱们定义了几乎和静态代理相同的内容,仅仅是在方法的调用上不一样,不一样的缘由与以前分析的同样(建立时机的不一样,建立的方式的不一样,即反射),Method类是反射机制中一个重要的类,用于封装方法,该类中有一个方法那就是invoke(Object object,Object...args)方法,其参数分别表示:所调用方法所属的类的对象和方法的参数列表,这里的参数列表正是从测试类中传递到代理类中的invoke方法三个参数中最后一个参数(调用方法的参数列表)中,在传递到method的invoke方法中的第二个参数中的(此处有点啰嗦)。
第四点:测试类中的异同
静态代理中咱们测试类中直接建立代理类的对象,使用代理类的对象来调用其方法便可,如果别的接口(这里指的是别的调用方)要调用Iuser的方法,也可使用此法
动态代理中要复杂的多,首先咱们要将以前提到的实现类的实例建立(补充完整),而后利用这个实例做为参数,调用代理来的带参构造器来建立“代理类实例对象”,这里加引号的缘由是由于它并非真正的代理类的实例对象,而是建立真正代理类实例的一个参数,这个实现了InvocationHandler接口的类严格意义上来讲并非代理类,咱们能够将其看做是建立代理类的必备中间环节,这是一个调用处理器,也就是处理方法调用的一个类,不是真正意义上的代理类,能够这么说:建立一个方法调用处理器实例。
下面才是真正的代理类实例的建立,以前建立的”代理类实例对象“仅仅是一个参数
Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[]{Iuser.class}, h);
这里使用了动态代理所依赖的第二个重要类Proxy,此处使用了其静态方法来建立一个代理实例,其参数分别是:类加载器(可为父类的类加载器)、接口数组、方法调用处理器实例
这里一样使用了多态,使用接口指向代理类的实例,最后会用该实例来进行具体方法的调用便可。
(3)InvocationHandler
InvocationHandler是JDK中提供的专门用于实现基于接口的动态代理的接口,主要用于进行方法调用模块,而代理类和实例的生成须要借助Proxy类完成。
每一个代理类的实例的调用处理器都是实现该接口实现的,并且是必备的,即每一个动态代理实例的实现都必须拥有实现该接口的调用处理器,也能够这么说,每一个动态代理实例都对应一个调用处理器。
这里要区分两个概念,代理类和代理实例,调用处理器是在建立代理实例的时候才与其关联起来的,因此它与代理实例是一一对应的,而不是代理类。
(4)Proxy
Proxy类是JDK提供的用于生成动态代理类和其实例的类。
咱们能够经过Proxy中的静态方法getProxyClass来生成代理类,须要的参数为类加载器和接口列表(数组),而后再经过反射调用代理类的构造器来生成代理实例,须要以一个InvocationHandler做为参数(体现出方法调用是与实例相关的,而非类)。
1 InvocationHandler handler = new MyInvocationHandler(...); 2 Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class); 3 Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).newInstance(handler);
咱们也能够直接经过Proxy中的静态方法newProxyInstance方法来直接生产代理实例,须要提供参数为上面的三个参数,即类加载器,接口数组,InvocationHandler。
1 Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class<?>[] { Foo.class },handler);
(5)、总结
咱们总结下JDK动态代理的实现步骤:
第一步:建立接口,JDK动态代理基于接口实现,因此接口必不可少(准备工做)
第二步:实现InvocationHandler接口,重写invoke方法(准备工做)
第三步:调用Proxy的静态方法newProxyInstance方法生成代理实例(生成实例时须要提供类加载器,咱们可使用接口类的加载器便可)
第四步:使用新生成的代理实例调用某个方法实现功能。
咱们的动态代理实现过程当中根本没有涉及到真实类实例。
五、Cglib动态代理的实现
JDK动态代理拥有局限性,那就是必须面向接口编程,没有接口就没法实现代理,咱们也不可能为了代理而为每一个须要实现代理的类强行添加毫无心义的接口,这时咱们须要Cglib,这种依靠继承来实现动态代理的方式,再也不要求咱们必需要有接口。
第一步:添加Cglib的Maven依赖
1 <dependency> 2 <groupId>cglib</groupId> 3 <artifactId>cglib</artifactId> 4 <version>3.1</version> 5 </dependency>
第二步:建立具体实现类User.java
1 public class User { 2 public void eat(String s){ 3 System.out.println("我要吃" + s); 4 } 5 }
第三步:建立实现MethodInterceptor接口的代理类
1 import net.sf.cglib.proxy.MethodInterceptor; 2 import net.sf.cglib.proxy.MethodProxy; 3 4 import java.lang.reflect.Method; 5 6 public class UserInterceptor implements MethodInterceptor { 7 8 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 9 System.out.println("预处理"); 10 Object object = methodProxy.invokeSuper(o,objects); 11 System.out.println("后处理"); 12 return object; 13 } 14 15 }
第四步:建立测试类ProxyTest.java
1 import net.sf.cglib.proxy.Enhancer; 2 3 public class ProxyTest { 4 public static void main(String[] args){ 5 Enhancer enchancer = new Enhancer();//字节码加强器 6 enchancer.setSuperclass(User.class);//设置被代理类为父类 7 enchancer.setCallback(new UserInterceptor());//设置回调 8 User user = (User)enchancer.create();//建立代理实例 9 user.eat("葡萄"); 10 } 11 }
执行结果:
预处理
我要吃葡萄
后处理
六、cglib动态代理分析
经过代码实例咱们是能够看出一点,其实在编码上,cglib动态代理和JDK动态代理的编码逻辑相似,都是实现一个接口,再使用另一个提供的类来建立代理实例。这为咱们编码和记忆提供了便利,可是也容易带来混淆。
咱们有必要仔细分析下Cglib动态代理的实现。
(待续)