基于方法编程&基于类的编程
在理解Spring AOP前,我们有必要理解一下基于方法编程和基于类编程这两种概念
基于方法编程
- 目标方法具体是由哪个实例来调用的不会影响方法的执行结果
- 数据的传输依赖于形参
基于类的编程
- 每次请求目标方法的执行时需要一个新的对象
- 数据的传输依赖于成员变量的set/get方法
代码对比
其实非常好理解,用代码形象的演示一下
基于方法编程
基于类编程
关于Spring的安全
- Spring是基于方法编程的,同时我们也知道Bean的默认作用域是单例,所以多个线程可以访问同一个对象,这也表明Spring是线程不安全的,但因为资源是不共享的,比如形参name,每个线程有自己的线程栈,对于自己name的更改,并不影响其他线程的执行结果
- 拓展:Struts2是基于类的
Spring AOP
Spring最为重要的两大部分就是Spring IOC(控制反转)和Spring AOP(面向切面编程)了
如果说,IOC是Spring的核心,那么面向切面编程就是Spring最为重要的功能之一了
略显正式的解释一下
AOP
- AOP全称Aspect-Oriented Programming,面向切面编程,也可以理解为面向方法编程,与OOP(Object-Oriented Programming)面向对象编程相辅相成,提供了与OOP不同抽象的软件结构的视角
- 在OOP中,类(class)是我们的基本单元,而在AOP中,切面(Aspect)是基本单元
- Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中
- 可以简单的理解为,使用@Aspect注解的类就是切面
形象化解释
正常情况下,mvc三层模型的访问如下:
可以看到service层的工作非常繁琐,那么切面就出面帮助完成这种繁琐的工作
加入了切面后
一些术语
- advice(通知):切面要完成的工作,通知定义了切面是什么以及何时使用,是由aspect添加到特定的join point的一段代码
Spring切面有五种类型的通知:
前置通知(Before):在目标方法被调用之前调用通知功能
后置通知(After):在目标方法完成之后调用通知,在最后一步,此时不关心方法的输出结果是什么
返回通知(After-returning):在目标方法成功执行之后调用通知
异常通知(After-throwing):在目标方法抛出异常后调用通知
环绕通知(Around):通知包裹了被通知的方法,在被通知方法调用之前和调用之后执行自定义的行为 - join point(连接点):程序运行中的一些时间点,例如ige方法的执行或一个异常的处理,在Spring AOP中,join point总是方法的执行点,即只有方法连接点
- point cut(切点):匹配join point的谓词,advice是个特定的point cut关联的,并且在point cut相匹配的join point中执行,
在Spring中,所有方法都可以认为是join point,但我们并不希望在所有的方法上都添加Advice,而point cut的作用域就是提供一组规则(切点表达式)来匹配join point,给满足规则的join point添加Advice - aspect(切面):通知和切点的结合,通知和切点共同定义了切面的全部内容:既包含横切逻辑的定义,也包括了连接点的定义,表明了它是什么,在何时和何处完成其功能
- introduction(引入):为一个类型添加额外的方法或字段,Spring AOP允许我们为目标对象引入新的接口,即在不修改现有类的基础上,向现有类添加新的方法或属性
- weaving(织入):织入是把切面应用到目标对象并创建新的代理对象的过程
切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里,有以下几个点可以进行织入:
编译期:切面在目标类编译时被织入,这种方式需要特殊的编译器,AspectJ的织入编译器就是以这种方式织入切面的
类加载期:切面在目标类加载到JVM时被织入,这种方式需要 特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码
运行期:切面在应用运行的某个时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象,Spring AOP就是以这种方式织入切面的
代理模式
Spring IOC实质上是一个工厂模式
而Spring AOP实质上是一个代理模式
那么为了了解Spring AOP,我们就需要理解一下代理模式到底是怎么回事了。
定义
给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用
代理对象:中介作用,类似明星经纪人,连接客户端和目标对象
代理模式很好的解决了防止直接访问目标对象给系统带来的不必要复杂性,保护真实对象
具象化理解
作为中华儿女,一定要牢记中华文化的博大精深,那么我们以西门庆和潘金莲的故事来看代理模式
实质上代理模式就是像王婆一样,做了个类似中介的事情,避免乱七八糟的东西接触到真实对象
代理模式的分类
纵观一下几类代理模式
代理模式 | 描述 |
---|---|
静态代理 | 代理类和被代理类都要继承同一个接口,如果被代理对象扩展了一个方法,那么代理对象也需要扩展一个同样的方法 |
JDK动态代理 | 依赖于jdk提供的类实现了代理模式,动态的,扩展性好,但是只能代理接口 |
cglib动态代理 | 依赖于第三方jar包实现的,动态的,既可以代理接口又可以代理类 |
没有什么能比代码更清晰了,我们上代码
- 静态代理
执行结果:
优点:
真实对象处理的业务更加纯粹
实现业务分工,公共业务发生扩展时变得更加集中和方便
缺点:
代理类必须提前写好,如果真实对象的接口发生了变化,代理类的代码也要随着变化,维护成本较高
- JDK动态代理
执行结果:
优点:
不需要为真实对象写一个形式上完全一样的封装类,减少了维护成本
可以在运行时制定代理类的执行逻辑,提升了系统的灵活性
缺点:
真实对象必须实现接口,如果没有接口,则不能被代理
- CGLIB动态代理
基本流程:
根据指定的回调类生成的Class字节码,通过defineClass()将字节码定义为类,使用反射机制生成该类的实例
优点:
Cglib代理通过继承的方式进行代理,无论目标对象是否实现接口都可以对其代理,弥补了jdk动态代理的缺陷
缺点:
1、性能好的同时带来的时间上的牺牲,cglib创建代理对象所花费的时间比jdk多得多,所以对于单例对象,因为无需频繁的创建对象,用cglib比较合适,反之,使用jdk方式要合适一些
2、由于cglib采用的是动态创建子类的方法,所以对于final方法,无法进行代理。
上述说了这么多,实际上 在Spring中,使用的Jdk动态代理+cglib动态代理,可以通过配置选定代理方式,例如对于Service层,因为存在接口,我们使用Jdk动态代理,在Controller因为没有接口,可以使用cglib