静态代理和动态代理学习笔记

本文详细介绍了代理模式的概念及其实现方式,包括静态代理和动态代理(JDK Proxy和CGLib)。通过示例代码展示了如何使用这些代理模式进行方法增强和对象延迟加载,同时对比了JDK Proxy与CGLib的优劣。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先我们先来了解代理模式
代理模式,包含
抽象角色:声明真实对象和代理对象的共同接口; 
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。 
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

也就是说实现上述三种类,就是实现了代理模式,而这在Spring AOP,Hibernate等框架中大量使用
代理分两种,一种是静态代理,代理类拥有实现类的所有代理方法。

首先是接口类Output:

public interface Output {
  public void output(); 
}

真实实现类RealOutput:
public class RealOutput implements Output{
 
 public RealOutput() {
  //模拟重量级对象构造方法
  try {
   Thread.sleep(1000);
  } catch (InterruptedException e) {
   
   e.printStackTrace();
  }
 }
 @Override
 public void output() {
  System.out.println("我是实现类");
 }
}

代理类ProxyOutput:

public class ProxyOutput implements Output{
  //实现类对象
 private Output output;
 
 public ProxyOutput(Output output) {
  this.output = output;
 }
 
 public ProxyOutput() {
 
 }

 @Override
 public void output() {
 
  System.out.println("我是代理类");
  //实现方法增强
  System.out.println("方法增强前");
  if(output == null) {
   output = new RealOutput();
  }
  //调用实现类方法
  output.output();
  System.out.println("方法增强后");
 }
}

进行测试:

public static void main(String[] args) {
 
  Output output = new ProxyOutput();
  output.output();
 }

结果如下:


使用静态代理可以实现代理模式(延迟加载和方法增强),但缺点也显而易见,你需要构建一个拥有与实现类同样多方法的代理类,才能实现代理模式,那有没有其它更好的方法呢?
就是我们的动态代理了。
动态代理有几种实现方式:

JDK Proxy JavaEE自带动态代理
要使用JDK的动态代理,你需要继承InvocationHandler接口,并实现invoke方法,并在需要调用现实类的方法中调用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法返回一个代理类,然后调用其方法,完成动态代理过程。
newProxyInstance方法中第一个参数是实现类接口类加载方法,第二个是给出实现类的接口集,第三个参数是代理类对象。


首先是接口SuperClass:

public interface SuperClass {
 
 public void select();
 
 public String select(String abc);
 
 public String[] select(String abc, String d);

}

以及其实现类
public class SubClass implements SuperClass{
 
 @Override
 public void select() {
  System.out.println("请求一个查询动作");
 }

 @Override
 public void select(String param) {
  //增删查改功能
  System.out.println("请求一个带参数的查询动作: " + param);
 }
 
 public SubClass(){
  System.out.println("我是只类构造器");
  //模拟重量级构造方法
  try {
   Thread.sleep(1000);
  } catch(Exception ex) {
   ex.printStackTrace();
  }
 }

 @Override
 public void select(String[] params) {
  System.out.println("带多个参数的查询动作" + params);
 }

}

若想要方法增强或者对象延迟加载,则需要实现接口类InvocationHandler的invoke方法。

这是实现类ProxySubClass

public class ProxySubClass implements InvocationHandler{
 
 private SubClass subClass;

 @Override
 public Object invoke(Object proxy, Method method, Object[] args)
   throws Throwable {
  System.out.println("Invoke a proxy");
  //在inovke方法里面调用proxy方法会再一次引发invoke方法,死循环栈溢出
  System.out.println("被调用的对象是: " + proxy.getClass().getAnnotations());
  subClass = new SubClass(); //realize lazy loading.
  System.out.println("被拦截的方法: " + method.getName());
  System.out.println(args);
  Object result = method.invoke(subClass, args);
  System.out.println("finish invoke");
  return result;
 }

}

在invoke方法中,第一个参数是动态生成并集成同一接口的实例对象,如果不是调用proxy的final类方法,则会导致无限调用其增强方法而报OutOfMemory异常。第二个参数是被调用的方法,第三个参数是方法参数列表

测试代码:
public static void main(String[] args) {
 
  ProxySubClass proxy = new ProxySubClass();
  //实现的结果是生成一个集成接口的对象,而不是目标类
  SuperClass enhanceSub = (SuperClass) Proxy.newProxyInstance(SuperClass.class.getClassLoader(),
                   SubClass.class.getInterfaces(), proxy);
 
  enhanceSub.select("rich");
 }

结果:



这是JDK的动态代理,可以实现延迟加载和方法增强。但是如果你要增强的方法的类没有继承接口的话,是不能使用JDK自带的增强代理的,因为代理生成的类是需要继承与实际需要增强的类相同的接口,不然Proxy的newProxyInstance方法无法调用生成,这时,CGLib的动态代理方法就可以帮你实现方法增强了。

Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类。使用CGLIB即使被代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDKProxy动态代理。


使用CGLIB需要导入以下两个jar文件
cglib.jar
asm.jar

我们依然以SubClass作为需要增强的类,首先需要一个类继承MethodInterceptor接口类,实现其intercept方法


实现类CGLibSubClass:

public class CGLibSubClass implements MethodInterceptor{

 @Override
 public Object intercept(Object object, Method method, Object[] objects,
   MethodProxy methodPorxy) throws Throwable {
  System.out.println("调用开始");
  System.out.println("被拦截的方法是: " + method.getName());
  System.out.println("被拦截的对象是: " +object.getClass()); //不能调用object对象的非final方法,否则死循环堆栈溢出
  System.out.println(objects == null ? "无参方法" : objects.length);
  Object result = methodPorxy.invokeSuper(object, objects); //调用父类的方法
  System.out.println("调用结束");
  return result;
 }

}

与JDK的动态代理是差不多的,但这里有一个methodPorxy对象表示的是Method对象的一个对象,使用invokeSuper方法调用父类的方法。

测试:
public static void main(String[] args) {
  Enhancer enhancer = new Enhancer();
  CGLibSubClass cglibProxy = new CGLibSubClass();
  enhancer.setSuperclass(SubClass.class);
//	 enhancer.setInterfaces(new Class[]{SubClass.class});
  enhancer.setCallback(cglibProxy);
  SubClass subClass = (SubClass) enhancer.create();
  subClass.select(); //调用方法

 }

结果:


CGLIB用途十分广泛。很多框架都在使用,搞懂对自己框架代码的理解有十分好的帮助。














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值