JDK动态代理和Cglib动态代理原理与区别

2.2动态代理实现

2.2.1JDK动态代理实现

只能针对接口进行代理

public class JDKProxy {
    public static void main(String[] args) {

        //目标对象
        Targer targer = new Targer();

        //当前类的类加载器
        ClassLoader loader = JDKProxy.class.getClassLoader();

        //Proxy.newProxyInstance : 创建一个新的代理实例,运行期间生成代理类的字节码
        /*
                参数说明:
                loader 加载代理类在运行期间动态生成的字节码
                new Class[]可以代理多个Foo接口
                对行为进行封装
         */
        Foo proxy = (Foo)Proxy.newProxyInstance(loader, new Class[]{Foo.class}, new InvocationHandler() {
            //参数说明:代理对象自己、正在执行的方法对象、方法传来的实际参数
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("before...");
                //目标 方法(参数)
                //方法反射调用目标对象
                //目标对象 和 参数
                Object result = method.invoke(targer, args);
                //让代理也返回目标方法执行的结果
                System.out.println("after...");
                return result;
            }
        });

        //调用代理的父方法
        proxy.foo();
    }
    interface Foo{
        void foo();
    }

    static class Targer implements Foo{

        @Override
        public void foo() {
            System.out.println("target foo");
        }
    }
}
  • 代理类和目标类是兄弟关系,所以目标类可以被final修饰

原理:

以下是一个半成品的proxy,可以看到实现的方式很巧妙

  • 调用类
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author 我见青山多妩媚
 * @date 2023/4/15 0015 16:06
 * @Description TODO
 */
public class Proxy$0 {
    interface Foo{
        int foo();

        void bar();
    }

    static class Target implements Foo {

        @Override
        public int foo() {
            System.out.println("target foo");
            return 1;
        }

        @Override
        public void bar() {
            System.out.println("bar foo");
        }
    }

    public static void main(String[] args) {
        Foo proxy = new $Proxy0(new InvocationHandler() {
            @Override
            public Object invoke(Object proxy,Method method,Object[] args) throws InvocationTargetException, IllegalAccessException {
                //1.实现功能增强
                System.out.println("before...");
                //2.调用目标
//                new Target().foo();
                //反射调用
                return method.invoke(new Target(),args);
            }
        });
        proxy.foo();
        proxy.bar();
    }

    //抽象成一个代理接口,所有代理的方法就不用写在代理内内部,具体实现具体做,不会写死,成为动态代理
    interface InvocationHandler{
        public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
    }

}
  • 代理类
import com.example.springreaddemo.SpringRead.Demo02_SpringAop.proxy.JDK.Proxy$0.Foo;
import com.example.springreaddemo.SpringRead.Demo02_SpringAop.proxy.JDK.Proxy$0.InvocationHandler;

import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;

/**
 * @author 我见青山多妩媚
 * @date 2023/4/15 0015 15:59
 * @Description TODO
 */
//JDK 代理类
public class $Proxy0 implements Foo  {
    private InvocationHandler invocationHandler;

    public $Proxy0(InvocationHandler invocationHandler){
        this.invocationHandler = invocationHandler;
    }

    @Override
    public int foo() {
        try {
            //将foo的方法 以及 方法参数传过去,调用foo方法
            Object invoke = invocationHandler.invoke(this,foo, new Object[0]);
            return (int)invoke;
        } catch (RuntimeException | Error e) {  //运行时异常直接抛出
            throw e;
        } catch (Throwable e){
            throw new UndeclaredThrowableException(e);  //检查异常转化为运行异常抛出
        }
    }

    @Override
    public void bar() {
        try {
            //将foo的方法 以及 方法参数传过去,调用bar方法
            invocationHandler.invoke(this,bar,new Object[0]);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    static Method foo;
    static Method bar;
    static {
        try {
            foo = Foo.class.getMethod("foo");
            bar = Foo.class.getMethod("bar");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());  //比较严重的问题
        }
    }

}

JDK生成的代理类,不会有源码阶段,直接到字节码,这个动态生成代理类并且直接到字节码的方式叫ASM

2.2.2cglib代理实现

与JDK不同,可以针对很对进行代理

public class CglibProxy {

    static class Target{
        public void foo(){
            System.out.println("target foo...");
        }
    }

    public static void main(String[] args) {
        Target target = new Target();


        Target proxy = (Target)Enhancer.create(Target.class, new MethodInterceptor() {
            @Override
            /*
                参数:
                        1.o 代理类自己
                        2.method 代理类内执行的方法
                        3.objects 参数
                        4.方法对象,和method参数有些区别
             */
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("before...");
//                method.invoke(target,objects);  //方法反射调用目标
                //methodProxy 可以避免反射调用 内部没有用反射
//                Object result = methodProxy.invoke(target, objects);  //需要目标才能调用自己  spring使用的是这个
                Object result = methodProxy.invokeSuper(o, objects);    //内部没有用反射,需要代理
                System.out.println("after...");
                return result;
            }
        });

        proxy.foo();
    }
}
  • 代理类是目标类的子类,所以目标类不能被final修饰,

  • 因为是代理,相当于是对目标类的方法重写,所以方法也不能被final修饰

内部实现:

我们知道cglib是根据继承实现的,我们这有个父类

  • Target
public class Target {
    public void save(){
        System.out.println("save");
    }

    public void save(int i){
        System.out.println(i+"save");
    }

    public void save(long i){
        System.out.println("save long");
    }
}
  • Proxy
public class Proxy extends Target {

    private MethodInterceptor methodInterceptor;

    public void setMethodInterceptor(MethodInterceptor methodInterceptor){
        this.methodInterceptor = methodInterceptor;
    }

    static Method save0;
    static Method save1;
    static Method save2;

    static MethodProxy save0Proxy;
    static MethodProxy save1Proxy;
    static MethodProxy save2Proxy;


    static {
        try {
            save0 = Target.class.getMethod("save");
            save1 = Target.class.getMethod("save", int.class);
            save2 = Target.class.getMethod("save", long.class);

            //参数:
            //目标对象
            //代理对象
            //方法参数描述符
            //带增强功能的方法
            //带原始功能的方法
            save0Proxy = MethodProxy.create(Target.class,Proxy.class,"()V","save","saveSuper");
            //参数类型为整型
            save1Proxy = MethodProxy.create(Target.class,Proxy.class,"(I)V","save","saveSuper");
            //长整型为大写J
            save2Proxy = MethodProxy.create(Target.class,Proxy.class,"(J)V","save","saveSuper");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

    //代理原始功能的方法
    public void saveSuper(){
        super.save();
    }

    public void saveSuper(int i){
        super.save(i);
    }

    public void saveSuper(long i){
        super.save(i);
    }


    //属于待增强功能的方法
    @Override
    public void save() {
        try {
            methodInterceptor.intercept(this,save0,new Object[0],save0Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void save(int i) {
        try {
            methodInterceptor.intercept(this,save1,new Object[]{i},save1Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void save(long i) {
        try {
            methodInterceptor.intercept(this,save2,new Object[]{i},save2Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}
  • Main
public class Main {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        Target target = new Target();
        proxy.setMethodInterceptor(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects,
                                    MethodProxy methodProxy) throws Throwable {
                System.out.println("before...");
                //内部有反射
//                return method.invoke(target,objects);
                //内部无反射调用 结合目标使用
//                return methodProxy.invoke(target,objects);
                //内部无反射
                return methodProxy.invokeSuper(o,objects);
            }
        });
        proxy.save();
        proxy.save(1);
        proxy.save(2L);
    }
}

内部实现,和jdk的区别,他可以做到不使用反射进行动态的代理

不使用反射调用的原理:

package com.example.springreaddemo.SpringRead.Demo02_SpringAop.proxy.Cglib;

import org.springframework.cglib.core.Signature;

/**
 * @author 我见青山多妩媚
 * @date 2023/4/16 0016 20:21
 * @Description TODO
 */
//desc 不用反射实现代理的原理
public class TargetFastClass {

    //将目标对象的方法对应为三个签名
    static Signature s0 = new Signature("save","()V");
    static Signature s1 = new Signature("save","(I)V");
    static Signature s2 = new Signature("save","(J)V");


    /**
     * 根据方法的前面信息,获取目标方法的编号
     *
     * save             0
     * save(int i)      1
     * save(long i)     2
     * @param signature 签名 包括方法的名字、参数、返回值等等
     * @return 编号
     */
    public int getIndex(Signature signature){
        if(s0.equals(signature)){
            return 0;
        }else if (s1.equals(signature)){
            return 1;
        }else if(s2.equals(signature)){
            return 2;
        }else {
            return -1;
        }
    }

    /**
     * 根据获取到的方法编号,调用目标对象中的方法
     * @param index 编号
     * @param target 目标对象
     * @param args 参数列表
     * @return 返回目标对象
     */
    public Object invoke(int index,Object target,Object[] args){
         if(index == 0){
             ((Target) target).save();
             return null;
         }else if(index == 1){
             ((Target) target).save((int) args[0]);
             return null;
         }else if(index == 2){
             ((Target) target).save((long) args[0]);
             return null;
         }else{
             throw new RuntimeException("无此方法");
         }
    }
}

MethodProxy内部不使用反射的原理:

public class ProxyFastClass {
    //将目标对象的方法对应为三个签名
    static Signature s0 = new Signature("saveSuper","()V");
    static Signature s1 = new Signature("saveSuper","(I)V");
    static Signature s2 = new Signature("saveSuper","(J)V");


    /**
     * 根据方法的前面信息,获取目标方法的编号
     *
     * save             0
     * save(int i)      1
     * save(long i)     2
     * @param signature 签名 包括方法的名字、参数、返回值等等
     * @return 编号
     */
    public int getIndex(Signature signature){
        if(s0.equals(signature)){
            return 0;
        }else if (s1.equals(signature)){
            return 1;
        }else if(s2.equals(signature)){
            return 2;
        }else {
            return -1;
        }
    }

    /**
     * 根据获取到的方法编号,调用目标对象中的方法
     * @param index 编号
     * @param proxy 代理对象
     * @param args 参数列表
     * @return 返回目标对象
     */
    public Object invoke(int index,Object proxy,Object[] args){
        if(index == 0){
            ((Proxy) proxy).saveSuper();
            return null;
        }else if(index == 1){
            ((Proxy) proxy).saveSuper((int) args[0]);
            return null;
        }else if(index == 2){
            ((Proxy) proxy).saveSuper((long) args[0]);
            return null;
        }else{
            throw new RuntimeException("无此方法");
        }
    }

    public static void main(String[] args) {
        ProxyFastClass proxyFastClass = new ProxyFastClass();
        int index = proxyFastClass.getIndex(new Signature("saveSuper", "()V"));
        System.out.println(index);
        proxyFastClass.invoke(index,new Proxy(),new Object[0]);
    }
}

实际上就是调用了代理类

区别

jdk在调用第17次时一个方法生成一个代理类,而cglib会给生成一个代理类,对应两个fastClass,一个对应目标代理,一个对代理类代理,生成的代理类数目比jdk少

你可能感兴趣的:(java,开发语言,jvm)