概述
- 软件开发的目的是为了解决各种真实场景的业务和系统需求。面向对象能对现实世界能够对代码结构进行地抽象与封装,让系统有很好地模块化,扩展性和可维护性。
- 需求与具体实现之间的关系上一对一,但是在系统开发过程中往往会存在一些切面,切面通常与系统逻辑无关,是面向多个或者所有的需求线的一个面。现在我们需要一种能够将这种横切面与业务逻辑解耦的一种方法,那就是AOP,即面向切面编程。
- 但是AOP通常并不能被作为一种主要的开发方式,毕竟建立起一个业务系统主要还是得靠复杂而繁多的业务逻辑。我们通常会使用OOP的方法来开发业务逻辑。而AOP只能一个依附于OOP的一种特性。即是没有AOP我们也能工作,但是有AOP我们能够做得更好。
静态与动态
静态AOP:在程序运行之前的编译期,将各个切面以字节码的形式编译到各个功能模块中。切面以字节码的形式编译到Java类中,Java虚拟机可以像通常一样加载Java类运行,不会对整个系统的运行造成任何的性能损失。 但是每一次切入点需要更改关注点的位置,就需要使用编译器重新编译Aspect重新织入到系统的位置。
动态AOP:通过Java语言提供的各种动态特性来实现Aspect织入到当前系统。AOP的各种实体都是普通的Java类,AOP的织入过程在系统运行开始之后进行,而不是在预编译到系统中。织入信息式大多以XML和注解的形式保存,在修改织入点的同时不必变更系统的其他模块。另一方面,动态AOP的织入时机大多在类加载或者系统运行时,因此动态AOP会对系统造成一定的性能损失。
Java的AOP实现机制
动态代理:通过Java的动态代理来实现AOP, 在运行时为对应的接口生成代理对象,我们开发可以将切面的逻辑封装在动态代理的InvocationHandlder中。所有的被代理的对象必须实现接口。如果某些系统并不遵循面向接口编程,那么Java的动态代理不被支持。
动态字节码:通过ASM或者CGlib等工具在程序运行时生成字节码的class文件,为这些系统的模块类添加子类,通过子类达到将横切面织入系统的目的。程序没有实现接口同样可以进行代理。但是如果被代理的方法被生命未final,那么也不能支持代理类的生成。
代码生成:使用工具,根据描述文件,自动生成被代理的对象。方式在开发中生成。
自定义类加载器:自定义的类加载器通过读取外部文件规则的织入规则和必要的信息,在加载class期间可以将横切面的逻辑添加到系统模块现有的逻辑中。然后将改动的class交给虚拟机执行就是了。通过这种貌似可以实现基于方法体内部的横切面。可谓是功能非常强大。
AOL扩展:属于一种新的 AOP 语言,能够实现几乎所有的横切面类型。但是学习成本过高。
AOP的几大角色
实现AOP需要的几大角色:JoinPoint, PointCut , Advice, Aspect
- JoinPoint
在系统运行之前,我们需要知道系统的哪些执行点需要上进行织入操作,这些执行点就被称为JoinPoint。程序中可以认为必要的执行点都可以认为了JoinPoint,常见的JoinPoint分为下面几类:
① 方法调用, 当方法被调用的时候的执行点。在方法执行之前后。是最常用的一种JoinPoint。
② 方法执行调用, 在方法执行之中的执行点,需要切入到方法逻辑内部。
③ 构造方法, 在构造实例之前、之间、之后的切入点。
④ 字段设置、获取, 对象的字段通过setter或者getter、或者直接赋值、反射设置属性时。
⑤ 异常处理执行,在某种类型的异常被跑出时会执行。
⑥ 类初始化,指代类中的某些静态类型或者静态代码块的初始化点。 - PointCut
对于需要被关注的横切面的表述语言。比如一个检查用户登录的PointCut则需要描述系统中的哪些Controller需要被织入。常见的PointCut的表述方式:
① 直接指定JointPoint所在的方法
② 正则表达式,执行方法满足的正则表达式
③ PointCut表达式,一种类似于正则表达式的语言,通常PointCut的表达式都会使用这种
④ 注解扫描。扫描被注解了某个注解的方法,将这些方法纳入到切面。 - Advice
Advice是对于关注点的横切逻辑,按照Advice在PointCut的位置会有不同的功能。具体地可分为下列的多种:
① 前置通知 Before Advice 在方法执行之前执行的横切逻辑
② 后置通知 After Advice 在方法执行之后执行的横切逻辑
③ 环绕通知 Around Advice 会环绕整个方法执行的横切逻辑,包含前置,后置,异常所有特性
④ Introduction 可以为原有的对象添加新的特性或者行为,并不是按照实际来区分不同的通知类型 - Aspect
Aspect是一个切面的概念实体。通常在一个Aspect中会定义PointCut的描述、执行Advice的逻辑、以及在Advice中会包含的JoinPoint逻辑。因此,相当于Aspect即可以包含一个切面的所有内容的组合体。在Java中,Aspect就是一个Java的类
基本实现
- 代理模式
public interface IRequest {
void request();
}
public class RequestImpl implements IRequest{
@Override
public void request() {
System.out.println("request");
}
}
public class RequestProxy implements IRequest {
private IRequest request;
public RequestProxy(IRequest request) {
this.request = request;
}
@Override
public void request() {
System.out.println("before...");
request.request();
System.out.println("after...");
}
}
public class Client {
public static void