精华内容
下载资源
问答
  • 面试的时候,常常面试官会问spring 的aop底层原理,而aop的底层原理就是动态代理,每个JAVA程序员基本都能耳熟能详,对jdk动态代理cglib动态代理的区别也了然于胸,jdk动态代理只能基于接口,而cglib不需要。...

    面试的时候,常常面试官会问spring 的aop底层原理,而aop的底层原理就是动态代理,每个JAVA程序员基本都能耳熟能详,对jdk动态代理和cglib动态代理的区别也了然于胸,jdk动态代理只能基于接口,而cglib并不需要。

    那么jdk的动态代理为什么要基于接口呢?

    下面我们先实现一个简单的jdk动态代理

    接口类

    public interface Person {    void say();}

    实现类

    public class Man implements Person{    @Override    public void say() {        System.out.println("man");    }}

    代理类实现InvocationHandler接口,并重写invoke方法

    public class JDKDynamicProxy implements InvocationHandler {    private Object target;    public JDKDynamicProxy(Object target) {        this.target = target;    }    public  T getProxy() {        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("Do something before");        Object result = method.invoke(target, args);        System.out.println("Do something after");        return result;    }}

    调用一下

    public class Test {    public static void main(String[] args) {        Person person = new JDKDynamicProxy(new Man()).getProxy();        person.say();    }}

    调用结果如下

    Do something beforemanDo something after

    说明动态代理生效了。

    那么问题来了,jdk动态代理要求代理类实现InvocationHandler接口,重写invoke方法就可以实现动态代理,且构造函数注入Object即可,并无要求Object必须基于接口,而且重本质上说getProxy()获取代理类(Object的原类),完全可以通过继承对象的方式。

    因此再定义一个WonMan.class,不实现接口,再来测试一下

    public class Woman {    public void say(){        System.out.println("woman");    }}

    调用接口

    Woman woman = new JDKDynamicProxy(new Woman()).getProxy();woman.say();

    结果如下

    Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.ganhuojun.demo.proxy.Woman    at com.ganhuojun.demo.proxy.Test.main(Test.java:8)

    说明,确实不支持没有接口的object动态代理。

    接下来我们来分析下源码,探究下为什么只支持接口。因为动态代理的核心是获取代理类,我们下面方法开始看

    Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

    由于源码很多,只说简单说下核心部分,源码如下,红色部分是获取代理类

    bbecf0b0fb5b5718eb469a15f92d5354.png

    再看getProxyClass0方法,为了方便理解,将核心部分抽出来,如下

    private static final WeakCache[], Class>>        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());private static Class> getProxyClass0(ClassLoader loader,                                           Class>... interfaces) {    if (interfaces.length > 65535) {        throw new IllegalArgumentException("interface limit exceeded");    }    // If the proxy class defined by the given loader implementing    // the given interfaces exists, this will simply return the cached copy;    // otherwise, it will create the proxy class via the ProxyClassFactory    return proxyClassCache.get(loader, interfaces);}

    proxyClassCache是WeakCache,再看get方法,一步一步往下看,最终会看到调用到ProxyClassFactory类的apply()方法,如下

    026ebc0f8248c159865633e91ea116c5.png

    此处发现红色部分是生成真正的代理类的方法,继续进去看一下,发现只需要将saveGeneratedFiles设置true,即可本地查看生成的代理类,如下

    0e31ea788b330c44d303666be14657b9.png

    saveGeneratedFiles的值如下,

    private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));

    此时我们将测试代码改成如下样子

    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");Person person = new JDKDynamicProxy(new Man()).getProxy();person.say();

    执行后发现本地多了一个代理类,如下图

    60bd793d7eb9f683f238fe0e5a30c425.png

    打开看一下,源码如下,发现$Proxy0本身就继承了一个Proxy类,由于java不支持多继承,因此无法实现动态代理

    public final class $Proxy0 extends Proxy implements Person {    private static Method m1;    private static Method m2;    private static Method m3;    private static Method m0;    public $Proxy0(InvocationHandler var1) throws  {        super(var1);    }    public final boolean equals(Object var1) throws  {        try {            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});        } catch (RuntimeException | Error var3) {            throw var3;        } catch (Throwable var4) {            throw new UndeclaredThrowableException(var4);        }    }    public final String toString() throws  {        try {            return (String)super.h.invoke(this, m2, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    public final void say() throws  {        try {            super.h.invoke(this, m3, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    public final int hashCode() throws  {        try {            return (Integer)super.h.invoke(this, m0, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    static {        try {            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));            m2 = Class.forName("java.lang.Object").getMethod("toString");            m3 = Class.forName("com.ganhuojun.demo.proxy.Person").getMethod("say");            m0 = Class.forName("java.lang.Object").getMethod("hashCode");        } catch (NoSuchMethodException var2) {            throw new NoSuchMethodError(var2.getMessage());        } catch (ClassNotFoundException var3) {            throw new NoClassDefFoundError(var3.getMessage());        }    }}

    结论:

    生成的代理类继承了Proxy,由于java是单继承,所以只能实现接口,通过接口实现

    展开全文
  • CGLIB动态代理

    2020-04-05 19:47:46
    它的优势在于不需要提供接口,只要一个非抽象类即可实现动态代理什么CGLIB CGLIB是一个功能强大,高性能的代码生成包。它没有实现接口的类提供代理JDK的动态代理提供了很好的补充。通常可以使用Java的...

    CGLIB动态代理

    JDK的动态代理必须提供接口才能使用,但是一些情况下是没有接口的,只能采用第三方技术了,比如CGLIB动态代理。它的优势在于不需要提供接口,只要一个非抽象类即可实现动态代理。

    什么是CGLIB

    CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

    原理

    动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

    被代理的类

    class Person{
    
        public void goHome(String name , String fromAddress,String toAddress){
            System.out.println(name + "从"+fromAddress+"去"+toAddress);
        }
    
    }
    

    拦截器

    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    public class CGLIBProxy implements MethodInterceptor {
    
        //重写拦截方法
        //object 目标对象
        //method 目标方法
        //objects 参数
        //methodProxy CGLib方法代理对象
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            if (method.getName().equals("goHome")){
                System.out.println(Arrays.toString(objects));
                return methodProxy.invokeSuper(o,objects) ;
            }
            return null ;
        }
    
        public static void main(String[] args) {
            //字节码增强器
            Enhancer enhancer = new Enhancer() ;
            enhancer.setSuperclass(Person.class);
            enhancer.setCallback(new CGLIBProxy());
            Person person = (Person) enhancer.create();
            person.goHome("我","上海","北京");
    
        }
    
    }
    

    [我, 上海, 北京]
    我从上海去北京

    未完待续!!!

    参考:https://blog.csdn.net/zghwaicsdn/article/details/50957474

    展开全文
  • jdk的动态代理为什么需要接口

    万次阅读 多人点赞 2017-12-01 14:38:30
    一直待明白的是为什么,jdk的动态代理需要接口才能实现,这也是其短板和令人诟病的地方。很多的博文说的很复杂,代码一大堆,没有太明白。手打了一下,参考了一些优秀的博文,在这里给自己做个总结。 首先,动态...

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

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

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

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

     

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

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

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

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

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

     

    过程:

     

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

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

     

    package Activeproxy.jdk;
    
    public interface tagService {
    
    	public void printSomeThing();
    }
    

     

    1.2编写他的简单实现类

     

    package Activeproxy.jdk;
    
    public class tagServiceImpl implements tagService {
    
    	public final void printSomeThing() {
    		System.err.println("this is printSomeThing Core ServiceImpl");
    	}
    
    
    }
    


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

     

     

    package Activeproxy.jdk;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class jdkInvocation implements InvocationHandler {
    
    	private Object object;
    
    	public void setTagServiceObject(Object object) {
    		this.object = object;
    	}
    
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		System.out.println("TagService代理前");
    		Object returnObject = method.invoke(this.object, args);
    		System.out.println("TagService代理后");
    		return returnObject;
    	}
    
    }
    


    1.4编写调用类

     

     

    package Activeproxy.jdk;
    
    import java.lang.reflect.Proxy;
    
    public class start {
    
    	public static void main(String[] args) {
    		jdkInvocation invocation = new jdkInvocation();
    		invocation.setTagServiceObject(new tagServiceImpl());
    		tagService service = (tagService) Proxy
    				.newProxyInstance(start.class.getClassLoader(), new Class[] { tagService.class }, invocation);
    		service.printSomeThing();
    
    	}
    }
    

     

    启动后,控制台会输出

     

     

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

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

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

     

    @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
    
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }

     

     

     

     

     

    讲解一下:

     

     

    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中

     

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

    3.2.ProxyClassFactory

        private static final class ProxyClassFactory
            implements BiFunction<ClassLoader, Class<?>[], Class<?>>
        {
            // prefix for all proxy class names
            private static final String proxyClassNamePrefix = "$Proxy";
    
            // next number to use for generation of unique proxy class names
            private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
            @Override
            public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
                Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
                for (Class<?> intf : interfaces) {
                    /*
                     * Verify that the class loader resolves the name of this
                     * interface to the same Class object.
                     */
                    Class<?> interfaceClass = null;
                    try {
                        interfaceClass = Class.forName(intf.getName(), false, loader);
                    } catch (ClassNotFoundException e) {
                    }
                    if (interfaceClass != intf) {
                        throw new IllegalArgumentException(
                            intf + " is not visible from class loader");
                    }
                    /*
                     * Verify that the Class object actually represents an
                     * interface.
                     */
                    if (!interfaceClass.isInterface()) {
                        throw new IllegalArgumentException(
                            interfaceClass.getName() + " is not an interface");
                    }
                    /*
                     * Verify that this interface is not a duplicate.
                     */
                    if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                        throw new IllegalArgumentException(
                            "repeated interface: " + interfaceClass.getName());
                    }
                }
    
                String proxyPkg = null;     // package to define proxy class in
                int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    
                /*
                 * Record the package of a non-public proxy interface so that the
                 * proxy class will be defined in the same package.  Verify that
                 * all non-public proxy interfaces are in the same package.
                 */
                for (Class<?> intf : interfaces) {
                    int flags = intf.getModifiers();
                    if (!Modifier.isPublic(flags)) {
                        accessFlags = Modifier.FINAL;
                        String name = intf.getName();
                        int n = name.lastIndexOf('.');
                        String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                        if (proxyPkg == null) {
                            proxyPkg = pkg;
                        } else if (!pkg.equals(proxyPkg)) {
                            throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                        }
                    }
                }
    
                if (proxyPkg == null) {
                    // if no non-public proxy interfaces, use com.sun.proxy package
                    proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
                }
    
                /*
                 * Choose a name for the proxy class to generate.
                 */
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
                /*
                 * Generate the specified proxy class.
                 */
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
                try {
                    return defineClass0(loader, proxyName,
                                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                    /*
                     * A ClassFormatError here means that (barring bugs in the
                     * proxy class generation code) there was some other
                     * invalid aspect of the arguments supplied to the proxy
                     * class creation (such as virtual machine limitations
                     * exceeded).
                     */
                    throw new IllegalArgumentException(e.toString());
                }
            }
        }

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

     

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

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

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

     

    package com.sun.proxy;  
      
    import com.Activeproxy.jdk.tagService;  
    import java.lang.reflect.InvocationHandler;  
    import java.lang.reflect.Method;  
    import java.lang.reflect.Proxy;  
    import java.lang.reflect.UndeclaredThrowableException;  
      
    public final class $Proxy0 extends Proxy implements tagService{  
      private static Method m1;  
      private static Method m3;  
      private static Method m0;  
      private static Method m2;  
      
      public $Proxy0(InvocationHandler paramInvocationHandler) {  
        super(paramInvocationHandler);  
      }  
      
      public final boolean equals(Object paramObject) {  
        try {  
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();  
        }  
        catch (Error|RuntimeException localError) {  
          throw localError;  
        }  
        catch (Throwable localThrowable) {  
          throw new UndeclaredThrowableException(localThrowable);  
        }  
      }  
      
      public final void printSomeThing(String paramString) {  
        try {  
          this.h.invoke(this, m3, new Object[] { paramString });  
          return;  
        }  
        catch (Error|RuntimeException localError) {  
          throw localError;  
        }  
        catch (Throwable localThrowable) {  
          throw new UndeclaredThrowableException(localThrowable);  
        }  
      }  
      
      public final int hashCode() {  
        try {  
          return ((Integer)this.h.invoke(this, m0, null)).intValue();  
        }  
        catch (Error|RuntimeException localError) {  
          throw localError;  
        }  
        catch (Throwable localThrowable) {  
          throw new UndeclaredThrowableException(localThrowable);  
        }  
      }  
      
      public final String toString() {  
        try {  
          return (String)this.h.invoke(this, m2, null);  
        }  
        catch (Error|RuntimeException localError) {  
          throw localError;  
        }  
        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.Activeproxy.jdk.tagService").getMethod("printSomeThing", new Class[0]);  
          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());  
        }  
      }  
    }    
      
      public final boolean equals(Object paramObject) {  
        try {  
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();  
        }  
        catch (Error|RuntimeException localError) {  
          throw localError;  
        }  
        catch (Throwable localThrowable) {  
          throw new UndeclaredThrowableException(localThrowable);  
        }  
      }  
      
      public final void printSomeThing(String paramString) {  
        try {  
          this.h.invoke(this, m3, new Object[] { paramString });  
          return;  
        }  
        catch (Error|RuntimeException localError) {  
          throw localError;  
        }  
        catch (Throwable localThrowable) {  
          throw new UndeclaredThrowableException(localThrowable);  
        }  
      }  
      
      public final int hashCode() {  
        try {  
          return ((Integer)this.h.invoke(this, m0, null)).intValue();  
        }  
        catch (Error|RuntimeException localError) {  
          throw localError;  
        }  
        catch (Throwable localThrowable) {  
          throw new UndeclaredThrowableException(localThrowable);  
        }  
      }  
      
      public final String toString() {  
        try {  
          return (String)this.h.invoke(this, m2, null);  
        }  
        catch (Error|RuntimeException localError) {  
          throw localError;  
        }  
        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.Activeproxy.jdk.tagService").getMethod("printSomeThing", new Class[0]);  
          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());  
        }  
      }  
    }  

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

     

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

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

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

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

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

     

    这就是jdk的动态代理及为什么需要接口
     

     

    展开全文
  • 面试的时候,常常面试官会问spring 的aop底层原理,而aop的底层原理就是动态代理,每个JAVA程序员基本都能耳熟能详,对jdk动态代理cglib动态代理的区别也了然于胸,jdk动态代理只能基于接口,而cglib不需要。...

    面试的时候,常常面试官会问spring 的aop底层原理,而aop的底层原理就是动态代理,每个JAVA程序员基本都能耳熟能详,对jdk动态代理和cglib动态代理的区别也了然于胸,jdk动态代理只能基于接口,而cglib并不需要。

    那么jdk的动态代理为什么要基于接口呢?

    下面我们先实现一个简单的jdk动态代理

    接口类

    public interface Person {    void say();}

    实现类

    public class Man implements Person{    @Override    public void say() {        System.out.println("man");    }}

    代理类实现InvocationHandler接口,并重写invoke方法

    public class JDKDynamicProxy implements InvocationHandler {    private Object target;    public JDKDynamicProxy(Object target) {        this.target = target;    }    public  T getProxy() {        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("Do something before");        Object result = method.invoke(target, args);        System.out.println("Do something after");        return result;    }}

    调用一下

    public class Test {    public static void main(String[] args) {        Person person = new JDKDynamicProxy(new Man()).getProxy();        person.say();    }}

    调用结果如下

    Do something beforemanDo something after

    说明动态代理生效了。

    那么问题来了,jdk动态代理要求代理类实现InvocationHandler接口,重写invoke方法就可以实现动态代理,且构造函数注入Object即可,并无要求Object必须基于接口,而且重本质上说getProxy()获取代理类(Object的原类),完全可以通过继承对象的方式。

    因此再定义一个WonMan.class,不实现接口,再来测试一下

    public class Woman {    public void say(){        System.out.println("woman");    }}

    调用接口

    Woman woman = new JDKDynamicProxy(new Woman()).getProxy();woman.say();

    结果如下

    Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.ganhuojun.demo.proxy.Woman    at com.ganhuojun.demo.proxy.Test.main(Test.java:8)

    说明,确实不支持没有接口的object动态代理。

    接下来我们来分析下源码,探究下为什么只支持接口。因为动态代理的核心是获取代理类,我们下面方法开始看

    Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

    由于源码很多,只说简单说下核心部分,源码如下,红色部分是获取代理类

    1631d273475464011bca0179d703c749.png

    再看getProxyClass0方法,为了方便理解,将核心部分抽出来,如下

    private static final WeakCache[], Class>>        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());private static Class> getProxyClass0(ClassLoader loader,                                           Class>... interfaces) {    if (interfaces.length > 65535) {        throw new IllegalArgumentException("interface limit exceeded");    }    // If the proxy class defined by the given loader implementing    // the given interfaces exists, this will simply return the cached copy;    // otherwise, it will create the proxy class via the ProxyClassFactory    return proxyClassCache.get(loader, interfaces);}

    proxyClassCache是WeakCache,再看get方法,一步一步往下看,最终会看到调用到ProxyClassFactory类的apply()方法,如下

    4370675d3071655daa00eb1cd2db2f06.png

    此处发现红色部分是生成真正的代理类的方法,继续进去看一下,发现只需要将saveGeneratedFiles设置true,即可本地查看生成的代理类,如下

    a3e9943d7e975b83e9751ef00105b962.png

    saveGeneratedFiles的值如下,

    private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));

    此时我们将测试代码改成如下样子

    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");Person person = new JDKDynamicProxy(new Man()).getProxy();person.say();

    执行后发现本地多了一个代理类,如下图

    f515d4b66ecdb95114b9628aca4392fe.png

    打开看一下,源码如下,发现$Proxy0本身就继承了一个Proxy类,由于java不支持多继承,因此无法实现动态代理

    public final class $Proxy0 extends Proxy implements Person {    private static Method m1;    private static Method m2;    private static Method m3;    private static Method m0;    public $Proxy0(InvocationHandler var1) throws  {        super(var1);    }    public final boolean equals(Object var1) throws  {        try {            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});        } catch (RuntimeException | Error var3) {            throw var3;        } catch (Throwable var4) {            throw new UndeclaredThrowableException(var4);        }    }    public final String toString() throws  {        try {            return (String)super.h.invoke(this, m2, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    public final void say() throws  {        try {            super.h.invoke(this, m3, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    public final int hashCode() throws  {        try {            return (Integer)super.h.invoke(this, m0, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    static {        try {            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));            m2 = Class.forName("java.lang.Object").getMethod("toString");            m3 = Class.forName("com.ganhuojun.demo.proxy.Person").getMethod("say");            m0 = Class.forName("java.lang.Object").getMethod("hashCode");        } catch (NoSuchMethodException var2) {            throw new NoSuchMethodError(var2.getMessage());        } catch (ClassNotFoundException var3) {            throw new NoClassDefFoundError(var3.getMessage());        }    }}

    结论:

    生成的代理类继承了Proxy,由于java是单继承,所以只能实现接口,通过接口实现

    展开全文
  • Spring的AOP为什么需要引入aspectjrt-1.6.8.jar与aspectjweaver-1.6.8.jar这两个包?如果是接口直接使用JDK的动态代理,如果是普通类则用cglib不就完了吗?这是我的第一个问题 问题二~~~ 第二个问题是Spring使用...
  • 一直待明白的是为什么,jdk的动态代理需要接口才能实现,这也是其短板和令人诟病的地方。很多的博文说的很复杂,代码一大堆,没有太明白。手打了一下,参考了一些优秀的博文,在这里给自己做个总结。首...
  • 开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能 ,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要..
  • 前言 如果你学习过spring,那么你一定接触和使用过Aop。...java原生的动态代理代理的对象必须要实现一个顶级接口,而cglib的动态代理不需要这样的接口。在很长一段时间里很迷惑为什么java的动态代...
  • 3 代理-动态代理-CGLIB

    2021-03-10 17:23:58
    为什么要用代理模式 中介隔离作用:在某些情况下,一个使用者想或者能直接引用一个被代理对象,而代理类对象可以在使用者和被委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。 开闭原则,...
  • 一、为什么要使用动态代理 其实动态代理是弥补了静态代理的短板,静态代理需要给每一个被代理对象写一个代理类,这就需要编写大量冗余代码,而动态代理,只需要编写一个代理类,就可以代理多个不同的对象,大大减少...
  • 首先简单说明下为什么需要代理模式:为其他对象提供一种代理以控制对这个对象的访问,可以隔离客户端和委托类的中介。我们还可以借助代理来在增加一些功能,而不需要修改原有代码。 重点是代理模式的三种实现方式:...
  • 一直待明白的是为什么,jdk的动态代理需要接口才能实现,这也是其短板和令人诟病的地方。很多的博文说的很复杂,代码一大堆,没有太明白。手打了一下,参考了一些优秀的博文,在这里给自己做个总结。 首先,动态...
  • 代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口代理类负责委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类并真正实现服务,而是具有委托类的实例对象,...
  • 为什么要用代理?其实就是希望修改对象的情况下,增强对象。 静态代理: 静态代理模式,需要代理类和目标类实现同一接口代理类的方法调用目标类的方法,调用方法的前后实现需要增强的逻辑。 静态代理有一个...
  • 某一个对象创建一个代理对象,程序直接用原本的对象,而是由创建的代理对象来控制原对象,通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也实施不同控制...
  • 为什么要用动态代理? 因为静态代理需要额外编写代理类,对于每一个要代理的对象,都要书写一个额外的代理类。 使用代理的原因? 有些类是能够直接访问的或者有些访问要经过特殊处理。 1. Java JDK中的动态...
  • 代理可以理解中介:用户能直接通过厂家购买少量的商品,只能通过淘宝等中间商购买,但是淘宝商家不是直接供货的,只是间接的供货—购买的事件最终是调用的目标方法是厂家提供的。 作用:1.目标方法的调用,2....
  • cglib.jar下载

    2018-01-20 21:25:51
    CGLIB介绍与原理(部分节选自网络) 一、什么CGLIB?...cglib-nodep-2.2.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类. cglib-2.2.jar:使用此jar包需要关联asm的jar包,否则运行时报错.
  • aop默认代理方式是什么

    千次阅读 2018-08-15 10:50:19
    如果proxy-target-class 属性值被设置true,那么基于类的代理将起作用(这时需要cglib库)。 如果proxy-target-class属值被设置false或者这个属性被省略,那么标准的JDK 基于接口代理。 如果给出...
  • 1、静态代理 ...2) 不需要依赖某个具体业务 动态代理分为:JDK动态代理CGLib动态代理 区别是: JDK动态代理的被代理者必须实现任意接口 CGLib动态代理不用实现接口,是通过继承实现的 JDK动态代
  • 什么是代理模式 我觉得代理模式就是一个对象提供...代理模式有不同的形式,主要有三种静态代理、动态代理(jdk代理)和cglib代理。这次主要谈论静态代理模式 什么是静态代理模式 静态代理在使用时需要定义接口或者...
  • 代理模式

    2018-03-14 16:40:23
    代理模式什么代理模式 所谓代理模式就是目标对象提供另外一种访问方式,通过访问代理对象来间接访问目标对象。优点是在修改原来方法的情况下,给现有的对象中的方法追加额外的功能,即扩展目标对象的功能。...
  • JDK动态代理

    2020-10-25 20:52:14
    为什么要使用代理呢,因为我们常常需要对既有的代码增强,又希望改变现在的代码,所以就出现了AOP(面向界面编程),JDK动态代理就是AOP的实现方式之一。 首先由于JDK动态代理用于目标类实现了接口,所以首先需要...
  • 什么代理模式? 代理模式(Proxy),其他对象提供一种代理以控制对这个对象的访问。 代理模式结构图: 分类: 静态代理 动态代理 jdk cglib 满足代理模式应用的三个必要条件: 1、两个角色:被代理对象,执行者 ...
  • 代理模式实际上就是对于能直接访问的对象提供给一个代理对象,通过代理对象可以访问该对象的功能,这样做的有点...2、基于子类的动态代理(第三方的CGLib)要求被代理的类能用final修饰的类(最终类,为什么不能用f
  • 什么是Aop

    2021-01-10 23:08:04
    aop:面向切面编程是软件编程思想发展到一定阶段的产物,是面向对象编程(OOP) 的有益补充。AOP一般适用于具有横切逻辑的场合,如安全控制、...JDK动态代理需要接口的支持,如果没有接口只有类,则使 用cglib实现。 ...
  • 前几天有个朋友问了一个问题,...AOP都是基于动态代理来实现,而动态代理常见的就是cglib和java动态代理了解的可以看下之前干货君写的文章java动态代理为什么需要基于接口cglib动态代理对类没有任何限制吗?但此...

空空如也

空空如也

1 2 3
收藏数 47
精华内容 18
关键字:

为什么cglib代理不需要接口