设计模式——代理模式

设计模式——代理模式

本文个人博客地址:https://abeille.top/blog/detail/AT815XG44

代理模式:其主要作用有两个,一个是保护目标对象,另外一个是增强目标对象;代理模式属于结构型模式;按其类型分为::静态代理::和::动态代理::;
代理的外部功能和实际对象一般一致,用户和代理打交道,不直接接触实际对象。虽然外部功能和实际对象一样,但是代理有它存在的价值:

  • 节省成本较高的实际对象的创建,按需延迟加载,创建代理对象并不真正创建实际对象,而只是保存实际对象的地址,在需要时再加载或创建;
  • 执行权限检查,代理检查权限后,再调用实际对象;
  • 屏蔽网络差异和复杂性,代理在本地,而实际对象在其他服务器上,调用本地代理时,本地代理请求其他服务器;

代理模式中的三种角色:

  • 抽象主题角色:可以是抽象类也可以是接口,是一个最普通的业务类型定义;
  • 具体主题角色:也叫做被委托角色,被代理角色,是业务逻辑的具体执行者;
  • 代理主题角色:也叫委托类、委托角色,负责对真实角色的应用,把所有抽象主题角色定义的方法限制委托给具体主题角色,并在具体主题角色处理完成的前后做预处理和善后处理;
  1. 静态代理:

静态代理使用示例:

创建顶层接口Person(抽象主题角色):

public interface Person {
     
 public void findLove();
}

实现Son类(具体主题角色):

public class Son implements Person {
     
 @Override
 public void findLove () {
     
  System.out.println(“相亲找对象”);
 }
} 

实现Father类(代理主题角色):

public class Father implements Person {
     
 private Son son;
 public Father (Son son) {
     
  this.son = son;
 }
 @Override
 public void findLove () {
     
  System.out.println(“父亲托人介绍”);
  this.son.finsLove();
  System.out.println(“相亲成功”);
 }
}

调用示例;

public class ProxyExample {
     
 public static void main(String[] args) {
     
  Person person = new Father(new Son());
  person.findLove();
 }
}

代码说明:例子中Son、Father都实现了Person这个接口,在Father类内部有一个Son的成员变量,指向实际的Son对象,在构造方法中被初始化,对于findLove()方法的调用,转发给了实际的对象;

  1. 动态代理:
    动态代理时实现面向切面的AOP(Aspect Oriented Programming)的基础,主要有两种方式:JDK动态代理、CGLIB动态代理;
  • JDK动态代理的使用示例(以上面静态代理的示例进行修改):
    创建InvocationHandler的实现类SimpleInvocationHandler:
public class SimpleInvocationHandler implements InvocationHandler {
     
 private Object obj;
 // obj 表示被代理对象
 public SimpleInvocationHandler (Object obj) {
     
  this.obj = obj;
 }
 /** proxy表示本身,method表示正在被调用的接口方法,args表示方法的参数 */
 @Override
 public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
     
  System.out.println(“entering” + method.getName());
  Object result = method.invoke(obj, args);
  System.out.println(“leaving” + method.getName());
  return result;
 }
}

示例调用:

public class DynamicProxyExample {
     
 public static void main (String[] args) {
     
  Father father = new Father();
  Class<? extends Person> pc = father.getClass();
  // newProcyInstance 的返回类型可以强制转换为interfaces中的某个接口的类型,但是不能转换为某个类的类型,
  // 例如:该方法中,只能强转为Person而不能转换为Father
  Person person = (Person)Proxy.newProxyInstance(pc.getClassLoader(), pc.getInterfaces(), new SimpleInvocationHandler(father));
  person.findLove();
 }
}

动态代理的优点:
使用动态代理可以编写通用的代理逻辑,用于各种类型的被代理对象,而不需要为没被代理的类型都创建一个静态代理类;
JDK动态代理的局限性:
只能为接口创建代理,返回的代理对象也只能转换成接口类型;

  • CGLIB动态代理:
    CGLIB动态代理的实现机制是通过继承实现的;同样也是动态创建一个类,但这个类的父类是被代理的类,代理类重写了父类所有的public 非 final方法,改为调用Callback 中的相关方法;
    CGLIB动态代理使用示例:
    创建被代理类:
public class Father {
     
 public void say(){
     
  System.out.println(“morining”);
 }
}

创建代理执行类:

public class SimpleInterceptor implements MethodInterceptor {
     
 @Override
 public Object intercept (Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
     
  System.out.println(“entering” + method.getName());
  // 这里需要调用invokeSuper()
  Object result = method.invokeSuper(obj, args);
  System.out.println(“leaving” + method.getName());
  return result;
 }
}

调用示例:

public class CglibExample {
     
 public static <T> T getProxy () {
     
  Enhancer enhancer = new Enhancer();
  enhancer.setSuperClass(Father.class);
  enhancer.setCallback(new SimpleInterceptor());
  return (T) enhancer.create();
 }

 public static void main (String[] args) {
     
  Father proxy = getProxy();
  proxy.say();
 }
}

JDK动态代理与CGLIB动态代理对比:
JDK动态代理面向的是一组接口,它为这些接口动态的创建一个实现类,接口的具体实现逻辑是通过自定义的InvocationHandler实现的,这个实现是自定义的,也就是说,其背后都不一定有真正被代理的对象,可能有多个实际对象,根据情况动态选择;
从代理的角度看,JDK动态代理代理的是对象,需要现有一个实际对象,自定义的InvocationHandler引用该对象,然后创建一个代理类和代理对象,客户端访问的是代理对象,代理对象再调用实际的对象方法;
JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvocationHandler来处理;

CGLIB动态代理面向的是一个具体的类,它动态创建一个新类,继承被代理对象的类,重写其方法;
从代理的角度看,CGLIB动态代理代理的是类,创建的对象只有一个;
CGLIB动态代理是利用asm开源包,对被代理对象类的class文件加载进来,通过修改其字节码生成子类来处理

你可能感兴趣的:(设计模式,设计模式,java)