jdk动态代理和cglib动态代理

Posted ghghg

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jdk动态代理和cglib动态代理相关的知识,希望对你有一定的参考价值。

静态代理就不说了...

jdk动态代理

  1. 在java中动态代理主要有一个接口InvocationHandler和Proxy
  2. 实现InvocationHandler接口的并不是代理类,他主要封装了调用方法,invoke(ClassLoader,Interfaces[],InvocationHandler),以加载器作为参数是因为在java中只有类加载器和类相同的两个对象才是同一类对象(即使是同一个类new出来的,但类加载器不同,这两对象是不同Class的对象),接口作为参数,主要是想获取接口中的方法定义,InvocationHandler可以获取到被代理的对象(获取到this的所有field就可以获取注入的被代理类),InvocationHandler并不是代理类.
  3. public interface Student {
        void learn();
        void playGame();
    }

    公共接口

  4. public class Hg implements Student {
    
        @Override
        public void learn() {
            System.out.println("hg爱学习");
        }
    
        @Override
        public void playGame() {
            System.out.println("hg爱玩游戏");
        }
    }

    被代理的类

  5. public class HgHandler implements InvocationHandler {
        private Object obj=null;
        public Object getProxy(Object obj){
            this.obj=obj;
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
        }
        //proxy是代理对象的参数,这个参数并不能直接转为代理类接口,但可以返回,因此可以连续调用
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if(method.getName().equals("learn")){
                System.out.println("爱学习吗?");
            }
            if(method.getName().equals("playGame")){
                System.out.println("还敢打游戏?");
            }
            Object invoke = method.invoke(obj, args);
            return null;
        }
    }

    在这里注意:注入被代理对象是必须的,因为invoke方法中需要使用,而getProxy是非必须的,可以推迟到client中newInstance(),而且在方法中并没有使用proxy,那不由得产生疑问,那这个proxy放在这有啥作用呢,了解后发现,他虽然不能直接使用,但是他可以作为返回值返回,因此可以链式调用(即objProxy.learn().playGame();那如果我们利用proxy是代理这一特性在invoke中强转为Student后,不就可以直接使用了吗,看下图

  6. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Student hg=(Student)proxy;
            hg.playGame();
            return null;
        }

    技术图片结果发现堆栈溢出,这是什么操作,仔细想想,就是目前proxy还没有确定自己所代理的对象,然后又将他强转为Student,而代理又去找自己的代理,无限找下去,当然会溢出了

cglib动态代理

  1. jdk动态代理是jdk自带的,而cglib是依赖第三方库
  2. <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
          <groupId>cglib</groupId>
          <artifactId>cglib</artifactId>
          <version>3.3.0</version>
        </dependency>

     

  3. cglib动态代理主要有两部分,MethodInterceptor(方法拦截器)和Enhancer(提高器)在方法拦截器中被代理对象的方法进行包装,增强
  4. //cglib通过继承父类,并覆盖父类的方法来实现代理父类,所以一个类被final修饰,
    // 则该类不能被代理,一个方法被final修饰,则该方法不能贝被覆盖
    public class Person {
        public void dance(){
            System.out.println("person 爱跳舞");
        }
    
        public final void drink(){
            System.out.println("person 喝水");
        }
    }

     

  5. MyMethodInterceptor类
  6. public class MyMethodInterceptor implements MethodInterceptor {
        /**
         *
         * @param o cglib生成的代理对象
         * @param method 被代理对象方法
         * @param objects 方法入参
         * @param methodProxy 代理方法
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("前置检查");
            Object o1 = methodProxy.invokeSuper(o, objects);
            System.out.println("后置检查");
            return o1;
        }
    }

    method在方法中并没有使用,使用了被代理对象的invokeSuper的methodProxy方法,增强

  7. 测试类
  8. public class TestCglib {
        public static void main(String[] args) {
            Enhancer enhancer=new Enhancer();
            enhancer.setSuperclass(Person.class);
            enhancer.setCallback(new MyMethodInterceptor());
            Person per = (Person)enhancer.create();
            per.dance();
            per.drink();
        }
    }

     

  9. 执行结果
  10. 技术图片
  11. 可见喝水由于是final修饰的,得不到增强,如果将Person类用final修饰,将会在运行时报错
//cglib通过继承父类,并覆盖父类的方法来实现代理父类,所以一个类被final修饰,
// 则该类不能被代理,一个方法被final修饰,则该方法不能贝被覆盖
public class Person {
public void dance(){
System.out.println("person 爱跳舞");
}

public final void drink(){
System.out.println("person 喝水");
}
}

以上是关于jdk动态代理和cglib动态代理的主要内容,如果未能解决你的问题,请参考以下文章

JDK和Cglib动态代理

jdk动态代理和cglib动态代理

深入理解设计模式-代理模式(静态代理动态代理jdk和cglib)

JDK动态代理与CGLib动态代理

(java反射-JDK动态代理)+CGLIB动态代理

jdk动态代理和cglib动态代理底层实现原理超详细解析(jdk动态代理篇)