为您推荐:
精华内容
最热下载
问答
  • 33KB weixin_38669628 2019-03-01 16:54:39
  • Cglib和jdk动态代理区别? 1、Jdk动态代理:利用拦截器(必须实现InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理 2、 Cglib动态代理:利用ASM框架,对代理...

    Cglib和jdk动态代理的区别?

    1、Jdk动态代理:利用拦截器(必须实现InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理

    2、 Cglib动态代理:利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来处理

    什么时候用cglib什么时候用jdk动态代理?

    1、目标对象生成了接口 默认用JDK动态代理

    2、如果目标对象使用了接口,可以强制使用cglib

    3、如果目标对象没有实现接口,必须采用cglib库,Spring会自动在JDK动态代理和cglib之间转换

    JDK动态代理和cglib字节码生成的区别?

    1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类

    2、Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,但是因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的

    Cglib比JDK快?

    1、cglib底层是ASM字节码生成框架,但是字节码技术生成代理类,在JDL1.6之前比使用java反射的效率要高

    2、在jdk6之后逐步对JDK动态代理进行了优化,在调用次数比较少时效率高于cglib代理效率

    3、只有在大量调用的时候cglib的效率高,但是在1.8的时候JDK的效率已高于cglib

    4、Cglib不能对声明final的方法进行代理,因为cglib是动态生成代理对象,final关键字修饰的类不可变只能被引用不能被修改

    JDK动态代理实现:

    /**
     * 创建动态代理对象
     * 动态代理不需要实现接口,但是需要指定接口类型
     */
    public class ProxyFactory{
    
        //维护一个目标对象
        private Object target;
        public ProxyFactory(Object target){
            this.target=target;
        }
    
       //给目标对象生成代理对象
        public Object getProxyInstance(){
            return Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("开始事务2");
                            //执行目标对象方法
                            Object returnValue = method.invoke(target, args);
                            System.out.println("提交事务2");
                            return returnValue;
                        }
                    }
            );
        }
    
    }
    
    /**
     * 测试类
     */
    public class App {
        public static void main(String[] args) {
            // 目标对象
            IUserDao target = new UserDao();
            // 【原始的类型 class cn.itcast.b_dynamic.UserDao】
            System.out.println(target.getClass());
    
            // 给目标对象,创建代理对象
            IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
            // class $Proxy0   内存中动态生成的代理对象
            System.out.println(proxy.getClass());
    
            // 执行方法   【代理对象】
            proxy.save();
        }
    }

    Cglib代理实现

    /**
     * Cglib子类代理工厂
     * 对UserDao在内存中动态构建一个子类对象
     */
    public class ProxyFactory implements MethodInterceptor{
        //维护目标对象
        private Object target;
    
        public ProxyFactory(Object target) {
            this.target = target;
        }
    
        //给目标对象创建一个代理对象
        public Object getProxyInstance(){
            //1.工具类
            Enhancer en = new Enhancer();
            //2.设置父类
            en.setSuperclass(target.getClass());
            //3.设置回调函数
            en.setCallback(this);
            //4.创建子类(代理对象)
            return en.create();
    
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("开始事务...");
    
            //执行目标对象的方法
            Object returnValue = method.invoke(target, args);
    
            System.out.println("提交事务...");
    
            return returnValue;
        }
    }
    
    /**
     * 测试类
     */
    public class App {
    
        @Test
        public void test(){
            //目标对象
            UserDao target = new UserDao();
    
            //代理对象
            UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
    
            //执行代理对象的方法
            proxy.save();
        }
    }

    展开全文
    qq_25118431 2021-10-08 11:09:37
  • 动态的创建一个代理类出来, 创建这个代理类的实例对象,...如果某个类没有实现接口, 那么springaop 会改用cglib 去生成动态代理. 去生成原有类的子类, 可以动态的生成字节码. 覆盖原有的方法, 在方法里去加入增强的代码.

    动态的创建一个代理类出来, 创建这个代理类的实例对象, 注入的对象, 也是走的代理的对象.
    负责做一些代码上的增强, 去调用原有的类.

    如果实现了某个接口的, 那么Spring的aop会使用jdk的动态代理. 会去生成一个同样接口的代理类, 构造一个实例对象出来, jdk生成的代理类会去生成一个实现一样接口的类.

    如果某个类没有实现接口, 那么springaop 会改用cglib 去生成动态代理. 去生成原有类的子类, 可以动态的生成字节码. 覆盖原有的方法, 在方法里去加入增强的代码.

    jdk代理代码示例

    代理类去实现InvocationHandler 接口
    jdk动态代理代码示例
    Proxy.newProxyInstance 去生成代理类, invoke 方法执行目标方法, 并进行增强.
    jdk动态代理会生成一个动态代理类,生成相应的字节码,然后通过ClassLoader加载字节码

    cglib动态代理代码示例

    cglib动态代理代码示例
    如果类是final 或者方法是final的 ,那么无法代理.
    实现MethodInterceptor 接口
    getInstance 去创建代理对象. intercept 方法去执行代理的方法和目标方法.

    展开全文
    qq_33229669 2021-06-05 14:24:56
  • 在Java中一般是2种,静态代理和动态代理动态代理又分为CGLIB和jdk自带。   3,如何使用? 3.1静态代理: public class StaticProxy { public static void main(String[] args) { Singer singer = new Agent(new ...

     

    1,引入

    如果从一个Controller调用Service的非事务方法a,然后在a里调用事务方法b,b事务生效吗?

     public void update() {
            updateActual();
            int a = 1 / 0;
        }
        @Transactional
        public void updateActual() {
            WithHoldInfoVO vo = new WithHoldInfoVO();
            vo.setId(18);
            vo.setStatus(5);
            withholdMapper.updateWithHoldInfo(vo);
            WithHoldInfoVO vo1 = new WithHoldInfoVO();
            vo1.setId(27);
            vo1.setStatus(5);
            withholdMapper.updateWithHoldInfo(vo1);
        }

    形如这样,如果从Controller层直接调用update方法,updateActual方法的事务可以生效吗?如果不能,那么如何才能使事务生效?


    2,什么是代理?

    代理,简单来说,就是代替处理,代替原有操作者去处理一件事。在Java中一般是2种,静态代理和动态代理,动态代理又分为CGLIB和jdk自带。

     

    3,如何使用?

    3.1静态代理:

    public class StaticProxy {
        public static void main(String[] args) {
            Singer singer = new Agent(new Star());
            singer.sing();
        }
    }
    /**
    * 代理实现,代理了歌星,唱歌的时候 会先在歌手唱歌之前收钱,然后再唱歌
    */
    class Agent implements Singer {
        Star s;
        public Agent(Star s) {
            super();
            this.s = s;
        }
        @Override
        public void sing() {
            System.out.println("在歌手唱歌之前收钱....");
            s.sing();
        }
    }
    /**
    * 真实实现,一个歌星
    */
    class Star implements Singer {
        @Override
        public void sing() {
            System.out.println("Star Singing~~~");
        }
    }
    /**
    * 最顶层接口 歌手
    */
    interface Singer {
        void sing();
    }

    3.2 jdk动态代理

     

    @SuppressWarnings("restriction")
    public class JavaProxyTest {
        public static void main(String[] args) throws Exception {
            JavaProxyInterface javaProxyInterface = new ConcreteClass();
            
            JavaProxyInterface newJavaProxyInterface = (JavaProxyInterface) Proxy.newProxyInstance(
                    JavaProxyTest.class.getClassLoader(), new Class[] { JavaProxyInterface.class },
                    new MyInvocationHandler(javaProxyInterface));
            //这里可以看到这个类以及被代理,在执行方法前会执行aopMethod()。这里需要注意的是oneDay()方法和oneDayFinal()的区别。oneDayFinal的方法aopMethod执行1次,oneDay的aopMethod执行1次
            newJavaProxyInterface.gotoSchool();
            newJavaProxyInterface.gotoWork();
            newJavaProxyInterface.oneDayFinal();
            newJavaProxyInterface.oneDay();
        }
    }
    /**
    * InvocationHandler 的一个实现,实际上处理代理的逻辑在这里
    */
    class MyInvocationHandler implements InvocationHandler {
        JavaProxyInterface javaProxy;
        public MyInvocationHandler(JavaProxyInterface javaProxy) {
            this.javaProxy = javaProxy;
        }
        private void aopMethod() {
            System.out.println("before method");
        }
    	//继承方法,代理时实际执行的犯法,如果要实现原方法,则需要调用method.invoke(javaProxy, args),这里还调用了一个aopMethod(),可以类比于Spring中的切面before注解。
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            aopMethod();
            return method.invoke(javaProxy, args);
        }
    }
    /**
    * 需要一个最顶层接口,必须
    */
    interface JavaProxyInterface {
        void gotoSchool();
        void gotoWork();
        void oneDay();
        void oneDayFinal();
    }
    /**
    * 需要被代理的类,实现了顶层接口,非必须
    */
    class ConcreteClass implements JavaProxyInterface {
        @Override
        public void gotoSchool() {
            System.out.println("gotoSchool");
        }
        @Override
        public void gotoWork() {
            System.out.println("gotoWork");
        }
        @Override
        public void oneDay() {
            gotoSchool();
            gotoWork();
        }
        @Override
        public final void oneDayFinal() {
            gotoSchool();
            gotoWork();
        }
    }

    3.3 cglib 动态代理

     
    public class CglibProxyTest {
        public static void main(String[] args) throws Exception {
            CglibTestSon CglibTestSon = new CglibTestSon();
            Enhancer enhancer = new Enhancer();
            Callback s = new MthdInvoker(CglibTestSon);
            enhancer.setSuperclass(CglibTestSon.class);
            Callback callbacks[] = new Callback[] { s };
            enhancer.setCallbacks(callbacks);
            CglibTestSon CglibTestSon2 = (CglibTestSon) enhancer.create();
            CglibTestSon2.gotoHome();
            CglibTestSon2.gotoSchool();
    		这里可以看到这个类以及被代理,在执行方法前会执行aopMethod()。这里需要注意的是oneDay()方法和onedayFinal()的区别。onedayFinal的方法aopMethod执行2次,oneDay的aopMethod执行1次 ,注意这里和jdk的代理的区别
            CglibTestSon2.oneday();
            CglibTestSon2.onedayFinal();
        }
    }
    /**
    * 需要被代理的类,不需要实现顶层接口
    */
    class CglibTestSon {
        public CglibTestSon() {
        }
        public void gotoHome() {
            System.out.println("============gotoHome============");
        }
        public void gotoSchool() {
            System.out.println("===========gotoSchool============");
        }
        public void oneday() {
            gotoHome();
            gotoSchool();
        }
        public final void onedayFinal() {
            gotoHome();
            gotoSchool();
        }
    }
    /**
    * 可以类比于jdk动态代理中的InvocationHandler ,实际上被代理后重要的类,实际上后续执行的就是intercept里的方法,如果需要执行原来的方法,则调用 method.invoke(s, args);这里也加了一个aopMethod();
    */
    class MthdInvoker implements MethodInterceptor {
        private CglibTestSon s;
        public MthdInvoker(CglibTestSon s) {
            this.s = s;
        }
        private void aopMethod() {
            System.out.println("i am aopMethod");
        }
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            aopMethod();
            Object a = method.invoke(s, args);
            return a;
        }
    }
     
    cglib 需要的maven配置:
    <dependencies>
    		<dependency>
    			<groupId>cglib</groupId>
    			<artifactId>cglib</artifactId>
    			<version>3.2.6</version>
    		</dependency>
    </dependencies>
        
    
    

    4,区别

    名称备注
    静态代理简单,代理模式,是动态代理的理论基础。常见使用在代理模式
    jdk动态代理

    需要有顶层接口才能使用,但是在只有顶层接口的时候也可以使用,常见是mybatis的mapper文件是代理。

    使用反射完成。使用了动态生成字节码技术。

    cglib动态代理可以直接代理类,使用字节码技术,不能对 final类进行继承。使用了动态生成字节码技术。

    5,底层怎么实现呢?

    都知道是动态代理是使用动态生成字节码技术生成了字节码,那么生成的字节码有什么区别呢?怎么看生成的字节码呢?如何生成的字节码呢?

    5.1 jdk底层实现

    字节码最后是靠这一句来生成,

    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces); // proxyName 为类名,interfaces为顶层接口Class
    //如果需要看,可以将字节码写入文件进行观察
    File file = new File("D:/testProxy/Ddd.class");
    FileOutputStream fileOutputStream = new FileOutputStream(file);
    fileOutputStream.write(bs);
    fileOutputStream.flush();
    fileOutputStream.close();

    自己观察可以从Proxy.newProxyInstance( ClassLoader paramClassLoader,  Class<?>[] paramArrayOfClass,  InvocationHandler paramInvocationHandler)进行观察,简单来看就是先生成新的class文件,然后加载到jvm中,然后使用反射,先用class取得他的构造方法,然后使用构造方法反射得到他的一个实例。

    标红的是最复杂的。然后cglib的实现原理基本一致,唯一的区别在于生成新的class文件方式和结果不一样。

    5.2 cglib 底层实现

    直接上最终生成字节码的代码。需要注意的是这里要想执行这句话,必须在实现cglib动态代理的代码之后,否则取不到DefaultGeneratorStrategy的实例。

    byte[] bs = DefaultGeneratorStrategy.INSTANCE.generate(enhancer);
    FileOutputStream fileOutputStream = new FileOutputStream("D:/testProxy/Cc.class");
    fileOutputStream.write(bs);
    fileOutputStream.flush();
    fileOutputStream.close();

    跟踪enhancer.create(); 这个代码可以看具体怎么生成字节码,怎么将字节码转化为类。过程基本雷同于jdk,主要区别在于生成字节码的区别。

    5.3 小结

    这里生成代码的过程中,都使用了缓存,jdk自带的使用了weakReference引用,而cglib使用的直接是 WeakHashMap,基本也类似。

    6,生成的字节码到底是什么样子的呢?

    6.1 cglib

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package aaa;
    
    import java.lang.reflect.Method;
    import net.sf.cglib.core.ReflectUtils;
    import net.sf.cglib.core.Signature;
    import net.sf.cglib.proxy.Callback;
    import net.sf.cglib.proxy.Factory;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CglibTestSon$$EnhancerByCGLIB$$3119e01d extends CglibTestSon implements Factory {
        private boolean CGLIB$BOUND;
        public static Object CGLIB$FACTORY_DATA;
        private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
        private static final Callback[] CGLIB$STATIC_CALLBACKS;
        private MethodInterceptor CGLIB$CALLBACK_0;
        private static Object CGLIB$CALLBACK_FILTER;
        private static final Method CGLIB$gotoHome$0$Method;
        private static final MethodProxy CGLIB$gotoHome$0$Proxy;
        private static final Object[] CGLIB$emptyArgs;
        private static final Method CGLIB$gotoSchool$1$Method;
        private static final MethodProxy CGLIB$gotoSchool$1$Proxy;
        private static final Method CGLIB$oneday$2$Method;
        private static final MethodProxy CGLIB$oneday$2$Proxy;
        private static final Method CGLIB$equals$3$Method;
        private static final MethodProxy CGLIB$equals$3$Proxy;
        private static final Method CGLIB$toString$4$Method;
        private static final MethodProxy CGLIB$toString$4$Proxy;
        private static final Method CGLIB$hashCode$5$Method;
        private static final MethodProxy CGLIB$hashCode$5$Proxy;
        private static final Method CGLIB$clone$6$Method;
        private static final MethodProxy CGLIB$clone$6$Proxy;
    
        static void CGLIB$STATICHOOK2() {
            CGLIB$THREAD_CALLBACKS = new ThreadLocal();
            CGLIB$emptyArgs = new Object[0];
            Class var0 = Class.forName("com.rrc.finance.CglibTestSon$$EnhancerByCGLIB$$3119e01d");
            Class var1;
            Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
            CGLIB$equals$3$Method = var10000[0];
            CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
            CGLIB$toString$4$Method = var10000[1];
            CGLIB$toString$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
            CGLIB$hashCode$5$Method = var10000[2];
            CGLIB$hashCode$5$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$5");
            CGLIB$clone$6$Method = var10000[3];
            CGLIB$clone$6$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
            var10000 = ReflectUtils.findMethods(new String[]{"gotoHome", "()V", "gotoSchool", "()V", "oneday", "()V"}, (var1 = Class.forName("com.rrc.finance.CglibTestSon")).getDeclaredMethods());
            CGLIB$gotoHome$0$Method = var10000[0];
            CGLIB$gotoHome$0$Proxy = MethodProxy.create(var1, var0, "()V", "gotoHome", "CGLIB$gotoHome$0");
            CGLIB$gotoSchool$1$Method = var10000[1];
            CGLIB$gotoSchool$1$Proxy = MethodProxy.create(var1, var0, "()V", "gotoSchool", "CGLIB$gotoSchool$1");
            CGLIB$oneday$2$Method = var10000[2];
            CGLIB$oneday$2$Proxy = MethodProxy.create(var1, var0, "()V", "oneday", "CGLIB$oneday$2");
        }
    
        final void CGLIB$gotoHome$0() {
            super.gotoHome();
        }
    
        public final void gotoHome() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                var10000.intercept(this, CGLIB$gotoHome$0$Method, CGLIB$emptyArgs, CGLIB$gotoHome$0$Proxy);
            } else {
                super.gotoHome();
            }
        }
    
        final void CGLIB$gotoSchool$1() {
            super.gotoSchool();
        }
    
        public final void gotoSchool() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                var10000.intercept(this, CGLIB$gotoSchool$1$Method, CGLIB$emptyArgs, CGLIB$gotoSchool$1$Proxy);
            } else {
                super.gotoSchool();
            }
        }
    
        final void CGLIB$oneday$2() {
            super.oneday();
        }
    
        public final void oneday() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                var10000.intercept(this, CGLIB$oneday$2$Method, CGLIB$emptyArgs, CGLIB$oneday$2$Proxy);
            } else {
                super.oneday();
            }
        }
    
        final boolean CGLIB$equals$3(Object var1) {
            return super.equals(var1);
        }
    
        public final boolean equals(Object var1) {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                Object var2 = var10000.intercept(this, CGLIB$equals$3$Method, new Object[]{var1}, CGLIB$equals$3$Proxy);
                return var2 == null ? false : (Boolean)var2;
            } else {
                return super.equals(var1);
            }
        }
    
        final String CGLIB$toString$4() {
            return super.toString();
        }
    
        public final String toString() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$4$Method, CGLIB$emptyArgs, CGLIB$toString$4$Proxy) : super.toString();
        }
    
        final int CGLIB$hashCode$5() {
            return super.hashCode();
        }
    
        public final int hashCode() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                Object var1 = var10000.intercept(this, CGLIB$hashCode$5$Method, CGLIB$emptyArgs, CGLIB$hashCode$5$Proxy);
                return var1 == null ? 0 : ((Number)var1).intValue();
            } else {
                return super.hashCode();
            }
        }
    
        final Object CGLIB$clone$6() throws CloneNotSupportedException {
            return super.clone();
        }
    
        protected final Object clone() throws CloneNotSupportedException {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            return var10000 != null ? var10000.intercept(this, CGLIB$clone$6$Method, CGLIB$emptyArgs, CGLIB$clone$6$Proxy) : super.clone();
        }
    
        public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
            String var10000 = var0.toString();
            switch(var10000.hashCode()) {
            case -1311123629:
                if (var10000.equals("gotoHome()V")) {
                    return CGLIB$gotoHome$0$Proxy;
                }
                break;
            case -508378822:
                if (var10000.equals("clone()Ljava/lang/Object;")) {
                    return CGLIB$clone$6$Proxy;
                }
                break;
            case 800823646:
                if (var10000.equals("gotoSchool()V")) {
                    return CGLIB$gotoSchool$1$Proxy;
                }
                break;
            case 1826985398:
                if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                    return CGLIB$equals$3$Proxy;
                }
                break;
            case 1913648695:
                if (var10000.equals("toString()Ljava/lang/String;")) {
                    return CGLIB$toString$4$Proxy;
                }
                break;
            case 1984935277:
                if (var10000.equals("hashCode()I")) {
                    return CGLIB$hashCode$5$Proxy;
                }
                break;
            case 2071325759:
                if (var10000.equals("oneday()V")) {
                    return CGLIB$oneday$2$Proxy;
                }
            }
    
            return null;
        }
    
        public CglibTestSon$$EnhancerByCGLIB$$3119e01d() {
            CGLIB$BIND_CALLBACKS(this);
        }
    
        public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
            CGLIB$THREAD_CALLBACKS.set(var0);
        }
    
        public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
            CGLIB$STATIC_CALLBACKS = var0;
        }
    
        private static final void CGLIB$BIND_CALLBACKS(Object var0) {
            CglibTestSon$$EnhancerByCGLIB$$3119e01d var1 = (CglibTestSon$$EnhancerByCGLIB$$3119e01d)var0;
            if (!var1.CGLIB$BOUND) {
                var1.CGLIB$BOUND = true;
                Object var10000 = CGLIB$THREAD_CALLBACKS.get();
                if (var10000 == null) {
                    var10000 = CGLIB$STATIC_CALLBACKS;
                    if (CGLIB$STATIC_CALLBACKS == null) {
                        return;
                    }
                }
    
                var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
            }
    
        }
    
        public Object newInstance(Callback[] var1) {
            CGLIB$SET_THREAD_CALLBACKS(var1);
            CglibTestSon$$EnhancerByCGLIB$$3119e01d var10000 = new CglibTestSon$$EnhancerByCGLIB$$3119e01d();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        }
    
        public Object newInstance(Callback var1) {
            CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
            CglibTestSon$$EnhancerByCGLIB$$3119e01d var10000 = new CglibTestSon$$EnhancerByCGLIB$$3119e01d();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        }
    
        public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
            CGLIB$SET_THREAD_CALLBACKS(var3);
            CglibTestSon$$EnhancerByCGLIB$$3119e01d var10000 = new CglibTestSon$$EnhancerByCGLIB$$3119e01d;
            switch(var1.length) {
            case 0:
                var10000.<init>();
                CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
                return var10000;
            default:
                throw new IllegalArgumentException("Constructor not found");
            }
        }
    
        public Callback getCallback(int var1) {
            CGLIB$BIND_CALLBACKS(this);
            MethodInterceptor var10000;
            switch(var1) {
            case 0:
                var10000 = this.CGLIB$CALLBACK_0;
                break;
            default:
                var10000 = null;
            }
    
            return var10000;
        }
    
        public void setCallback(int var1, Callback var2) {
            switch(var1) {
            case 0:
                this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
            default:
            }
        }
    
        public Callback[] getCallbacks() {
            CGLIB$BIND_CALLBACKS(this);
            return new Callback[]{this.CGLIB$CALLBACK_0};
        }
    
        public void setCallbacks(Callback[] var1) {
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
        }
    
        static {
            CGLIB$STATICHOOK2();
        }
    }
    
    

    6.2 jdk

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    import com.rrc.finance.JavaProxyInterface;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class Ddd extends Proxy implements JavaProxyInterface {
        private static Method m1;
        private static Method m5;
        private static Method m3;
        private static Method m2;
        private static Method m4;
        private static Method m6;
        private static Method m0;
    
        public Ddd(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 void gotoSchool() throws  {
            try {
                super.h.invoke(this, m5, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final void oneDay() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        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 oneDayFinal() throws  {
            try {
                super.h.invoke(this, m4, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final void gotoWork() throws  {
            try {
                super.h.invoke(this, m6, (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"));
                m5 = Class.forName("com.rrc.finance.JavaProxyInterface").getMethod("gotoSchool");
                m3 = Class.forName("com.rrc.finance.JavaProxyInterface").getMethod("oneDay");
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m4 = Class.forName("com.rrc.finance.JavaProxyInterface").getMethod("oneDayFinal");
                m6 = Class.forName("com.rrc.finance.JavaProxyInterface").getMethod("gotoWork");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    
    

     

    可以看到cglib是直接继承了原有类,实现了Factory接口,而jdk是实现了自己的顶层接口,继承了Proxy接口。这里需要注意一下,这样的话,按照类来找话,jdk就找不到他的实现了,因为他的实现类实际上是一个Proxy类,而不是他自己。

    这句话通俗点的意思,可以看下一小节。

    7,Spring中使用的是哪种代理呢?

    先上结论,如果一个类有顶层接口,则默认使用jdk的动态代理来代理,如果直接是一个类,则使用cglib动态代理。 其次,如果没有需要代理的方法,如所有方法都没有@Transactional注解,Aop这种,则不会被代理。

    问题来了,只说结论,没有依据,怎么让人信服?

    @Service
    public class TestServiceImpl implements TestService {
        @Transactional
        public void updateActual() {
            
        }
        
    }

    在这里,我们实现一个Service,让他实现一个顶层接口,然后我们如果在Controller里使用

    @Autowired
    private TestServiceImpl testServiceImpl;

    注解来注入,这时候会发现,启动时报错的。

    报错也很明显:

    The bean 'testServiceImpl' could not be injected as a 'com.rrc.finance.service.apply.TestServiceImpl' because it is a JDK dynamic proxy that implements:
    	com.rrc.finance.service.apply.TestService

    改成 :@Autowired
    private  TestService  testServiceImpl; 

    即可正常启动。

    证明动态代理生成的代码是一个  TestService 却不是一个TestServiceImpl。使用的是jdk的动态代理。

    这里去掉事务注解和 去掉接口实现 自己可以再试一下。

    8,会了这些就完了吗?

    在Spring的体系下,大多数的实现都在使用动态代理,我们可以通过他们优秀的源码来进行学习,来写一个自己的项目。

    例如:Mybatis的mapper代理,分页插件的代理实现。Spring的Aop,事务注解等。


    展开全文
    doujinlong1 2018-06-13 15:46:52
  • 4星
    42KB kansuny 2012-11-06 20:55:59
  • 上周五在电面试阿里时,被问到Spring AOP中JDK和CGLib动态代理区别?于是搜集网上相关知识点,在此整理一下,供大家参考。 JDK和CGLib动态代理实现 动态代理在Java中有着广泛的应用,如Spring AOP,Hibernate...

    前言

    上周五在电面试阿里时,被问到Spring AOP中JDK和CGLib动态代理的区别?于是搜集网上相关知识点,在此整理一下,供大家参考。

    JDK和CGLib动态代理实现

    动态代理在Java中有着广泛的应用,如Spring AOP,Hibernate数据查询、测试框架的后端mock、RPC,Java注解对象获取等。动态代理的代理关系是在运行时期确定的。在讲解两种动态代理区别之前,首先通过实例代码分别实现两种动态代理,直观感受一下动态代理是个什么东西,是如何实现的,然后通过分析其实现方式和原理,阐述两种动态代理的区别。

    Jdk原生动态代理

    IHelloService接口

    public interface IHelloService {
        void sayHello();
    }

    HelloServiceImpl实现类

    public class HelloServiceImpl implements IHelloService {
        @Override
        public void sayHello() {
            System.out.println("Jdk say Hello");
        }
    }

    JdkDynamicProxy实现InvocationHandler接口类

    public class JdkDynamicProxy implements InvocationHandler {
    
        private Object targetObject;
    
        public Object newProxyInstance(Object targetObject) {
            //将目标对象传入进行代理
            this.targetObject = targetObject;
            //返回代理对象
            return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
        }
    
        @Override
        public Object invoke(Object proxyObject, Method method, Object[] args) throws Throwable {
            System.out.println("JdkDynamicProxy," + method.getName() + "方法,执行之前");
            return method.invoke(this.targetObject, args);
        }
    }

    在该类除了覆写invoke()方法,还封装了一个生成代理类的方法,该方法可以不放置在此的。上述代码的关键是Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) 方法,该方法会根据指定的参数动态创建代理对象。三个参数的意义如下:

    1. loader,指定代理对象的类加载器;
    2. interfaces,代理对象需要实现的接口,可以同时指定多个接口;
    3. handler,方法调用的实际处理者,代理对象的方法调用都会转发到这里(*注意1)。

    newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法。动态代理神奇的地方就是:

    1. 代理对象是在程序运行时产生的,而不是编译期;
    2. 对代理对象的所有接口方法调用都会转发到InvocationHandler.invoke()方法,在invoke()方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等。

    注意1:对于从Object中继承的方法,JDK Proxy会把hashCode()、equals()、toString()这三个非接口方法转发给InvocationHandler,其余的Object方法则不会转发。详见JDK Proxy官方文档

    测试代码:

    @Test
    public void jdkDynamicProxyPatternTest() {
        IHelloService helloService = new HelloServiceImpl();
        IHelloService proxySubject = (IHelloService) new JdkDynamicProxy2().newProxyInstance(helloService);
        proxySubject.sayHello();
    }

    运行结果:

    image.png

    JDK动态代理整个实现过程其实很简单,包括三个步骤:

    1. 定义一个接口IHelloService,大家都知道Jdk的动态代理是基于接口,这个就是那个接口。
    2. 编写一个实现该接口的类HelloServiceImpl,这个就是目标对象,也就是被代理的对象类。
    3. 最后编写一个实现InvocationHandler接口的类JdkDynamicProxy,代理类的方法调用都会被转发到该类的invoke()方法。

     

    CGLib动态代理

    HelloService没有实现任何接口的类

    public class HelloService {
        public void sayHello() {
            System.out.println("Cglib Say Hello");
        }
    }

    CglibDynamicProxy实现MethodInterceptor接口类

    public class CglibDynamicProxy implements MethodInterceptor {
    
        // 维护目标对象
        private Object target;
    
        public CglibDynamicProxy(Object target) {
            this.target = target;
        }
    
        //给目标对象创建一个代理对象
        public Object newProxyInstance(){
            //1.工具类
            Enhancer en = new Enhancer();
            //2.设置父类
            en.setSuperclass(target.getClass());
            //3.设置回调函数
            en.setCallback(this);
            //4.创建子类(代理对象)
            return en.create();
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("CglibDynamicProxy,"+ method.getName()+"方法,执行之前");
            Object returnValue = method.invoke(target, args);
            System.out.println("CglibDynamicProxy,"+ method.getName()+"方法,执行之后");
            return returnValue;
        }
    }

    上述代码中,通过CGLIB的Enhancer来指定要代理的目标对象(target.getClass())、实际处理代理逻辑的对象(this),最终通过调用create()方法得到代理对象,对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法,在intercept()方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等。CGLIG中MethodInterceptor的作用跟JDK代理中的InvocationHandler很类似,都是方法调用的中转站。

    注意:对于从Object中继承的方法,CGLIB代理也会进行代理,如hashCode()equals()toString()等,但是getClass()wait()等方法不会,因为它是final方法,CGLIB无法代理。

     

    测试代码:

    @Test
    public void cglibDynamicProxyPatternTest() {
        HelloService cglibProxySubject = (HelloService) new CglibDynamicProxy(new HelloService()).newProxyInstance();
        cglibProxySubject.sayHello();
    }

    运行结果:

    image.png

     

    JDK和CGLib动态代理分析

    自Java 1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了Spring的很多地方。JDK动态代理主要涉及java.lang.reflect包下边的两个类:Proxy和InvocationHandler。其中,InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑(如:我们在方法执行前后打印的日志,本文只是为了演示,实际的应用一般不会只是简单的打印日志的),并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。

     

    JDK动态代理的话,他有一个限制,就是它只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,如何创建动态代理实例哪?答案就是CGLib。


    CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。

     

    JDK和CGLib动态代理区别

    1、JDK动态代理具体实现原理:

    • 通过实现InvocationHandler接口创建自己的调用处理器;
    • 通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;
    • 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
    • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;

    JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。

    2、CGLib动态代理:

    利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    3、两者对比:

    • JDK动态代理是面向接口的。
    • CGLib动态代理是通过字节码底层继承要代理类来实现,因此如果被代理类被final关键字所修饰,会失败。

    4、使用注意:

    • 如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);
    • 如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。

     

    JDK和CGLib动态代理性能对比

    关于两者之间的性能的话,网上有人对于不通版本的jdk进行测试,经过多次试验,测试结果大致是这样的,在1.6和1.7的时候,JDK动态代理的速度要比CGLib动态代理的速度要慢,但是并没有教科书上的10倍差距,在JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了,但是JDK动态代理和CGLIB动态代理的适用场景还是不一样的哈!

     

    最后希望小伙伴在遇到这个问题的时候能够有的放矢!稍微把问题拓展一下,不要是说两者的区别,应该循序渐进,娓娓道来。

     

     

    参考文章:
    https://www.cnblogs.com/CarpenterLee/p/8241042.html

    https://blog.csdn.net/yhl_jxy/article/details/80635012

    展开全文
    shallynever 2019-12-02 16:38:25
  • qiaofengzxq 2019-11-15 10:11:46
  • u010310183 2019-01-29 15:11:40
  • u010870518 2018-09-07 16:52:54
  • weixin_41932830 2020-08-10 19:11:55
  • c_royi 2019-01-18 14:49:42
  • jinjiniao1 2019-06-16 17:34:44
  • qq_44718303 2021-08-22 16:27:47
  • flyfeifei66 2018-08-07 15:33:35
  • qq_42937522 2020-07-06 15:47:24
  • Leon_Jinhai_Sun 2020-11-05 23:07:33
  • qq_34310242 2017-09-20 22:31:10
  • qq_38763885 2020-12-10 15:32:07
  • 246KB hlsjtdtm 2017-07-04 21:10:12
  • 368KB gududedabai 2018-12-21 10:40:07
  • qq_43203949 2021-08-18 18:17:47
  • xiangbq 2015-11-12 09:38:30
  • whxwkb 2020-02-19 15:50:46
  • 9KB weixin_38669628 2019-03-27 01:18:03

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 56,793
精华内容 22,717
关键字:

cglib和jdk动态代理区别