设计模式之代理模式

96 阅读3分钟

一、概述

  • 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象
  • 好处:可以在目标对象实现的基础上,增强额外的功能操作,即拓展目标对象的功能
  • 三种形式:静态代理、动态代理(JDK代理、接口代理)、Cglib代理(可以在内存动态的创建对象,而不需要实现接口,它是属于动态代理的范畴)

二、静态代理

  • 在使用时,需要定义接口或父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同的父类 在这里插入图片描述
public interface ITeacherDao {
  void teach();
}
public class TeacherDao implements ITeacherDao {
  @Override
  public void teach() {
    System.out.println("老师上课中");
  }
}
public class TeacherDaoProxy implements ITeacherDao {
  private ITeacherDao iTeacherDao;

  public TeacherDaoProxy(ITeacherDao iTeacherDao) {
    this.iTeacherDao = iTeacherDao;
  }

  @Override
  public void teach() {
    System.out.println("代理正式开始");
    iTeacherDao.teach();
    System.out.println("代理结束");
  }
}
public class Client {
  public static void main(String[] args) {
    ITeacherDao teacherDaoProxy = new TeacherDaoProxy(new TeacherDao());
    teacherDaoProxy.teach();
  }
}
  • 优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能拓展
  • 缺点:因为代理对象需要与目标实现一样的接口,所以会有很多代理类
  • 一旦接口增加方法,目标对象与代理对象都要维护

三、动态代理

  • 代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
  • 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
  • 动态代理也叫做:JDK代理、接口代理
  • 只需通过Proxy.newProxyInstance()方法即可获取代理对象,但是该方法需要接收三个参数,完整写法是:static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    • loader:为被代理对象的ClassLoader
    • interfaces:被代理对象需要被代理的接口,如果只需要代理部分接口,那就把要被代理的接口写上去
    • h:存放代理内容,InvocationHandler的实现类
  • 与静态代理相同的案例: 在这里插入图片描述
  • 代理工厂
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("proxy 之前");
        Object res = method.invoke(target, args);
        System.out.println("proxy 之后");
        return res;
      }
    });
  }
}
  • 调用
public class Client {
  public static void main(String[] args) {
    ITeacherDao target = new TeacherDao();
    ProxyFactory proxyFactory = new ProxyFactory(target);
    ITeacherDao instance = (ITeacherDao) proxyFactory.getProxyInstance();
    instance.teach();
  }
}

四、Cglib代理

  • 静态代理和JDK代理模式都要求目标对象实现一个接口,但是有时候一个对象没有接口,那就需要使用目标对象的子类来实现代理,这就是Cglib代理
  • Cglib代理又叫子类代理,它在内存中构建一个子类对象从而实现对目标对象功能拓展
  • AOP编程中如何选择代理模式:
    • 目标对象有接口,就是用JDK代理
    • 目标对象没有接口,用Cglib代理
  • Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类 在这里插入图片描述
  • 导入依赖
<dependencies>
    <!-- https://mvnrepository.com/artifact/org.ow2.asm/asm -->
    <dependency>
        <groupId>org.ow2.asm</groupId>
        <artifactId>asm</artifactId>
        <version>7.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.ow2.asm/asm-commons -->
    <dependency>
        <groupId>org.ow2.asm</groupId>
        <artifactId>asm-commons</artifactId>
        <version>7.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.ow2.asm/asm-tree -->
    <dependency>
        <groupId>org.ow2.asm</groupId>
        <artifactId>asm-tree</artifactId>
        <version>7.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/cglib/cglib-nodep -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib-nodep</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>
  • 代理工厂
public class ProxyFactory implements MethodInterceptor {
  private Object target;

  public ProxyFactory(Object target) {
    this.target = target;
  }

  public Object getProxyInstance() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(target.getClass());
    enhancer.setCallback(this);
    return enhancer.create();
  }

  @Override
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    System.out.println("cglib 代理模式 开始");
    Object returnVal = method.invoke(target, objects);   // 反射
    System.out.println("cglib 代理模式 提交");
    return returnVal;
  }
}
  • 调用
public class Client {
  public static void main(String[] args) {
    ProxyFactory proxyFactory = new ProxyFactory(new TeacherDao());
    TeacherDao instance = (TeacherDao) proxyFactory.getProxyInstance();
    instance.teach();
  }
}