前言

Spring全家桶是当下Java开发中最锋利的一柄剑,IOC思想的产生大大降低了代码内的耦合。IOC被称为控制反转,最具有代表性的实现方式是DI(依赖注入),但我们今天不讲IOC(︿( ̄︶ ̄)︿我海王阿浪就是要闪断在座各位的腰),想了解IOC原理的可以翻翻我其它文章。

AOP(面向切面编程)是Spring核心思想之一,常用于日志管理、事务控制(取代Filter)、权限验证等。我们知道传统OOP面向对象编程,方法的调用方式都是垂直调用,会产生大量的冗余。而AOP主要为了将通用功能分离,使多个类共享。由此可看出来,两个编程范式是互补的关系。

1、代理 Proxy

在聊AOP之前阿浪想先梳理梳理代理的相关体系知识,因为AOP的底层实现就是依据动态代理来实现的。

代理是一种设计模式,通过代理对象访问目标对象,可以在目标对象功能实现的基础上,增加额外的功能,从而达到扩展目标对象功能的效果,其本质为了控制和管理访问。但日常开发过程中,经常容易把它和装饰者模式搞混。

1.1、静态代理


网上有很多介绍代理模式的文章,这张图表达的结构应该都很熟悉了,但有没有想过为何代理类与被代理类一定要实现一个相同接口?

下面是我个人理解:

  1. 为了实现多态,符合面向接口编程设计思想,依赖接口,而不依赖具体实现。
  2. 可以对它的用户隐藏目标对象的具体信息,使客户不知道代理委托了另一个对象,实现控制和管理访问。
  3. 用户只需关心接口功能,而不在乎谁提供了具体功能。

Talk is cheap,show me the code

public interface Noodle {    void Material();}

首先创建了公共接口,然后根据接口创建一个实际处理功能方法的目标类:

public class ChongqingNoodle implements Noodle {    @Override    public void Material() {        System.out.println("重庆小面");    }}

代理类同时实现接口,并在编译阶段明确指定代理对象,静态代理的特点就是简单粗暴。

public class NoodleProxy implements Noodle {

    private Noodle noodle;

    public NoodleProxy() {        //关系在编译时确定        this.noodle = new ChongqingNoodle();    }

    @Override    public void Material() {        noodle.Material();        System.out.println("加辣!");        System.out.println("加醋!");    }}

模拟用户调用试试看效果:

public static void main(String[] args) {        Noodle noodle = new NoodleProxy();        noodle.Material();    }

打印结果:    重庆小面    加辣!    加醋!

虽然静态代理起到了控制访问的功能,但缺点也显而易见。不易维护,一旦接口增加方法,目标对象与代理对象都要进行修改。那有没有更好的实现方法呢?

1.2、动态代理

静态代理需要手动编写代码让代理类Proxy实现接口。而在动态代理中,我们可以让程序在运行的时候自动在内存中创建一个实现接口的代理,而不需要去定义Proxy这个类。

动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态代理。

静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件。

动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中。

Java动态代理机制中有两个重要的类和接口InvocationHandler(接口)和Proxy(类)。每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中。

当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用。

public class DynamicProxy implements InvocationHandler {

    private Object target;

    private Integer num;        //辣度

    public DynamicProxy(Object target) {        this.target = target;        this.num = 0;    }

    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        Object result = method.invoke(target, args);        num++;        System.out.println("添加" + num + "勺辣椒!");        return result;    }}

写个测试看看效果:

public static void main(String[] args) {        //动态代理        Noodle noodle = new ChongqingNoodle();        noodle = (Noodle) Proxy.newProxyInstance(ChongqingNoodle.class.getClassLoader(),                                        ChongqingNoodle.class.getInterfaces(),                                        new DynamicProxy(noodle));

        noodle.Material();        noodle.Material();

    }

打印结果:        重庆小面        添加1勺辣椒!        重庆小面        添加2勺辣椒!

1.3、装饰者模式

代理模式关注于控制对对象的访问,然而装饰器模式关注于在一个对象上动态的添加方法。

从结构图上看,装饰者模式和构造器模式非常相像,两者都实现了一个接口。但不同于代理的是装饰者与被装饰者通过构造器进行交互。

直接通过代码对比:

//公用接口public interface Noodle {    void Material();}

//被装饰者public class ChongqingNoodle implements Noodle {    @Override    public void Material() {        System.out.println("重庆小面");    }}

装饰者模式最大的特点就是装饰器的构造,而且允许多装饰器迭代装饰。

//装饰器一public class VinegarDecorator implements Noodle {

    private Noodle noodle;

    public VinegarDecorator(Noodle noodle) {        this.noodle = noodle;    }

    @Override    public void Material() {        this.noodle.Material();        System.out.println("加醋!");    }}

//装饰器二public class SpiceDecorator implements Noodle {

    private Noodle noodle;

    public SpiceDecorator(Noodle noodle) {        this.noodle = noodle;    }

    @Override    public void Material() {        this.noodle.Material();        System.out.println("加辣!");    }}

客户必须指定装饰者需要装饰的是哪一个类,并且装饰者能够在运行时递归地被构造。

public static void main(String[] args) {        Noodle noodle = new ChongqingNoodle();        noodle = new VinegarDecorator(noodle);        noodle = new SpiceDecorator(noodle);        noodle.Material();    }

打印结果:    重庆小面    加醋!    加辣!

通过上面的比较我们可以得出结论:

  • 静态代理只能编译时扩展目标类,装饰者可以运行时扩展目标类,动态代理为了

2、AOP与代理

Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP会使用JDK Proxy去创建代理对象。

如果要代理的对象没有实现接口,就无法使用 JDK Proxy ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理。

2.1、JDK Proxy

JDK Proxy就是我们上文提到的动态代理。

重要的方法

  //InvocationHandler接口类的invoke方法执行真正逻辑处理  public Object invoke(Object proxy, Method method, Object[] args)        throws Throwable;

  //Proxy类就是用来创建一个代理对象的类,重要方法newProxyInstance  public static Object newProxyInstance(ClassLoader loader,                                         Class<?>[] interfaces,                                         InvocationHandler h)           throws IllegalArgumentException;
  • loader:classloader对象,定义了由哪个classloader对象对生成的代理类进行加载

  • interfaces:interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。

  • h:InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。

2.2、CGLib Proxy


通过结构图,看以看出CGLib Proxy与JDK Proxy有很明显的差异。

代理类继承目标类,每次调用代理类的方法都会被方法拦截器拦截,在拦截器中才是调用目标类该方法的逻辑。

首先创建一个没有实现任何接口的实体类:

public class Wife {

    public String amorous(String express) {        return "MyWife :" +express;    }}

首先实现MethodInterceptor接口,方法调用会被转发到该类的intercept()方法。

public class MyMethodInterceptor implements MethodInterceptor {    @Override    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {        System.out.println("I say:" + Arrays.toString(objects));        return methodProxy.invokeSuper(o, objects);    }}

JDK Proxy提供一个Proxy类来创建代理类,而CGLib Proxy也提供了一个类似的类Enhancer;

public class CGlibProxy {

    public static void main(String[] args) {        Enhancer enhancer = new Enhancer();        enhancer.setSuperclass(Wife.class);        enhancer.setCallback(new MyMethodInterceptor());

        Wife wife = (Wife)enhancer.create();        System.out.println(wife.amorous("I love you!"));    }}

打印输出:      I say:[I love you!]      MyWife :I love you!

上述代码中,我们通过CGLib的Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象。

对代理对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法,在intercept()方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;

通过调用MethodProxy.invokeSuper()方法,我们将调用转发给原始对象。CGLib中MethodInterceptor的作用跟JDK Proxy中的InvocationHandler很类似,都是方法调用的中转站。


阿浪技术还很菜,文章内可能存在错误,欢迎各位大佬指正。


装饰者模式、代理模式与AOP相关推荐

  1. C++设计模式 | 四种结构型模式——代理模式、外观模式、适配器模式、装饰模式...

    结构型模式:让类和类进行组合,获得更大的结构. 代理模式 代理模式的定义: 为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端 ...

  2. 设计模式 — 结构型模式 — 代理模式

    目录 文章目录 目录 代理模式 应用场景 代码示例 代理模式 代理模式,为其他对象提供一种代理,以此控制一个对象的访问方式.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户 ...

  3. 设计模式(装饰者模式外观模式代理模式)

    目录 装饰者模式 介绍 实现 外观模式 介绍 实现 代理模式 介绍 实现 装饰者模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设 ...

  4. OOAD-设计模式(四)结构型模式之适配器、装饰器、代理模式

    前言 前面我们学习了创建型设计模式,其中有5中,个人感觉比较重要的是工厂方法模式.单例模式.原型模式.接下来我将分享的是结构型模式! 一.适配器模式 1.1.适配器模式概述 适配器模式(Adapter ...

  5. JAVA设计模式--结构型模式--代理模式

    1.代理模式(Proxy Pattern) 一个类代表另一个类的功能.这种类型的设计模式属于结构型模式.在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口. 1.1意图: 为其他对象提供 ...

  6. 《设计模式详解》结构型模式 - 代理模式

    代理模式 5.1 代理模式 5.1.1 概述 5.1.2 结构 5.1.3 静态代理 5.1.4 动态代理 JDK 动态代理 JDK 动态代理分析 CGLIB 动态代理 5.1.5 三种代理的对比 5 ...

  7. 单例模式 工厂模式 代理模式 适配器模式

    单例模式 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必 ...

  8. 结构型模式 -- 代理模式(静态代理动态代理)

    终于学到代理模式啦 真实角色: 代理角色:代理角色拥有真实角色的引用 真实角色和代理角色实现相同的接口 晚上再写嘻嘻 转载于:https://www.cnblogs.com/DDiamondd/p/1 ...

  9. 蓝懿IOS委托模式代理模式

    今天刘国斌老师讲了有关oc语言里的委托模式(代理模式),通过了一个打地鼠的游戏讲解了委托模式的功能作用,之后连带讲解了协议的书写和使用. 打地鼠功能包括屏幕随机出现地鼠,点击消失,如果不点击5秒后自动 ...

  10. Java—接口(工厂模式代理模式)

    程序1:简单的接口功能 package com.liaojianya.chapter2; /*** 编写程序实现一个usb接口,定义设备来使用这个接口,从而计算机可以调用具有usb接口的设备.* @a ...

最新文章

  1. 实战项目四:爬取911网站
  2. mysql Range按时间分区问题 Table has no partition for value 737669
  3. 智能城市dqn算法交通信号灯调度_博客 | 滴滴 KDD 2018 论文详解:基于强化学习技术的智能派单模型...
  4. Sybase数据库中找回丢失的sa密码
  5. flannel vxlan 实现原理【转】
  6. 后台给前台传值 php,前后台传值的几种方式(html,js,php)
  7. 基于JAVA+SpringMVC+Mybatis+MYSQL的周报管理系统
  8. python异步高并发_python高并发异步服务器核心库forkcore使用方法
  9. linux网卡驱动对XDP支持情况
  10. java switch的应用
  11. DiskFileUpload类
  12. 个人考研资料整理(更新一战后感想)
  13. VK1640脚位定义图
  14. sqlite3 error: database is locked
  15. 医学图像处理(三)ABIDE数据集下载
  16. 第四章 变形-学习笔记+练习题
  17. 移动魔百盒CM201-2,CH代工(nand,emmc)免拆-刷机固件
  18. 毕业设计说明书(论文)结构-系统设计方面
  19. 正确率、召回率、F值例子
  20. Python语法--File文件处理

热门文章

  1. 使用RaiDrive将NAS中的磁盘映射为本地磁盘
  2. 服务器2019添加虚拟机,Hyper-V安装Server 2019虚拟机图文教程
  3. java简单实现购物车添加,查询,修改,结算商品程序
  4. 火狐插件 测试浏览器兼容性_在Firefox中测试和报告插件兼容性
  5. 焦作java培训_周口市转行做it
  6. C# 复数类 Complex
  7. 女性三围--表单只能输入数字和英文输入下的逗号
  8. 这个卷走175个国家和地区45亿英镑的“加密女王”忽然失踪,骗局崩盘
  9. jquery fadeOut 异步
  10. Vue2 Browserslist: caniuse-lite is outdated. Please run: npx browserslist@latest --update-db