面试系列之动态代理

代理是一种设计模式,提供了对目标对象其他的访问方式,即通过代理对象访问目标对象。

优点是可以在目标对象实现的基础上,扩展一些功能。

例如:格力工厂生产了很多空调,通过各地的经销商来卖空调,这种通过经销商卖空调的模式就是代理模式。

常见的代理模式分为静态代理和动态代理,动态代理在Java中的实现分为JDK动态代理和CGLIB代理。

如果目标对象实现了接口,则会采用JDK动态代理。

如果目标对象没有实现接口,则会默认采用CGLIB代理。

静态代理和动态代理模式主要涉及三个角色:

Service:接口,该接口是对象和它的代理共用的接口

TargetObject:目标对象,是抽象接口的实现类

Proxy:代理对象,内部含有对TargetObject的引用,可以操作真实对象,代理对象也会实现该抽象接口,代理对象在执行目标对象操作时,扩展了其他的操作。

动态代理

称为JDK代理,接口代理,动态代理就是在程序运行过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程,代理的目的也是调用目标对象时可以转而执行InvocationHandler的invoke方法,主要依赖Proxy类的newProxyInstance方法。

JDK动态代理基于接口的原因:

生成的代理类继承了Proxy,由于Java是单继承,所以只能实现接口

CGLIB代理:

静态代理和动态代理模式都要求目标对象要实现一个接口,但是有的时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可以使用CGLIB代理。

CGLIB采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的方式拦截所有父类方法的调用,顺势织入横切逻辑。

使用注意:

1、代理的类不能为final类

2、目标对象的方法如果为final或者static,那么就不会被拦截

静态代理的代码示例:

1、定义一个售卖接口SellService,里边包含一个卖空调的方法

public interface SellService {
  //卖空调
  void sellAirConditioner();
}

2、编写SellService的实现类SellServiceImpl,实现卖空调的方法

public class SellServiceImpl implements SellService {
  @Override
  public void sellAirConditioner() {
    System.out.println("我卖的是格力空调,请大家赶快来购买,有88折优惠。");
  }
}

3、编写一个经销商DealerProxy来卖空调,通过构造方法注入SellService接口,在调用卖空调方法前后扩展一些其他的服务,如支付,安排配送、入户安装等。

public class DealerProxy implements SellService{
  SellService sellService;

  public DealerProxy(SellService sellService) {
    this.sellService = sellService;
  }

  @Override
  public void sellAirConditioner() {
    System.out.println("签订合同,支付空调费用");
    sellService.sellAirConditioner();
    System.out.println("安排师傅配送到家,帮用户安装空调。");
  }
}

4、写个测试类

public class ProxyTest {

  public static void main(String[] args) {
    SellService sellService = new SellServiceImpl();
    DealerProxy proxy = new DealerProxy(sellService);
    proxy.sellAirConditioner();
    
  }
}

5、测试结果

动态代理

动态代理涉及了一个非常重要的类Proxy,正是通过Proxy的静态方法newProxyInstance才会动态创建代理。

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

ClassLoader:类加载器

interfaces:代码要用来代理的接口

InvocationHandler:代理的类实现该接口,如果代理的方法被调用,那么代理便会通知和转发给内部的InvocationHandler实现类。

代码事例如下:

1、先定一个接口SellService

public interface SellService {
  //卖空调
  void sellAirConditioner();
}

2、编写该接口的实现类SellServiceImpl

public class SellServiceImpl implements SellService {
  @Override
  public void sellAirConditioner() {
    System.out.println("我卖的是格力空调,请大家赶快来购买,有88折优惠。");
  }
}

3、编写一个代理类TotalDealerProxy实现InvocationHandler接口,在invoke方法中扩展一些逻辑

public class TotalDealerProxy implements InvocationHandler {
  SellService sellService;

  public TotalDealerProxy(SellService sellService) {
    this.sellService = sellService;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("够买前,我在总经销商这里拿到了一些赠品饮水机");
    Object result = method.invoke(sellService,args);
    System.out.println("购买后,赠送负责入户安装的服务");
    return result;
  }
}

4、编写测试类

public class DealerProxyTest {
  public static void main(String[] args) {
    SellService  service = new SellServiceImpl();
    TotalDealerProxy proxy = new TotalDealerProxy(service);
    SellService ss = (SellService)Proxy.newProxyInstance(service.getClass().getClassLoader(),service.getClass().getInterfaces(),proxy);
    ss.sellAirConditioner();
  }

}

5、测试结果

CGLIB代理:

cglib代理,也叫子类代理,它是在内存中构建一个子类对象从而实现对目标对象的扩展功能。

CGLIB采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的方式拦截所有父类方法的调用,顺势织入横切逻辑。

接口的代码示例如下:

1、先创建一个接口SellService

public interface SellService {
  //卖空调
  void sellAirConditioner();
}

2、编写该接口的实现类

public class SellServiceImpl implements SellService {
  @Override
  public void sellAirConditioner() {
    System.out.println("我卖的是格力空调,请大家赶快来购买,有88折优惠。");
  }
}

3、编写一个代理类CgLibFactory,实现接口MethodInterceptor

public class CgLibFactory implements MethodInterceptor {
  private Object target;

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

  public Object getProxyInstance() {
    //1.工具类
    Enhancer en = new Enhancer();
    //2.设置父类
    en.setSuperclass(target.getClass());
    //3.设置回调函数
    en.setCallback(this);
    //4.创建子类(代理对象)
    return en.create();

  }

  @Override
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
      throws Throwable {
    System.out.println("CGLIB拦截开始啦");
    Object obj = method.invoke(target,objects);
    System.out.println("CGLIB拦截结束啦");
    return obj;
  }
}

4、编写一个测试类

public class CglibTest {
  public static void main(String[] args) {
    SellService sellService = new SellServiceImpl();
    SellService ss = (SellService)new CgLibFactory(sellService).getProxyInstance();
    ss.sellAirConditioner();

  }
}

5、执行结果

CGLIB类的代码示例:

1、编写一个Work类,写一个实现方法handle

public class Work {

  void handle() {
    System.out.println("因为疫情,我在家里工作。");
  }
}

2、编写一个代理类WorkProxy,在里边做一些代码增强

public class WorkProxy implements MethodInterceptor {
  Object targetObject;

  public WorkProxy(Object targetObject) {
    this.targetObject = targetObject;
  }

  public Object getProxyInstance() {
    //1.工具类
    Enhancer en = new Enhancer();
    //2.设置父类
    en.setSuperclass(targetObject.getClass());
    //3.设置回调函数
    en.setCallback(this);
    //4.创建子类(代理对象)
    return en.create();

  }

  @Override
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
      throws Throwable {
    System.out.println("进行正式工作前的准备,喝水");
    Object oo = method.invoke(targetObject,objects);
    System.out.println("结束工作了,我要下班了");
    return oo;
  }
}

3、编写一个测试类WorkTest

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

    Work work = new Work();
    WorkProxy proxy = new WorkProxy(work);
    Work ww = (Work)proxy.getProxyInstance();
    ww.handle();
    
  }
}

4、测试结果

参考文档:

Java的三种代理模式 - 洋葱源码 - 博客园

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值