Simple analysis of java static proxy and dynamic proxy

Source addresshttp: //www.cnblogs.com/V1haoge/p/5860749.html

 

1. Dynamic proxy (Dynamic Proxy)
  proxy is divided into static proxy and dynamic proxy. Static proxy is to manually complete the interface, implementation class, and proxy class at compile time, but if we need a lot of proxies, each one is like this It is a waste of time to create manually, and there will be a lot of repetitive code. At this time, we can use dynamic proxy. Dynamic proxy can dynamically create proxy classes and their instances as needed during the running of the program to complete specific functions.
  In fact, the method can be called directly to complete the function, why add an agent?
  The reason is that using the proxy mode can effectively decouple the specific implementation from the caller, and completely hide the specific implementation internally through interface-oriented coding.
2. The general mode of proxy implementation
  In fact, the general mode of proxy is the implementation mode of static proxy: first create an interface (JDK proxies are all interface-oriented), then create a specific implementation class to implement this interface, and create a proxy class to implement the same The difference of this interface is that the method of the concrete implementation class needs to implement the business logic function of the method defined in the interface, while the method in the proxy class only needs to call the corresponding method in the concrete class, so we need to use the interface When the function of a method in the proxy class is called directly, the specific implementation class is hidden in the bottom layer.
  The first step: define the general interface Iuser.java

1 package ceshi1;
2 public interface Iuser {
3     void eat(String s);
4 }

  Step 2: Create a concrete implementation class UserImpl.java

copy code
1 package ceshi1;
2 public class UserImpl implements Iuser {
3   @Override
4   public void eat(String s) {
5     System.out.println("我要吃"+s);
6   }
7 }
copy code

  Step 3: Create a proxy class UserProxy.java

copy code
 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 }
copy code

  第四步:创建测试类ProxyTest.java

copy code
1 package ceshi1;
2 public class ProxyTest {
3   public static void main(String[] args) {    
4     UserProxy proxy = new UserProxy();
5     proxy.eat("苹果");
6   }
7 }
copy code

  运行结果:

1 静态代理前置内容
2 我要吃苹果
3 静态代理后置内容

3、JDK动态代理的实现
  JDK动态代理的思维模式与之前的一般模式是一样的,也是面向接口进行编码,创建代理类将具体类隐藏解耦,不同之处在于代理类的创建时机不同,动态代理需要在运行时因需实时创建。
  第一步:定义总接口Iuser.java

1 package ceshi1;
2 public interface Iuser {
3   void eat(String s);
4 }

  第二步:创建具体实现类UserImpl.java

copy code
1 package ceshi1;
2 public class UserImpl implements Iuser {
3   @Override
4   public void eat(String s) {
5     System.out.println("我要吃"+s);
6   }
7 }
copy code

  第三步:创建实现InvocationHandler接口的代理类

copy code
 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 }
copy code

  第四步:创建测试类ProxyTest.java

copy code
 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 }
copy code

  运行结果为:

1 动态代理前置内容
2 我要吃苹果
3 动态代理后置内容

4、通过上面的动态代理实例我们来仔细分析研究一下动态代理的实现过程
(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方法生成代理实例(生成实例时需要提供类加载器,我们可以使用接口类的加载器即可)

    第四步:使用新生成的代理实例调用某个方法实现功能。

  我们的动态代理实现过程中根本没有涉及到真实类实例。

5、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接口的代理类

copy code
 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 }
copy code

  第四步:创建测试类ProxyTest.java

copy code
 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 }
copy code

  执行结果:

预处理
我要吃葡萄
后处理

6、cglib动态代理分析

  Through the code example, we can see that, in fact, in terms of coding, the coding logic of cglib dynamic proxy and JDK dynamic proxy is similar, both implement an interface, and then use another provided class to create a proxy instance. This is convenient for us to encode and remember, but it is also prone to confusion.

  It is necessary for us to carefully analyze the implementation of Cglib dynamic proxy.

(to be continued)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324831231&siteId=291194637