06.spring-动态代理

0 阅读3分钟

前言

java中的代理从本质上说都是要对被代理的类扩展一些功能或者不同的操作,分为静态代理和动态代理。而动态代理又分为JDK动态代理和cglib动态代理。大体如下图:

image-20230228092852840

静态代理

1. 静态代理概念

程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就已确定。静态代理其实就是代理模式的一个具体实现。来看下代理模式的类图:

image-20230228094442428

2. 示例

  • 定义接口

    public interface Persion {
        /**
         * 吃的方法
         */
        void eat();
    }
    
  • 委托类

    public class PersionImpl implements Persion {
        /**
         * 吃的方法
         */
        public void eat() {
            System.out.println("吃大米饭");
        }
    }
    
  • 代理类

    public class PersionProxy implements Persion {
        private Persion persion;
        public PersionProxy(Persion persion){
            this.persion = persion;
        }
    
        /**
         * 吃的方法
         */
        public void eat() {
            System.out.println("吃饭前洗手");
            this.persion.eat();
            System.out.println("吃饭后洗碗");
        }
    }
    
  • 测试代码

    Persion persion = new PersionImpl();
    persion.eat();
    System.out.println("=========代理类执行==========");
    Persion persionProxy = new PersionProxy(persion);
    persionProxy.eat();
    

动态代理

​ 动态代理可以针对于一些不特定的类或者一些不特定的方法进行代理,我们可以在程序运行时动态的变化代理的规则,代理类在程序运行时才创建的代理模式成为动态代理。这种情况下,代理类并不是在Java代码中定义好的,而是在程序运行时根据我们的在Java代码中的“指示”动态生成的。

JDK动态代理

1. 概念

​ jdk动态代理中通过实现InvocationHandler接口创建动态代理类,通过实现InvocationHandler接口中的invoke方法实现对被代理的方法进行扩展。一个动态代理类对象可以对多个被代理对象进行代理。

2. 示例

  • 代理类

    public class PersionJdkProxy implements InvocationHandler {
    
        private Object persionObj;
    
        public PersionJdkProxy(Object persionObj) {
            this.persionObj = persionObj;
        }
    
        /**
         * proxy : 代理对象
         * method: 被代理对象的方法
         * args: 被代理对象方法运行时的参数
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            if(method.getName().equals("eat")){
                System.out.println("吃饭前洗手");
                result = method.invoke(this.persionObj, args);
                System.out.println("吃饭后洗碗");
            }else {
                // 其他方法正常执行
                result = method.invoke(this.persionObj, args);
            }
            return result;
        }
    }
    
  • 测试

    Persion persion = new PersionImpl();
    Persion persionProxy = (Persion) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
            new Class<?>[]{Persion.class},
            new PersionJdkProxy(persion));
    persion.eat();
    System.out.println("=========代理类执行==========");
    persionProxy.eat();
    

CGLIB动态代理

1. 概念

​ Cglib(Code Generation Library)是个功能强大、高性能、开源的代码生成包,它可以为没有实现接口的类提供代理。具体而言,Cglib继承被代理的类,覆写其业务方法来实现代理。因为采用继承机制,所以不能对final修饰的类进行代理。

2. 示例

  • 代理类

    public class PersionCglibProxy implements MethodInterceptor {
        /**
         * @param o 代理对象persionProxy
         * @param method 父类原本要执行的方法 Persion.eat()方法
         * @param objects 调用时传入的参数
         * @param methodProxy 子类中重写父类的方法persionProxy.eat()
         * @return
         * @throws Throwable
         */
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            Object result = null;
            if(method.getName().equals("eat")){
                System.out.println("吃饭前洗手");
                result = methodProxy.invokeSuper(o, objects);
                System.out.println("吃饭后洗碗");
            }else {
                // 其他方法正常执行
                result = methodProxy.invokeSuper(o, objects);
            }
            return result;
        }
    }
    
  • 测试

    Persion persion = new PersionImpl();
    // 这里注意需要通过persion.getClass来设置Class;还可以通过PersionImpl.class来设置;
    // 但是绝对不能通过Persion这个接口类来设置,cglilb是要继承你的具体实现类的.
    Persion persionProxy = (Persion) Enhancer.create(PersionImpl.class, new PersionCglibProxy());
    persion.eat();
    System.out.println("=========代理类执行==========");
    persionProxy.eat();
    

代码地址

gitee.com/mayuanfei/S… 下的SpringProxy05

记忆印记

  • 代理是在不动原来类的代码的继承上,对原类的方法进行增强或者更改
  • 静态代理是用代码写死的方式对某个类进行扩展;而动态代理则能在运行期指定要代理的类
  • JDK动态代理的核心是要有接口,通过反射实现
  • CGLIB动态代理的核心是通过继承要代理的类,覆写方法实现