当前位置: 首页 > article >正文

Spring AOP原理

Spring AOP原理

  • 1. Spring AOP原理
    • 1.1 代理模式
      • 1.1.1 静态代理
      • 1.1.2 动态代理
  • 2. 总结

1. Spring AOP原理

上⾯我们主要学习了SpringAOP的应⽤,接下来我们来学习SpringAOP的原理,也就是Spring是如何实 现AOP的. SpringAOP是基于**动态代理**来实现AOP的,咱们学习内容主要分以下两部分

  1. 代理模式
  2. SpringAOP源码剖析

1.1 代理模式

代理模式,也叫委托模式.

定义:为其他对象提供⼀种代理以控制对这个对象的访问.它的作⽤就是通过提供⼀个代理类,让我们 在调⽤⽬标⽅法的时候,不再是直接对⽬标⽅法进⾏调⽤,⽽是通过代理类间接调⽤.

在某些情况下,⼀个对象不适合或者不能直接引⽤另⼀个对象,⽽代理对象可以在客⼾端和⽬标对象之 间起到中介的作⽤.

使用代理前:

使用代理后:


⽣活中的代理

  • 艺⼈经纪⼈:⼴告商找艺⼈拍⼴告,需要经过经纪⼈,由经纪⼈来和艺⼈进⾏沟通.
  • 房屋中介:房屋进⾏租赁时,卖⽅会把房屋授权给中介,由中介来代理看房,房屋咨询等服务.
  • 经销商:⼚商不直接对外销售产品,由经销商负责代理销售.
  • 秘书/助理:合作伙伴找⽼板谈合作,需要先经过秘书/助理预约.

代理模式的主要⻆⾊

  1. Subject:业务接⼝类.可以是抽象类或者接⼝(不⼀定有)
  2. RealSubject:业务实现类.具体的业务执⾏,也就是被代理对象.
  3. Proxy:代理类.RealSubject的代理.

⽐如房屋租赁

Subject就是提前定义了房东做的事情,交给中介代理,也是中介要做的事情

RealSubject:房东

Proxy:中介

UML类图如下:

代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进⾏⼀些功能的附加与增强.

根据代理的创建时期,代理模式分为静态代理和动态代理.

  • 静态代理:由程序员创建代理类或特定⼯具⾃动⽣成源代码再对其编译,在程序运⾏前代理类的 .class⽂件就已经存在了.
  • 动态代理:在程序运⾏时,运⽤反射机制动态创建⽽成.

1.1.1 静态代理

静态代理:在程序运⾏前,代理类的.class⽂件就已经存在了.(在出租房⼦之前,中介已经做好了相关的 ⼯作,就等租⼾来租房⼦了)

我们通过代码来加深理解.以房租租赁为例

  1. 定义接⼝(定义房东要做的事情,也是中介需要做的事情)

    public interface HouseSubject {
        void rentHouse();
    }
    
  2. 实现接⼝(房东出租房⼦)

    public class RealHouseSubject implements HouseSubject{
        @Override
        public void rentHouse() {
            System.out.println("我是房东, 我出租房⼦");
        }
    }
    
  3. 代理(中介,帮房东出租房⼦)

    public class HouseProxy implements HouseSubject{
        //将被代理对象声明为成员变量 
        private HouseSubject houseSubject;
        public HouseProxy(HouseSubject houseSubject) {
            this.houseSubject = houseSubject;
        }
        @Override
        public void rentHouse() {
            //开始代理 
            System.out.println("我是中介, 开始代理");
            //代理房东出租房⼦ 
            houseSubject.rentHouse();
            //代理结束 
            System.out.println("我是中介, 代理结束");
        }
    }
    
  4. 使用

    public class StaticMain {
        public static void main(String[] args) {
            HouseSubject subject = new RealHouseSubject();
            //创建代理类 
            HouseProxy proxy = new HouseProxy(subject);
            //通过代理类访问⽬标⽅法 
            proxy.rentHouse();
        }
    }
    

运行结果:


上⾯这个代理实现⽅式就是静态代理(仿佛啥也没⼲).

从上述程序可以看出,虽然静态代理也完成了对⽬标对象的代理,但是由于代码都写死了,对⽬标对象的 每个⽅法的增强都是⼿动完成的,⾮常不灵活.所以⽇常开发⼏乎看不到静态代理的场景.

接下来新增需求:中介⼜新增了其他业务:代理房屋出售

我们需要对上述代码进⾏修改

  1. 接口定义修改

    public interface HouseSubject {
        void rentHouse();
        void saleHouse();
    }
    
  2. 接口实现修改

    public class RealHouseSubject implements HouseSubject{
        @Override
        public void rentHouse() {
            System.out.println("我是房东, 我出租房⼦");
        }
        @Override
        public void saleHouse() {
            System.out.println("我是房东, 我出售房⼦");
        }
    }
    
  3. 代理类修改

    public class HouseProxy implements HouseSubject {
        //将被代理对象声明为成员变量
        private HouseSubject houseSubject;
        public HouseProxy(HouseSubject houseSubject) {
            this.houseSubject = houseSubject;
        }
        @Override
        public void rentHouse() {
            //开始代理 
            System.out.println("我是中介, 开始代理");
            //代理房东出租房⼦ 
            houseSubject.rentHouse();
            //代理结束 
            System.out.println("我是中介, 代理结束");
        }
        @Override
        public void saleHouse() {
            //开始代理 
            System.out.println("我是中介, 开始代理");
            //代理房东出租房⼦ 
            houseSubject.saleHouse();
            //代理结束 
            System.out.println("我是中介, 代理结束");
        }
    }
    

从上述代码可以看出,我们修改接⼝(Subject)和业务实现类(RealSubject)时,还需要修改代理类 (Proxy).

同样的,如果有新增接⼝(Subject)和业务实现类(RealSubject),也需要对每⼀个业务实现类新增代理类 (Proxy).

既然代理的流程是⼀样的,有没有⼀种办法,让他们通过⼀个代理类来实现呢?

这就需要⽤到动态代理技术了.

1.1.2 动态代理

相⽐于静态代理来说,动态代理更加灵活.

我们不需要针对每个⽬标对象都单独创建⼀个代理对象,⽽是把这个创建代理对象的⼯作推迟到程序运 ⾏时由JVM来实现.也就是说动态代理在程序运⾏时,根据需要动态创建⽣成.

⽐如房屋中介,我不需要提前预测都有哪些业务,⽽是业务来了我再根据情况创建.

我们还是先看代码再来理解.

Java也对动态代理进⾏了实现,并给我们提供了⼀些API,常⻅的实现⽅式有两种:

  1. JDK动态代理
  2. CGLIB动态代理

动态代理在我们⽇常开发中使⽤的相对较少,但是在框架中⼏乎是必⽤的⼀⻔技术.学会了动态代理 之后,对于我们理解和学习各种框架的原理也⾮常有帮助.

JDK动态代理

JDK动态代理类实现步骤

  1. 定义⼀个接⼝及其实现类(静态代理中的 HouseSubjectRealHouseSubject )
  2. ⾃定义 InvocationHandler 并重写 invoke ⽅法,在invoke ⽅法中我们会调⽤⽬标⽅ 法(被代理类的⽅法)并⾃定义⼀些处理逻辑
  3. 通过 Proxy.newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h) ⽅法创建代理对象

定义JDK动态代理类

实现 InvocationHandler 接⼝

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class JDKInvocationHandler implements InvocationHandler {
    //⽬标对象即就是被代理对象 
    private Object target;
    public JDKInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
    // 代理增强内容 
    System.out.println("我是中介, 开始代理");
    //通过反射调⽤被代理类的⽅法 
    Object retVal = method.invoke(target, args);
    //代理增强内容 
     System.out.println("我是中介, 代理结束");
     return retVal;
     }
}

创建⼀个代理对象并使⽤

public class DynamicMain {
    public static void main(String[] args) {
        HouseSubject target= new RealHouseSubject();
        //创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建 
        HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                new Class[]{HouseSubject.class},
                new JDKInvocationHandler(target)
        );
        proxy.rentHouse();
    }
}

代码简单讲解

主要是学习API的使⽤,我们按照JavaAPI的规范来使⽤即可

  1. InvocationHandler

    InvocationHandler接⼝是Java动态代理的关键接⼝之⼀,它定义了⼀个单⼀⽅法invoke() ,⽤于 处理被代理对象的⽅法调⽤.

public interface InvocationHandler {
    /**
     * 参数说明
     * proxy:代理对象
     * method:代理对象需要实现的⽅法,即其中需要重写的⽅法
     * args:method所对应⽅法的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

通过实现 InvocationHandler 接⼝,可以对被代理对象的⽅法进⾏功能增强.

  1. Proxy

    Proxy 类中使⽤频率最⾼的⽅法是: newProxyInstance() ,这个⽅法主要⽤来⽣成⼀个代理对象

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
            throws IllegalArgumentException {
        //...代码省略 
    }
    

    这个⽅法⼀共有3个参数:

    Loader:类加载器,⽤于加载代理对象.

    interfaces:被代理类实现的⼀些接⼝(这个参数的定义,也决定了JDK动态代理只能代理实现了接⼝的 ⼀些类)

    h:实现了InvocationHandler接⼝的对象

CGLIB动态代理

JDK动态代理有⼀个最致命的问题是其只能代理实现了接⼝的类.

有些场景下,我们的业务代码是直接实现的,并没有接⼝定义.为了解决这个问题,我们可以⽤CGLIB动 态代理机制来解决.

CGLIB(CodeGenerationLibrary)是⼀个基于ASM的字节码⽣成库,它允许我们在运⾏时对字节码进⾏ 修改和动态⽣成.CGLIB通过继承⽅式实现代理,很多知名的开源框架都使⽤到了CGLIB.例如Spring 中的AOP模块中:如果⽬标对象实现了接⼝,则默认采⽤JDK动态代理,否则采⽤CGLIB动态代理.

CGLIB动态代理类实现步骤

  1. 定义⼀个类(被代理类)
  2. ⾃定义MethodInterceptor 并重写intercept ⽅法, intercept ⽤于增强⽬标⽅ 法,和JDK动态代理中的invoke ⽅法类似
  3. 通过Enhancer类的create()创建代理类

接下来看下实现:

添加依赖

和JDK动态代理不同,CGLIB(CodeGenerationLibrary)实际是属于⼀个开源项⽬,如果你要使⽤它 的话,需要⼿动添加相关依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

⾃定义MethodInterceptor(⽅法拦截器)

实现MethodInterceptor接⼝

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLIBInterceptor implements MethodInterceptor {
    //⽬标对象, 即被代理对象 
    private Object target;
    public CGLIBInterceptor(Object target){
        this.target = target;
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 代理增强内容 
        System.out.println("我是中介, 开始代理");
        //通过反射调⽤被代理类的⽅法 
        Object retVal = methodProxy.invoke(target, objects);
        //代理增强内容 
        System.out.println("我是中介, 代理结束");
        return retVal;
    }
}

创建代理类,并使⽤

public class DynamicMain {
    public static void main(String[] args) {
        HouseSubject target= new RealHouseSubject();
        HouseSubject proxy= (HouseSubject) Enhancer.create(target.getClass(),new CGLIBInterceptor(target));
        proxy.rentHouse();
    }
}

代码简单讲解

  1. MethodInterceptor

    MethodInterceptor 和JDK动态代理中的 InvocationHandler 类似,它只定义了⼀个⽅法 intercept() ,⽤于增强⽬标⽅法.

    public interface MethodInterceptor extends Callback {
        /**
         * 参数说明: 
         * o: 被代理的对象 
         * method: ⽬标⽅法(被拦截的⽅法, 也就是需要增强的⽅法) 
         * objects: ⽅法⼊参 
         * methodProxy: ⽤于调⽤原始⽅法 
         */
        Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable;
    }
    
  2. Enhancer.create()

    Enhancer.create()⽤来⽣成⼀个代理对象

    public static Object create(Class type, Callback callback) {
        //...代码省略 
    }
    

    参数说明:

    type:被代理类的类型(类或接⼝)

    callback:⾃定义⽅法拦截器 MethodInterceptor

2. 总结

  1. AOP是⼀种思想,是对某⼀类事情的集中处理.Spring框架实现了AOP,称之为SpringAOP
  2. SpringAOP常⻅实现⽅式有两种:
    1. 基于注解@Aspect来实现
    2. 基于⾃定义注解来实现,还有⼀些 更原始的⽅式,⽐如基于代理,基于xml配置的⽅式,但⽬标⽐较少⻅
  3. SpringAOP是基于动态代理实现的,有两种⽅式:
    1. 基本JDK动态代理实现
    2. 基于CGLIB动态代理 实现.运⾏时使⽤哪种⽅式与项⽬配置和代理的对象有关.

http://www.kler.cn/a/522710.html

相关文章:

  • 【Elasticsearch 】悬挂索引(Dangling Indices)
  • C语言实现统计数组正负元素相关数据
  • 单路由及双路由端口映射指南
  • 【项目】基于Qt开发的音乐播放软件
  • A7. Jenkins Pipeline自动化构建过程,可灵活配置多项目、多模块服务实战
  • Nginx 安装配置指南
  • 智能门锁开发系列:从设计到实现的全面解析
  • Mybaties缓存机制
  • 装饰SpringMVC的适配器实现响应自动包装
  • 每日一题 429. N 叉树的层序遍历
  • WebPages 表单:设计与实现指南
  • react-bn-面试
  • 使用国内镜像加速器解决 Docker Hub 拉取镜像慢或被屏蔽的问题
  • 学习第七十六行
  • 备份与恢复管理系统深度解析及代码实践
  • GitHub 仓库的 Archived 功能详解:中英双语
  • 炫酷JavaScript文本时钟
  • 跨平台物联网漏洞挖掘算法评估框架设计与实现项目后期研究方案
  • 008 mybatis缓存
  • 全志 视频输入组件的使用
  • 强化学习在自动驾驶中的实现与挑战
  • RocketMQ优势剖析-性能优化
  • 安卓入门四十二 过渡动画
  • RAG制作客服机器人,文档用表格还是QA问答对?
  • python 一个组合问题:
  • LeetCode100之全排列(46)--Java