代理模式和一些杂学

整理一下知识

Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”

静态代理

为何静态?因为静态代理,代理类在编译时生成。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nMAzwKcL-1616138782653)(images/image-20210319145217173.png)]

如果具象的看,如果存在 租客房东,和中介三个类。则中介即为房东的代理类,而租房这个操作即为中介房东的共同需求,(即要实现的接口)。

代码实现:

//抽象角色:租房
public interface Rent {
    
    
   public void rent();
}
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
    
    
   public void rent() {
    
    
       System.out.println("房屋出租");
  }
}
//代理角色:中介
public class Proxy implements Rent {
    
    

   private Host host;
   public Proxy() {
    
     }
   public Proxy(Host host) {
    
    
       this.host = host;
  }

   //租房
   public void rent(){
    
    
       seeHouse();
       host.rent();
       fare();
  }
   //看房
   public void seeHouse(){
    
    
       System.out.println("带房客看房");
  }
   //收中介费
   public void fare(){
    
    
       System.out.println("收中介费");
  }
}
//客户类,一般客户都会去找代理!
public class Client {
    
    
   public static void main(String[] args) {
    
    
       //房东要租房
       Host host = new Host();
       //中介帮助房东
       Proxy proxy = new Proxy(host);
       //租客去找中介!
       proxy.rent();
  }
}

在这个过程中,直接接触的就是中介,就如同现实生活中的样子,看不到房东,但是依旧租到了房东的房子通过代理,这就是所谓的代理模式。

静态代理的好处:

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .(房东只关注把房子准备好就可以了,其它的事交给中介,高内聚,低耦合)
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .

缺点 :

  • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想

动态代理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BwlPgtj3-1616138782653)(images/image-20210319150336946.png)]

  • 动态代理的角色和静态代理的一样 .
  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

​ JDK的动态代理(本质利用反射实现)

Object invoke(Object proxy, 方法 method, Object[] args)//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
public class ProxyInvocationHandler implements InvocationHandler {
    
    
    private Object target;

    public void setTarget(Object target) {
    
    
        this.target = target;
    }

    //生成代理类
    public Object getProxy(){
    
    
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                                      target.getClass().getInterfaces(),this);
    }

    // proxy : 代理类
    // method : 代理类的调用处理程序的方法对象.
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }

    public void log(String methodName){
    
    
        System.out.println("执行了"+methodName+"方法");
    }

}
public class Test {
    
    
   public static void main(String[] args) {
    
    
       //真实对象
       UserServiceImpl userService = new UserServiceImpl();
       //代理对象的调用处理程序
       ProxyInvocationHandler pih = new ProxyInvocationHandler();
       pih.setTarget(userService); //设置要代理的对象
       UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!
       proxy.delete();
  }
}
动态代理的好处

静态代理有的它都有,静态代理没有的,它也有!

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .
  • 一个动态代理 , 一般代理某一类业务
  • 一个动态代理可以代理多个类,代理的是接口!

那么什么是AOP?

解释一下什么是 aop?

AOP(Aspect-Oriented Programming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”

Tips : 一些联想与复习

提到静态代理和动态代理我不禁想到了静态绑定和动态绑定,也想到了重载与重写,复习一下。

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;

而对于多态,只等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。

引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”

想到多态,我又想到了JAVA的虚拟函数调用和C++中的一些区别。

在C++中通过虚函数表的方式实现多态,每个包含虚函数的类都具有一个虚函数表(virtual table),在这个类对象的地址空间的最靠前的位置存有指向虚函数表的指针。在虚函数表中,按照声明顺序依次排列所有的虚函数。

实际上,在C++中虚函数表记录了这个类的所有虚函数的具体实现(就是在运行时确切要调用的),编译时就可以确定,不需要动态查找,效率较高。


而Java中,在运行时会维持类型信息以及类的继承体系。每一个类会在方法区中对应一个数据结构用于存放类的信息,可以通过Class对象访问这个数据结构。其中,类型信息具有superclass属性指示了其超类,以及这个类对应的方法表(其中只包含这个类定义的方法,不包括从超类继承来的)。而每一个在堆上创建的对象,都具有一个指向方法区类型信息数据结构的指针,通过这个指针可以确定对象的类型。

Java的实现方式依赖于内存中的类型体系信息,存在一个“原型链”,是一个完全动态的查找过程,相对于C++而言,效率会低一些,因为存在一个链表遍历查找的过程。之所以,Java中可以这样实现,本质上是因为它是一门虚拟机语言,虚拟机会维持所有的这些类型信息。

猜你喜欢

转载自blog.csdn.net/weixin_43615816/article/details/115007448