设计模式-代理模式(静态代理、动态代理、cglib动态代理)AOP的底层动态代理

1、概述

代理模式分为静态代理和动态代理,动态代理又分为基于接口实现的动态代理基于子类实现的动态代理;在jdk源码中,很多底层的实现也是基于代理模式的,例如:

  • 创建线程的方式之一实现Runnable接口就使用了静态代理模式
  • Spring框架最为重要的AOP的实现是基于动态代理模式

代理模式通常有三个角色:

  • Subject:抽象主题角色,抽象主题类可以是抽象类,也可以是接口,是一个最普通的业务类型定义,无特殊要求。
  • RealSubject:具体主题角色,也叫被委托角色、被代理角色。是业务逻辑的具体执行者。
  • Proxy:代理主题角色,也叫委托类、代理类。它把所有抽象主题类定义的方法给具体主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后工作。

代理可以看作是对调用目标的一个包装,这样对目标代码的调用不是直接发生的,而是通过代理完成。

2、静态代理

静态代理通常用于对原有业务逻辑的扩充。比如你有一个已实现的功能类,并且调用了其中的某些方法。由于需求变更,你需要加入日志打印,但又不合适将逻辑直接加入其中。所以可以创建一个代理类来实现。


public class ProxyDemo {

    public static void main(String[] args) {
        Proxy proxy = new Proxy(new RealSubject());
        proxy.doSomething();
    }
}

/**
 * 抽象主题
 */
interface Subject {
    public void doSomething();
}

/**
 * 具体主题角色
 */
class RealSubject implements Subject{

    public void doSomething() {
        System.out.println("do something");
    }
}

/**
 * 代理主题角色
 */
class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }
    public void doSomething() {
        System.out.println("proxy do");
        realSubject.doSomething();
    }
}
  • 优点
    • 不破坏原代码,不影响原来老的功能
  • 缺点
    • 如果有10个这样的功能类RealSubject,而且我们要去代理的方法是不同的,比如代理:doSomething、doAnotherThing、doTwoAnotherThing等方法

3、动态代理

动态代理的目的就是为了解决静态代理的缺点。通过使用动态代理,我们可以通过在运行时,动态生成一个持有RealSubject、并实现代理接口的Proxy,同时注入我们相同的扩展逻辑。哪怕你要代理的RealSubject是不同的对象,甚至代理不同的方法,都可以动过动态代理,来扩展功能,动态代理可以代理多个类或对象

JDK的动态代理机制只能代理实现了接口的类

使用动态代理,需要将待扩展的功能类实现InvocationHandler ,并且需要将扩展代码写在该类的中


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyDemo {

    public static void main(String[] args) {

        RealSubject realSubject = new RealSubject();
        Subject proxy = (Subject)
                Proxy.newProxyInstance(ProxyDemo.class.getClassLoader(),
                        new Class[]{Subject.class}, new DynamicProxyHandler(realSubject));
        proxy.doSomething();
    }


}

/**
 * 抽象主题
 */
interface Subject {
    public void doSomething();
}

/**
 * 具体主题角色
 */
class RealSubject implements Subject{

    public void doSomething() {
        System.out.println("do something");
    }
}

/**
 * 动态代理的
 */
class DynamicProxyHandler implements InvocationHandler {
    private Object realObject;

    public DynamicProxyHandler(Object realObject) {
        this.realObject = realObject;
    }

    /**
     * 作用:执行被代理对象的任何接口方法都会经过该方法
     * 方法参数的含义
     * @param proxy   代理对象的引用
     * @param method  当前执行的方法
     * @param args    当前执行方法所需的参数
     * @return        和具体主题角色的方法有相同的返回值
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //代理扩展逻辑
        System.out.println("proxy do");

        return method.invoke(realObject, args);
    }
}

3.1、生成代理类的方法

public static void main(String[] args) throws Exception {
    //主要是第二个参数:其为要实现的接口(数组形式传递,这里传入Service接口)
    byte[]classFile = ProxyGenerator.generateProxyClass("Proxy0",new Class[]{Subject.class});
    FileOutputStream fos =new FileOutputStream(new File("Proxy0.class"));
    fos.write(classFile);
    fos.flush();
    fos.close();
}

3.2、查看生成的代理类


public final class Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    /**
    * DynamicProxyHandler中实现InvocationHandler的有参构造
    */
    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 void doSomething() throws  {
        try {
            //调用的是父类Proxy中的InvocationHandler里的invoke()方法(
            //    即调用 newProxyInstance 中的 DynamicProxyHandler)
            //传入参数依次为:代理类Proxy0实例、通过反射获得的Subject接口中的doSomething()的method的实例、doSomething()方法中的参数
            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 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);
        }
    }
    
    //静态代码块中通过反射获取到所有对应类的Method实例
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            // 动态代理的方法
            m3 = Class.forName("com.pattern.Subject").getMethod("doSomething");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

3.3、动态代理总结

  1. JDK 自身提供的动态代理,就是主要利用反射机制实现的。
  2. 通过代理可以让调用者与实现者之间解耦

4、cglib动态代理

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理

CGLIB是基于修改字节码生成子类来实现的,底层是ASM的开源库。

jar包

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>

demo

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxyDemo {
    public static void main(String[] args) {
        // 代理类class文件存入本地磁盘方便我们反编译查看源码,地址自己定义
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:/demo");
        // 通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        // 设置产生的代理对象的父类
        enhancer.setSuperclass(HelloService.class);
        // 设置CallBack接口的实例
        enhancer.setCallback(new CGLibProxyInterceptor());
        // 使用默认无参数的构造函数创建目标对象
        HelloService proxy= (HelloService)enhancer.create();
        // 通过代理对象调用目标方法
        proxy.sayHello();
        // 无法通过代理调用
        proxy.sayOthers("111");

    }
}


class HelloService {

    public HelloService() {
        System.out.println("HelloService构造");
    }

    /**
     * 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
     */
    final public String sayOthers(String name) {
        System.out.println("HelloService:sayOthers>>"+name);
        return null;
    }

    public void sayHello() {
        System.out.println("HelloService:sayHello");
    }
}

/**
 * CGLib代理拦截
 */
class CGLibProxyInterceptor implements MethodInterceptor {

    /**
     * sub:cglib生成的代理对象
     * method:被代理对象方法(要被拦截的方法)
     * objects:被拦截方法的参数
     * methodProxy: 要触发父类的方法对象(被代理对象)
     */
    @Override
    public Object intercept(Object sub, Method method, Object[] objects,
                            MethodProxy methodProxy) throws Throwable {
        System.out.println("=====插入前置通知=====");
        //调用原始类的被拦截到的方法
        Object o = methodProxy.invokeSuper(sub, objects);
        System.out.println("=====插入后置通知=====");
        return o;
    }

}

4.1、总结

  • JDK动态代理不需要任何外部依赖,但是只能基于接口进行代理;
  • CGLIB通过继承的方式进行代理,无论目标对象有没有实现接口都可以代理,但是无法代理final对象与final方法。(final类型不能有子类,final方法不能被重载)。
  • JDK动态代理,那只有public方法可以被代理。而如果使用CGLIB,除了private方法,都可以被代理。(当然,final方法除外)。

在JDK中就使用到了动态代理来实现AOP,有两种方式

  • 一种是基于JDK
  • 另一种是基于CGLIB方式。

5、AOP的底层动态代理

AOP的底层是代理模式,Spring的底层使用了两种方式实现了动态代理,一种是JDK的动态代理,还有一种是CGLib。 两者都有一定限制。

Spring默认产生代理对象的行为是:如果你的Bean有对应的接口,是使用的基于JDK的动态代理,否则是使用CGLIB。

Spring用了使用spring.aop.proxy-target-class=true控制代理模式

  • false:如果你的Bean有对应的接口,是使用的基于JDK的动态代理,否则是使用CGLIB。
  • true:所有的要使用AOP的Bean都使用CGLIB代理,不管它是不是有接口。SpringBoot 2.0以后这个值默认就是true(1.5的为false)。
    在这里插入图片描述

5.1、AOP和循环依赖

大家可能会遇到过或者听说过Spring的循环依赖的问题。Spring使用了“三级缓存”来解决Bean的循环依赖,但可能很多人不知道为什么要使用三级缓存,其实这个也跟AOP有关。

如果没有AOP,其实Spring使用二级缓存就可以解决循环依赖的问题。若使用二级缓存,在AOP情形下,注入到其他Bean的,不是最终的代理对象,而是原始目标对象。

因为Spring对Bean有一个生命周期的定义,而代理对象是在Bean初始化完成后,执行后置处理器的时候生成的。所以不能在二级缓存的时候就直接生成代理对象,放进缓存。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值