zoukankan      html  css  js  c++  java
  • java代理的深入浅出(一)-Proxy

    java代理的深入浅出(一)-Proxy

    1.什么是代理

    代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
    按照代理的创建时期,代理类可以分为两种。
    静态代理:
    由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
    实现方式:
    1、为每个代理类,写对应的代理类

     优点:简单方便
     缺点:当业务系统中需要大量的代理类时, 定义繁多的代理类
    

    2、使用Aspectj

     使用Aspectj工具,将目标类class类,织入横切逻辑
     优点:因在JVM加载类前,已经将横切的业务逻辑加载到目标类中, 所以在执行效率上非常高
     缺点:不易维护, 修改的话, 还需要重新生成,编译 
    

    动态代理:在程序运行时,运用反射机制动态创建而成。
    优点:易修改,易维护
    缺点:需要动态生成代理类, 在效率上比静态代理相对低

    实现的两种方式
    1、JDK动态代理
    目标对象必须有对应接口定义
    2、CGLIB动态代理
    目标对象不用有对应接口, 会生成目标类的子类,所以目标类的方法不能是final

    Spring AOP实现机制是动态代理, 主要是上面两种方式JDK、CGLIB

    2.基本原理

    目前Java开发包中包含了对动态代理的支持,但是其实现只支持对接口的的实现。 其实现主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。 Proxy类主要用来获取动态代理对象,InvocationHandler接口用来约束调用者实现。

    Proxy类:

    Porxy类也是在java.lang.reflect,Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

    代理类具用以下属性:

    • 代理类是公共的、最终的,而不是抽象的。

    • 未指定代理类的非限定名称。但是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留。

    • 代理类扩展 java.lang.reflect.Proxy。

    • 代理类会按同一顺序准确地实现其创建时指定的接口。

    • 如果代理类实现了非公共接口,那么它将在与该接口相同的包中定义。否则,代理类的包也是未指定的。注意,包密封将不阻止代理类在运行时在特定包中的成功定义,也不会阻止相同类加载器和带有特定签名的包所定义的类。

    • 由于代理类将实现所有在其创建时指定的接口,所以对其 Class 对象调用 getInterfaces 将返回一个包含相同接口列表的数组(按其创建时指定的顺序),对其 Class 对象调用 getMethods 将返回一个包括这些接口中所有方法的 Method 对象的数组,并且调用 getMethod 将会在代理接口中找到期望的一些方法。

    • 如果 Proxy.isProxyClass 方法传递代理类(由 Proxy.getProxyClass 返回的类,或由 Proxy.newProxyInstance 返回的对象的类),则该方法返回 true,否则返回 false。

    • 代理类的 java.security.ProtectionDomain 与由引导类加载器(如 java.lang.Object)加载的系统类相同,原因是代理类的代码由受信任的系统代码生成。此保护域通常被授予 java.security.AllPermission。

    • 每个代理类都有一个可以带一个参数(接口 InvocationHandler 的实现)的公共构造方法,用于设置代理实例的调用处理程序。并非必须使用反射 API 才能访问公共构造方法,通过调用 Proxy.newInstance 方法(将调用 Proxy.getProxyClass 的操作和调用带有调用处理程序的构造方法结合在一起)也可以创建代理实例。

        protected  Proxy(InvocationHandler h)  //使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例。 
        
        
        static InvocationHandler getInvocationHandler(Object proxy) //返回指定代理实例的调用处理程序。 
        
        
        static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) //返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。 
        
        
        static boolean isProxyClass(Class<?> cl) //当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。 
        
        
        static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) //返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
      

    InvocationHandler接口:

    InvocationHandler接口也是在java.lang.reflect,唯一的一个方法是invoke如下:

    Object invoke(Object proxy, Method method, Object[] args)
    

    这个方法有三个参数,其中第二和第三个参数都比较好理解,一个是被拦截的方法,一个是该方法的参数列表。关键是第一个参数。按照doc文档的解析,proxy - the proxy instance that the method was invoked on也就是说,proxy应该是一个代理实例(动态代理类)。

    3.示例

    接口与实现类:

    public interface UserService {
    
        void addUser(long cardId);
    }
    
    public class UserServiceImpl implements UserService {
        public void addUser(long cardId) {
            System.out.println("cardId>>>>>"+cardId);
        }
    }
    

    代理类的创建和拦截处理类

    public class UserInvacationHandler implements InvocationHandler {
    
        private Object target;
         public Object getProxyInstance(Object target){
             this.target =target;
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            System.out.println("before target method.....");
            result = method.invoke(target,args);
            System.out.println("after target method.....");
            return result;
        }
    }
    

    代理测试类

    public class ProxyTest {
        public static void main(String[] args){
            UserInvacationHandler userInvacationHandler = new UserInvacationHandler();
            UserService userService = (UserService) userInvacationHandler.getProxyInstance(new UserServiceImpl());
            userService.addUser(1L);
        }
    }
    

    代理类反编译

    生成代理类的方法Proxy.newProxyInstance(), 其源码核心代码是ProxyGenerator.generateProxyClass(String paramString, Class[] paramArrayOfClass)

    public class ProxyClassFile {
    
        public static void main(String[] args){
    
            String proxyName = "UserServiceProxy";
    
            UserService a = new UserServiceImpl();
            Class[] interfaces =  a.getClass().getInterfaces();
            byte[] bytes = ProxyGenerator.generateProxyClass(proxyName, interfaces);
            File f  = new File("D:/work/code/middleware/study/proxy/UserServiceProxy.class");
            try{
                FileOutputStream fos = new FileOutputStream(f);
                fos.write(bytes);
                fos.flush();
                fos.close();
            }catch(FileNotFoundException e){
                e.printStackTrace();
            }catch(IOException e1){
                e1.printStackTrace();
            }
    
    
        }
    }
    

    反编译

    import com.longchao.proxy.UserService;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class UserServiceProxy extends Proxy
      implements UserService
    {
      private static Method m1;
      private static Method m3;
      private static Method m0;
      private static Method m2;
    
      public UserServiceProxy(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 (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final void addUser(long paramLong)
        throws 
      {
        try
        {
          this.h.invoke(this, m3, new Object[] { Long.valueOf(paramLong) });
          return;
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final int hashCode()
        throws 
      {
        try
        {
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final String toString()
        throws 
      {
        try
        {
          return (String)this.h.invoke(this, m2, null);
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        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("com.longchao.proxy.UserService").getMethod("addUser", new Class[] { Long.TYPE });
          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());
      }
    }
    

    从源码发现, addUser方法(或其他方法)都会调用this.h.invoke(this, m3, null);
    this.h 是其父类Proxy的protected InvocationHandler h; 即我们自定义的UserInvocationHandler,

    m3:
    m3 = Class.forName("com.user.UserService").getMethod("addUser", new Class[0]);

    4.总结

    一个典型的动态代理创建对象过程可分为以下四个步骤:

    • 1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);

    • 2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
      Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});

    • 3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
      Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

    • 4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
      Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
      为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
      生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))

    5美中不足

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

  • 相关阅读:
    框架集。样式表
    2017.11.23知识点整理
    HTML5的标签
    HTML5大体概括截图
    2017.11.21 通用标签及属性
    2017.11.21 课程随记
    JavaScript数组
    JavaScript语句
    javascript基础知识
    不用alert提示的非空表单验证
  • 原文地址:https://www.cnblogs.com/zhulongchao/p/5495071.html
Copyright © 2011-2022 走看看