zoukankan      html  css  js  c++  java
  • java中的静态代理和动态代理

    1、动态代理的定义:为其他对象提供一个代理以控制对这个对象的访问

    代理类主要负责委托类的预处理消息,过滤消息,把消息传给委托类以及消息事后处理

    按照代理类的创建时期,代理类可以分为2种:静态代理类(在程序运行前代理类.class文件就以及生成)和动态代理类(字节码是在程序运行时由java反射机制动态生成)

    静态代理的实例:

    package com.vcredit.ddcash.decision;

    public class ProxyTest {
    public static void main(String[] args) {
    HelloService helloService = new HelloServiceImpl();
    HelloServiceProxy helloServiceProxy = new HelloServiceProxy(helloService);
    helloServiceProxy.say();
    }

    }

    interface HelloService{
    void say();

    }
    class HelloServiceImpl implements HelloService{
    public void say(){
    System.out.println(11);
    };

    }
    class HelloServiceProxy implements HelloService{
    public HelloService helloService;
    HelloServiceProxy(HelloService helloService){
    this.helloService=helloService;
    }
    public void say(){
    System.out.println("预处理");
    helloService.say();//调用被代理的HellService的say方法
    System.out.println("事后处理");
    };
    }

    2、通过api看下proxy生成代理类的2中写法:

    创建某一接口 Foo 的代理:

         InvocationHandler handler = new MyInvocationHandler(...);
         Class proxyClass = Proxy.getProxyClass(
             Foo.class.getClassLoader(), new Class[] { Foo.class });
         Foo f = (Foo) proxyClass.
             getConstructor(new Class[] { InvocationHandler.class }).
             newInstance(new Object[] { handler });
     

    或使用以下更简单的方法:

         Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                              new Class[] { Foo.class },
                                              handler);
    其实第二中写法就相当于对第一中写法的封装,具体看Proxy这个类;

    3、具体实例:

    public interface Subject  
    {  
      public void doSomething();  
    }  
    public class RealSubject implements Subject  
    {  
      public void doSomething()  
      {  
        System.out.println( "call doSomething()" );  
      }  
    }  
    public class ProxyHandler implements InvocationHandler  
    {  
      private Object proxied;  
        
      public ProxyHandler( Object proxied )  
      {  
        this.proxied = proxied;  
      }  
        
      public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable  
      {  
        //在转调具体目标对象之前,可以执行一些功能处理

        //转调具体目标对象的方法
        return method.invoke( proxied, args); 
       
        //在转调具体目标对象之后,可以执行一些功能处理
      }   
    }

    import java.lang.reflect.InvocationHandler;  
    import java.lang.reflect.Method;  
    import java.lang.reflect.Proxy;  
    import sun.misc.ProxyGenerator;  
    import java.io.*;  
    public class DynamicProxy  
    {  
      public static void main( String args[] )  
      {  
        RealSubject real = new RealSubject();  
        Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
         new Class[]{Subject.class},
         new ProxyHandler(real));//要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
            
        proxySubject.doSomething();
      
        //write proxySubject class binary data to file  
        createProxyClassFile();  
      }  
        
      public static void createProxyClassFile()  
      {  
        String name = "ProxySubject";  
        byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject.class } );  
        try 
        {  
          FileOutputStream out = new FileOutputStream( name + ".class" );  
          out.write( data );  
          out.close();  
        }  
        catch( Exception e )  
        {  
          e.printStackTrace();  
        }  
      }  
    }

    分析:当调用proxySubject.doSomething();时,就调用了$proxy0类中的doSomething()方法,在doSomething()方法中调用父类

    proxy中h的invoke()方法,即InvercationHandler.invoke(),这个流程可以通过代理类的.class文件反编译后查看

    部分截图:

    public final void doSomething() {
       try {
        super.h.invoke(this, m3, null);
        return;
       } catch (Error e) {
       } catch (Throwable throwable) {
        throw new UndeclaredThrowableException(throwable);
       }
    }

    下面看下通过Proxy类的静态方法getProxyClass生成的class文件经过反编译后生成的代码:

    public final class $Proxy0
    extends Proxy
    implements Subject{
    ---表名是一个最终类,此类继承了Proxy类,实现了Subject接口

    public $Proxy0(InvocationHandler invocationHandler) {
            super(invocationHandler);
        }

    里面有一个带参数的构造方法,通过getProxyClass(...).getConstructor(parameterType p)//这个参数应该就是InvocationHandler这个接口类,从而通过super调用父类的带参构造器,从而给就有了InvocationHandler这个接口

     protected Proxy(InvocationHandler h) {
     this.h = h;
        }

    当调用$Proxy0类的doSomething()方法时:会调用h.invoke(...)方法

    public final String doSomething() {
            try {
                return (String)this.h.invoke((Object)this, m3, new Object[]{});
            }
            catch (Error | RuntimeException v0) {
                throw v0;
            }
            catch (Throwable var2_2) {
                throw new UndeclaredThrowableException(var2_2);
            }
        }
    看下invoke方法的3个参数 第一个指的就是被代理的类的实例也就是对应的RealSubject实例

    m3指的是对应的方法

    m3 = Class.forName("****.Subject").getMethod("doSomething");
    第三个参数指的就是方法中的参数,没有参数就是null

    再通过反射机制就可以唯一确定调用被代理类中的相关方法

    4、Proxy已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫Proxy。Java的继承机制注定了这些动态代理类们无法实现对class的动态代理,原因是多继承在Java中本质上就行不通。有很多条理由,人们可以否定对 class代理的必要性,但是同样有一些理由,相信支持class动态代理会更美好。接口和类的划分,本就不是很明显,只是到了Java中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。如此种种,不得不说是一个小小的遗憾。但是,不完美并不等于不伟大,伟大是一种本质,Java动态代理就是佐例。

    Cglib动态代理 
    JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

    还是举例说明:

    public class BookImpl{//没有实现接口
     public void add(){
      System.out.println("这是一个新增的方法");
     }

    }

    import java.lang.reflect.Method;

    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;

    //创建代理类,这个类实现MethodInterceptor接口
    public class BookProxy  implements MethodInterceptor {

     private Object target;
     
     public BookProxy(Object target){
      this.target=target;
     }
     
     @Override
     public Object intercept(Object arg0, Method arg1, Object[] arg2,
       MethodProxy arg3) throws Throwable {
      System.out.println("事务开始");
      Object result = arg3.invokeSuper(target, arg2);
      System.out.println("事务结束");
      return result;
     }

    }

    //测试类


    public class TestCglib {
     public static void main(String[] args){
      
     BookImpl bi = new BookImpl();

     BookProxy bp = new BookProxy(bi);
     BookImpl bii = (BookImpl)Enhancer.create(bi,bp);
      bii.add();
     }

    }

    //输出结果

    事务开始
    这是一个新增的方法
    事务结束

  • 相关阅读:
    Docker容器启动时初始化Mysql数据库
    使用Buildpacks高效构建Docker镜像
    Mybatis 强大的结果集映射器resultMap
    Java 集合排序策略接口 Comparator
    Spring MVC 函数式编程进阶
    换一种方式编写 Spring MVC 接口
    【asp.net core 系列】6 实战之 一个项目的完整结构
    【asp.net core 系列】5 布局页和静态资源
    【asp.net core 系列】4. 更高更强的路由
    【Java Spring Cloud 实战之路】- 使用Nacos和网关中心的创建
  • 原文地址:https://www.cnblogs.com/muliu/p/5498390.html
Copyright © 2011-2022 走看看