小言_互联网的博客

简单学习java设计模式的 静态代理模式和动态代理模式

322人阅读  评论(0)

代理模式概念:

理模式的定义
1、为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用,比如我们要去法院申诉,中间需要一个律师帮忙代理一下注意事项等,这个律师就是代理类。
2、当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。

抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,可以附加自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理对象实现同一个接口,先访问代理类再访问真正要访问的对象。

代理模式分为静态代理、动态代理。

静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

只看概念有点懵,现在举个例子,比如我们人要去法院申诉用代码演示

静态代理:

1、我们先创建一个接口类打官司接口Law

//打官司接口
public interface Law {
   public void law();
}

2、现在是我们进行申诉 目标类

public class Person implements Law {

 @Override
 public void law() {
  // TODO Auto-generated method stub
       System.out.println("目标类去法庭申述");
 }

}

3、现在是静态代理的核心部分,律师代理类

//代理类
public class Lawyer implements Law {
   
 //引入目标类
 private Person person; 
 @Override 
 //核心
 public void law() {
  // TODO Auto-generated method stub
      this.a();
      //person对象实例化,只有实例化之后,才能将这个对象放到内存中,然后才能在规定的范围内来调用
      if(person==null) {
       person=new Person();
      }
    //调用目标类的方法
      person.law();
      
      this.b();
 }
 
 //律师要处理的事情
 public void a() {
  System.out.println("开庭前收集证据");
 }
 
 public void b() {
  System.out.println("案件结案处理");
 }

}

静态代理类比较好理解,现在直接创建客户端测试一下
4、创建客户

public class test {

 public static void main(String[] args) {
  // TODO Auto-generated method stub

  Lawyer lawyer=new Lawyer();
  lawyer.law();
 }

}

运行结果:

上面就是静态代理模式,比较简单,我们就不解释了,动态代理不同于静态代理的特点是它更为灵活,因为动态代理就是在运行期间动态生成代理类。我们沿用上面的例子,假设有五百个不一样的人要打官司,都交给律师来操办,那么按照静态代理的思路来做,我们需要写五百个真实角色,并且代理角色持有这五百个真实角色。这显然不合逻辑。这时候动态代理就应运而生了。下面我们要演示动态代理模式。

动态代理类的接口类和目标类跟静态代理类一样
1、打官司接口Law

//打官司接口
public interface Law {
   public void law();
}

2、目标类

public class Person implements Law {

 @Override
 public void law() {
  // TODO Auto-generated method stub
       System.out.println(this.getClass().getSimpleName()+"目标类去法庭申述");
 }

}

3、现在我们需要创建一个对象类去实现动态代理的一个核心接口InvocationHandler,动态代理本质采用的Java反射机制实现
InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//实现动态代理的一个核心接口,动态代理本质采用的Java反射机制实现
//InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,
//如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。
public class MyHandler implements InvocationHandler {
 
 private Object object;
 
 public MyHandler(Object object) {
 
  this.object=object;
 }


// InvocationHandler 内的 invoke() 方法决定了怎么样处理代理传递过来的方法调用。
// proxy 代理对象
// method 代理对象调用的方法
// args  调用的方法中的参数
// Proxy 动态产生的代理会调用 InvocationHandler 实现类,因此 InvocationHandler 是实际执行者。
 
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  // TODO Auto-generated method stub
  System.out.println("开庭前律师收集证据");
  Object obj=method.invoke(object, args);
  System.out.println("目标类陈述事实");
  return obj;

}

上面用到 InvocationHandler 内的 invoke() 方法决定了怎么样处理代理传递过来的方法调用,里面的三个参数分别代表
// proxy 代理对象
// method 代理对象调用的方法
// args 调用的方法中的参数
// Proxy 动态产生的代理会调用 InvocationHandler 实现类,因此 InvocationHandler 是实际执行者。

4、我们直接去创建一个客户端去实现动态代理:

import java.lang.reflect.Proxy;
public class test {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
      //代理目标类
  Law law=new Person();
   //生成代理对象,Proxy类通过调用newProxyInstance这个方法生产代理对象
  //动态代码涉及了一个非常重要的类 Proxy,通过 Proxy 的静态方法 newProxyInstance 才会动态创建代理。
  //它的 3 个参数意义。
        //loader即这里的Law.class.getClassLoader() 自然是类加载器
        //interfaces即new Class[]{Law.class} 代码要用来代理的接口
        //h即new MyHandler(law) 一个 InvocationHandler 对象     
     Law proxy=(Law)Proxy.newProxyInstance(Law.class.getClassLoader(), new Class[]  {Law.class}, new MyHandler(law));
     proxy.law();
 }


// 上面执行person类的结果,那么如果律师接待第二个人Other呢?我们直接载加一个Other对象,然后客户端直接加下面代码就可以了
     Law law1=new Other();
     Law proxy1=(Law)Proxy.newProxyInstance(Law.class.getClassLoader(), new Class[]{Law.class}, new MyHandler(law1));
     proxy1.law();


}

生成代理对象Proxy类通过调用newProxyInstance这个方法生产代理对象
动态代码涉及了一个非常重要的类 Proxy,通过 Proxy 的静态方法 newProxyInstance 才会动态创建代理。

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

//它的 3 个参数意义。
//loader即这里的Law.class.getClassLoader() 自然是类加载器
//interfaces即new Class[]{Law.class} 代码要用来代理的接口
//h即new MyHandler(law) 一个 InvocationHandler 对象

添加的第二个人Other:

public class Other implements Law {

 @Override
 public void law() {
  // TODO Auto-generated method stub
       System.out.println(this.getClass().getSimpleName()+"目标类去法庭申述");
 }

}

运行结果:

上面就演示完静态代理模式和动态代理模式了,如果还是很懵,可以去https://www.jianshu.com/p/d9e1641fbb6c查看,那里有详细介绍Proxy.newInstance(…)、getProxyClass0(loader, intfs)源码

动态代理模式和静态代理模式的应用场景:

静态代理主要用来处理少部分类的托管或者扩展。静态代理对于被代理的对象很固定,我们只需要去代理一个类或者若干固定的类,数量不是太多的时候,可以使用,而且其实效果比动态代理更好。

动态代理在运行期间动态生成代理类,需要消耗的时间会更久一点。优点是可以做很多类的扩展,而且如果一个类的接口发生了变化,那么静态代理这时候修改起来就很麻烦了。这就是说动态代理灵活的原因。
动态代理主流的实现有两种,一种是基于接口的Java Proxy的代理机制,一种是基于继承的cglib代理机制。两种也都有其应用场景。


转载:https://blog.csdn.net/weixin_44001568/article/details/105639136
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场