为什么jdk动态代理必须基于接口 
原因如下: 
1、生成的代理类继承了Proxy,由于java是单继承,所以只能实现接口,通过接口实现 

2、从代理模式的设计来说,充分利用了java的多态特性,也符合基于接口编码的规范

CGLIB代理

CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。 

CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。

如果目标对象实现了接口,可以强制使用CGLIB实现代理。

如果目标对象没有实现接口,则默认会采用CGLIB代理; 



动态代理有关,无非是使用JDK动态代理,和cglib动态代理。一直不待明白的是为什么,jdk的动态代理需要接口才能实现,这也是其短板和令人诟病的地方。很多的博文说的很复杂,代码一大堆,没有太明白。手打了一下,参考了一些优秀的博文,在这里给自己做个总结。

首先,动态代理是个挺有用的东西,常见的就是javaAOP的有关,主要的作用是是在一个方法使用前后,能进行别的处理。比如吧,aop所说的,面向切面编程,日志有关,检测有关。都可以通过AOP或者说动态代理来实现。

先来看下最简单的代码下实现动态代理的情况,然后看下源码和我们的主题,为什么需要接口

为什么需要接口,先上结论

1.在需要继承proxy类获得有关方法和InvocationHandler构造方法传参的同时,java不能同时继承两个类,我们需要和想要代理的类建立联系,只能实现一个接口

2.需要反射获得代理类的有关参数,必须要通过某个类,反射获取有关方法,如本次测试用的 :printSomeThing

3.成功返回的是object类型,要获取原类,只能继承/实现,或者就是那个代理类

4.对具体实现的方法内部并不关心,这个交给InvocationHandler.invoke那个方法里去处理就好了,我只想根据你给我的接口反射出对我有用的东西。

5.考虑到设计模式,以及proxy编者编写代码的逻辑使然



过程:

1.实现简单的JDK动态代理

1.1为什么需要这个接口的---这个接口==



1. package Activeproxy.jdk;  
2.   
3. public interface tagService {  
4.   
5. public void printSomeThing();  
6. }


1.2编写他的简单实现类





1. package Activeproxy.jdk;  
2.   
3. public class tagServiceImpl implements tagService {  
4.   
5. public final void printSomeThing() {  
6. "this is printSomeThing Core ServiceImpl");  
7.     }  
8.   
9.   
10. }


1.3编写调用处理程序InvocationHandler,实现该方法,其实在后面调用的时候,具体方法的调用,会进入这里面按下面invoke里面的顺序执行。 三个参数,分别代表,传入的代理实现类,此次调用的方法名称,有关参数 。这是平时使用的时候的动态代理的核心方法,看起来也很简答



    1. package Activeproxy.jdk;  
    2.   
    3. import java.lang.reflect.InvocationHandler;  
    4. import java.lang.reflect.Method;  
    5.   
    6. public class jdkInvocation implements InvocationHandler {  
    7.   
    8. private Object object;  
    9.   
    10. public void setTagServiceObject(Object object) {  
    11. this.object = object;  
    12.     }  
    13.   
    14. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
    15. "TagService代理前");  
    16. this.object, args);  
    17. "TagService代理后");  
    18. return returnObject;  
    19.     }  
    20.   
    21. }


    1.4编写调用类


      1. package Activeproxy.jdk;  
      2.   
      3. import java.lang.reflect.Proxy;  
      4.   
      5. public class start {  
      6.   
      7. public static void main(String[] args) {  
      8. new jdkInvocation();  
      9. new tagServiceImpl());  
      10.         tagService service = (tagService) Proxy  
      11. class.getClassLoader(), new Class[] { tagService.class }, invocation);  
      12.         service.printSomeThing();  
      13.   
      14.     }  
      15. }


      启动后,控制台会输出

      java接口动态设置泛型 java 动态接口_java

      就简单的动态代理来说在这里就结束了,但是并没有解决的我的疑问,为什么jdk动态代理需要接口

      主要的秘密在newProxyInstance这个方法里

      2.1在jdk 1.8里这个方法的代码是这样的




        1. @CallerSensitive  
        2. public static Object newProxyInstance(ClassLoader loader,  
        3.                                           Class<?>[] interfaces,  
        4.                                           InvocationHandler h)  
        5. throws IllegalArgumentException  
        6.     {  
        7.         Objects.requireNonNull(h);  
        8.   
        9. final Class<?>[] intfs = interfaces.clone();  
        10. final SecurityManager sm = System.getSecurityManager();  
        11. if (sm != null) {  
        12.             checkProxyAccess(Reflection.getCallerClass(), loader, intfs);  
        13.         }  
        14.   
        15. /*
        16.          * Look up or generate the designated proxy class.
        17.          */  
        18.         Class<?> cl = getProxyClass0(loader, intfs);  
        19.   
        20. /*
        21.          * Invoke its constructor with the designated invocation handler.
        22.          */  
        23. try {  
        24. if (sm != null) {  
        25.                 checkNewProxyPermission(Reflection.getCallerClass(), cl);  
        26.             }  
        27.   
        28. final Constructor<?> cons = cl.getConstructor(constructorParams);  
        29. final InvocationHandler ih = h;  
        30. if (!Modifier.isPublic(cl.getModifiers())) {  
        31. new PrivilegedAction<Void>() {  
        32. public Void run() {  
        33. true);  
        34. return null;  
        35.                     }  
        36.                 });  
        37.             }  
        38. return cons.newInstance(new Object[]{h});  
        39. catch (IllegalAccessException|InstantiationException e) {  
        40. throw new InternalError(e.toString(), e);  
        41. catch (InvocationTargetException e) {  
        42.             Throwable t = e.getCause();  
        43. if (t instanceof RuntimeException) {  
        44. throw (RuntimeException) t;  
        45. else {  
        46. throw new InternalError(t.toString(), t);  
        47.             }  
        48. catch (NoSuchMethodException e) {  
        49. throw new InternalError(e.toString(), e);  
        50.         }  
        51.     }


        讲解一下:

        checkProxyAccess是验证一些参数

        Class<?> cl = getProxyClass0(loader, intfs);是查找或生成指定的代理类(重点)

        final Constructor<?> cons = cl.getConstructor(constructorParams);这行代码是获取,生成代理类的构造函数是 InvocationHandler参数的方法

        return cons.newInstance(new Object[]{h});这行代码的意思是将h,就是实现InvocationHandler的jdkInvocation注入到cons中。然后newInstance生成一个已经组装过参数的代理类。

        现在这个参数名是cl的代理类,经过,获得cl,注入InvocationHandler的实现类。通过他newInstance的类,我们可以猜测一下,应该是有一个构造方法可以注入InvocationHandler的实现类。而且,还能被(tagService)这样强转,那么应该是继承了或者实现了tagService。

        so,到这一步,理论上来说我们可以知道:一个代理类,继承或者实现了我们专门创的接口,且内部有构造方法接受InvocationHandler的实现类。两个类关联起来了。而且,如果调用实例化成功的话,我们已经可以通过接口访问内部的方法了。

        那么重点来了,这个方法里的重点就是getProxyClass0(loader, intfs);这个方法里面是什么样的,做了哪些操作,返回的是什么代理类

        3.1 getProxyClass0   通过该类源码,发现其实他是从  proxyClassCache里获取的。这个是已经获取完毕后放在所谓,有关代理的静态缓存中。怎么处理的秘密在ProxyClassFactory中


        1. private static Class<?> getProxyClass0(ClassLoader loader,  
        2.                                           Class<?>... interfaces) {  
        3. if (interfaces.length > 65535) {  
        4. throw new IllegalArgumentException("interface limit exceeded");  
        5.        }  
        6.   
        7. // If the proxy class defined by the given loader implementing  
        8. // the given interfaces exists, this will simply return the cached copy;  
        9. // otherwise, it will create the proxy class via the ProxyClassFactory  
        10. return proxyClassCache.get(loader, interfaces);  
        11.    }

        3.2.ProxyClassFactory



          1. private static final class ProxyClassFactory  
          2. implements BiFunction<ClassLoader, Class<?>[], Class<?>>  
          3. {  
          4. // prefix for all proxy class names  
          5. private static final String proxyClassNamePrefix = "$Proxy";  
          6.   
          7. // next number to use for generation of unique proxy class names  
          8. private static final AtomicLong nextUniqueNumber = new AtomicLong();  
          9.   
          10. @Override  
          11. public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {  
          12.   
          13. new IdentityHashMap<>(interfaces.length);  
          14. for (Class<?> intf : interfaces) {  
          15. /*
          16.              * Verify that the class loader resolves the name of this
          17.              * interface to the same Class object.
          18.              */  
          19. null;  
          20. try {  
          21. false, loader);  
          22. catch (ClassNotFoundException e) {  
          23.             }  
          24. if (interfaceClass != intf) {  
          25. throw new IllegalArgumentException(  
          26. " is not visible from class loader");  
          27.             }  
          28. /*
          29.              * Verify that the Class object actually represents an
          30.              * interface.
          31.              */  
          32. if (!interfaceClass.isInterface()) {  
          33. throw new IllegalArgumentException(  
          34. " is not an interface");  
          35.             }  
          36. /*
          37.              * Verify that this interface is not a duplicate.
          38.              */  
          39. if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {  
          40. throw new IllegalArgumentException(  
          41. "repeated interface: " + interfaceClass.getName());  
          42.             }  
          43.         }  
          44.   
          45. null;     // package to define proxy class in  
          46. int accessFlags = Modifier.PUBLIC | Modifier.FINAL;  
          47.   
          48. /*
          49.          * Record the package of a non-public proxy interface so that the
          50.          * proxy class will be defined in the same package.  Verify that
          51.          * all non-public proxy interfaces are in the same package.
          52.          */  
          53. for (Class<?> intf : interfaces) {  
          54. int flags = intf.getModifiers();  
          55. if (!Modifier.isPublic(flags)) {  
          56.                 accessFlags = Modifier.FINAL;  
          57.                 String name = intf.getName();  
          58. int n = name.lastIndexOf('.');  
          59. 1) ? "" : name.substring(0, n + 1));  
          60. if (proxyPkg == null) {  
          61.                     proxyPkg = pkg;  
          62. else if (!pkg.equals(proxyPkg)) {  
          63. throw new IllegalArgumentException(  
          64. "non-public interfaces from different packages");  
          65.                 }  
          66.             }  
          67.         }  
          68.   
          69. if (proxyPkg == null) {  
          70. // if no non-public proxy interfaces, use com.sun.proxy package  
          71. ".";  
          72.         }  
          73.   
          74. /*
          75.          * Choose a name for the proxy class to generate.
          76.          */  
          77. long num = nextUniqueNumber.getAndIncrement();  
          78.         String proxyName = proxyPkg + proxyClassNamePrefix + num;  
          79.   
          80. /*
          81.          * Generate the specified proxy class.
          82.          */  
          83. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  
          84.             proxyName, interfaces, accessFlags);  
          85. try {  
          86. return defineClass0(loader, proxyName,  
          87. 0, proxyClassFile.length);  
          88. catch (ClassFormatError e) {  
          89. /*
          90.              * A ClassFormatError here means that (barring bugs in the
          91.              * proxy class generation code) there was some other
          92.              * invalid aspect of the arguments supplied to the proxy
          93.              * class creation (such as virtual machine limitations
          94.              * exceeded).
          95.              */  
          96. throw new IllegalArgumentException(e.toString());  
          97.         }  
          98.     }  
          99. }


          这么长的一大串代码其实也没干太多事,无非也就是验证参数,验证接口,反射获得接口。重点是

           byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);

          这个方法就是要找到的原因。他隐藏起来了最关键的,怎么组装了一个byte类型代理类。在后面传了回去

          通过反编译,我们得知,返回的结果是,



          1. package com.sun.proxy;    
          2.     
          3. import com.Activeproxy.jdk.tagService;    
          4. import java.lang.reflect.InvocationHandler;    
          5. import java.lang.reflect.Method;    
          6. import java.lang.reflect.Proxy;    
          7. import java.lang.reflect.UndeclaredThrowableException;    
          8.     
          9. public final class $Proxy0 extends Proxy implements tagService{    
          10. private static Method m1;    
          11. private static Method m3;    
          12. private static Method m0;    
          13. private static Method m2;    
          14.     
          15. public $Proxy0(InvocationHandler paramInvocationHandler) {    
          16. super(paramInvocationHandler);    
          17.   }<strong>  </strong>  
          18.     
          19. public final boolean equals(Object paramObject) {    
          20. try {    
          21. return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();    
          22.     }    
          23. catch (Error|RuntimeException localError) {    
          24. throw localError;    
          25.     }    
          26. catch (Throwable localThrowable) {    
          27. throw new UndeclaredThrowableException(localThrowable);    
          28.     }    
          29.   }    
          30.     
          31. public final void printSomeThing(String paramString) {    
          32. try {    
          33. this.h.invoke(this, m3, new Object[] { paramString });    
          34. return;    
          35.     }    
          36. catch (Error|RuntimeException localError) {    
          37. throw localError;    
          38.     }    
          39. catch (Throwable localThrowable) {    
          40. throw new UndeclaredThrowableException(localThrowable);    
          41.     }    
          42.   }    
          43.     
          44. public final int hashCode() {    
          45. try {    
          46. return ((Integer)this.h.invoke(this, m0, null)).intValue();    
          47.     }    
          48. catch (Error|RuntimeException localError) {    
          49. throw localError;    
          50.     }    
          51. catch (Throwable localThrowable) {    
          52. throw new UndeclaredThrowableException(localThrowable);    
          53.     }    
          54.   }    
          55.     
          56. public final String toString() {    
          57. try {    
          58. return (String)this.h.invoke(this, m2, null);    
          59.     }    
          60. catch (Error|RuntimeException localError) {    
          61. throw localError;    
          62.     }    
          63. catch (Throwable localThrowable) {    
          64. throw new UndeclaredThrowableException(localThrowable);    
          65.     }    
          66.   }    
          67.     
          68. static {    
          69. try {    
          70. "java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });    
          71. "com.Activeproxy.jdk.tagService").getMethod("printSomeThing", new Class[0]);    
          72. "java.lang.Object").getMethod("hashCode", new Class[0]);    
          73. "java.lang.Object").getMethod("toString", new Class[0]);    
          74. return;    
          75.     }    
          76. catch (NoSuchMethodException localNoSuchMethodException) {    
          77. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());    
          78.     }    
          79. catch (ClassNotFoundException localClassNotFoundException) {    
          80. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());    
          81.     }    
          82.   }    
          83. }


          看到这个方法,就算破案了。这个就是jdk动态代理和为什么需要接口。因为

          1.在需要继承proxy类获得有关方法和InvocationHandler构造方法传参的同时,java不能同时继承两个类,我们需要和想要代理的类建立联系,只能实现一个接口

          2.需要反射获得代理类的有关参数,必须要通过某个类,反射获取有关方法,如本次测试用的 :printSomeThing

          3.成功返回的是object类型,要获取原类,只能继承/实现,或者就是那个代理类

          4.对具体实现的方法内部并不关心,这个交给InvocationHandler.invoke那个方法里去处理就好了,我只想根据你给我的接口反射出对我有用的东西。

          5.考虑到设计模式,以及proxy编者编写代码的逻辑使然