代理:java动态代理和静态代理

动态代理:类加载器,上下文加载器,

代理模式,是一种结构性设计模式,通过创建一个代理对象来控制对原有对象的访问。一般的,代理模式中有两个角色,代理角色和真实角色。代理类负责代理真实类,为真实类提供访问控制的功能,真实类其实是完成具体的业务逻辑。

所以,代理的主要目的就是

  • 保护目标对象
  • 增强目标对象

实现代理一般有两种实现方式:静态代理 和动态代理。

静态代理

在编译时,代理类已经被确定,需要事先手写一个代理类。

//创建接口规范真实类的行为
public interface Player{
void loadVideo(String filename);
}
//创建真实类实现接口
public class VPlayer implements Player {
@Override
public void loadVideo(String filename) {
System.out.println("加载MP4视频文件:"+filename);
}
}
//创建静态代理类,将真实类加载到代理类内部
public class VPlayerProxy implements Player {

private Player player;

public VPlayerProxy(Player player) {
this.player = player;
}

@Override
public void loadVideo(String filename) {
player.loadVideo(filename);
}
}
//客户端调用
public class Client1 {
public static void main(String[] args) {
//直连方式
Player vplay=new VPlayer();
proxy.loadVideo("aaa.mp4")
System.out.println();

//代理方式
Player proxy=new VPlayerProxy(vplay);
proxy.loadVideo("aaa.mp4");


}
}



动态代理

在运行时动态的根据需求生成代理类。其中代理类有两种实现方式:

  • java自带 java.lang.reflect.Proxy 类 (本文将重点介绍这种方法)
  • 开源的高性能代码生成包 CGlib

JDK动态代理

  1. 创建实现InvocationHandler接口的代理工厂:在调用Proxy类的静态方法newProxyInstance时,会动态的生成一个代理类。该代理类实现了目标接口,并且持有一个InvocationHandler类型的引用
  2. InvocationHandler接口:InvocationHandler是一个接口 他只有一个方法 invoke。在代理对象的方法被调用时,JVM会自动调用代理类的invoke方法
  3. 调用代理对象的方法:当代理对象的方法被调用时,JVM会自动调用代理类的invoke方法。可以添加调用代理类的invoke方法,并将被调用的方法名 参数等信息传递给该方法。
  4. invoke方法调用:在invoke方法中,通过反射机制调用目标对象的方法,并返回方法的返回值,在调用目标对象的方法前后,可以执行额外的逻辑。
public class JDKProxyFactory implements InvocationHandler {

//需要被代理的对象
private Object object;
//定义构造器方法
public JDKProxyFactory(Object object) {
this.object = object;
}

@SuppressWarnings("unchecked")
//获取代理对象
public <T> T getProxy(){
return (T) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),//当前线程的上下文ClassLoader
object.getClass().getInterfaces(), //代理需要实现的接口
this); // 处理器自身
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
//进行方法匹配,调用对应方法名的方法
if ("loadVideo".equals(method.getName())) {
result=method.invoke(object, args);
}

if ("playVideo".equals(method.getName())) {
System.out.println("前置增强");
result=method.invoke(object, args);
System.out.println("后置增强");
}
return result;
}
}

//客户端调用
public class Client2 {
public static void main(String[] args) {
Player player=new VPlayer();
Player proxy=new JDKProxyFactory(player).getProxy();
proxy.loadVideo("aaa.mp4");
proxy.playVideo("aaa.mp4");

/* 或者
Player p=new VPlayer();
Player o = (Player) Proxy.newProxyInstance(
p.getClass().getClassLoader(),
p.getClass().getInterfaces(),
new VPlayerProxyFactory(p)
);
o.loadVideo("aaaa.mp4");
*/
}
}

概括

代理的优点

  • 隐藏了原始对象的实现细节,使得客户端不需要了解原始对象的实现
  • 可以原始对象的基础上添加额外的功能,例如,缓存,安全验证,日志
  • 控制对原始对象的访问,保护原始对象不会被非法访问

缺点:

  • 增加了代理层,请求处理速度变慢
  • 引入代理类增加了系统复杂程度,增加了学习成本

适应场景

  • 当创建和初始化一个对象的开销很大的时候,可以使用代理模式延迟对象的实例化
  • 日志记录,代理模式在真实的对象方法执行前后进行日志记录,实现日志记录调优
  • 缓存代理,当客户端请求某个对象时,代理对象先检查缓存中是否存在该对象,如果存在则直接返回 否则创建新的对象并缓存起来,提高系统性能