AOP是什么?
Aspect Oriented Programing:面向切面编程。
AOP是继OOP之后,对编程设计思想影响最大的技术之一。AOP是进行横切逻辑编程的思想,它开拓了人们考虑问题的思路,但这不是Spring独有的,AOP体系结构下有很多的实现者,例如:AspectJ、AspectWerkz、JBoss AOP、Spring AOP等。AOP思想的出现到底是为了解决什么问题?
-
要解决的问题:
按照软件重构思想的理念,如果多个类中出现相同的代码,应该考虑定义一个共同的抽象类,将这些相同的代码提取到抽象类中。但并非所有的场景都可以这样,比如事务、日志、安全等等。这些代码本身就和方法中的其他代码有前后的逻辑关联,无法纵向抽取到父类中。AOP独辟蹊径通过横向抽取机制为这类无法通过纵向继承体系进行抽象的重复性代码提供了解决方案。
-
实现原理:
首先为目标对象生成一个代理类,使用者不直接使用目标对象,而是使用代理类。代理类会在方法执行时,给方法不同位置注入不同的逻辑代码(具体要根据配置信息),实现诸如上面提到的事务、安全、日志等功能。底层实现时通过JDK动态代理或CGLib实现,设计到反射机制。从而把和业务无关的代码,抽取了出来,并可以在合适的时间注入。
相关术语:
AOP中一些术语需要先了解下。
1.连接点:Joinpoint
程序执行的某个特定位置,比如类的开始初始化前、类初始化后、类的某个方法调用前和调用后、方法抛出异常后。Spring对AOP思想的实现仅支持方法的连接点,即仅能在方法调用前后或抛出异常时织入增强。
2. 切点:Pointcut
AOP通过切点定位特定的连接点,一个切点可以匹配多个连接点。Spring中通过 xxx.apo.Pointcut接口进行描述,它使用类和方法作为查询条件,Spring AOP的规则解析引擎负责解析并查找对应的连接点,因为连接点包含了方法执行前、后等方位信息,所以切点如果想定位到具体的连接点上,还需要方位信息。
3.增强:Advice
增强是织入目标类连接点上的一段程序代码。这一点和木马有点类似。增强除了带有一段程序代码,还拥有连接点的方位信息。再结合切点就可以确定连接点,并实施增强逻辑。所以Spring提供的增强接口都是带方位的:BeforeAdvice、AfterRetuningAdvice、ThrowsAdvice等。
4.目标对象:Target
目标业务类
5.引介:Introduction
引介是一种特殊的增强,它为类添加一些熟悉和方法。这样,即使一个业务类原本没有实现某个 接口,通过AOP引介功能,也可以动态的为该业务类添加接口的实现逻辑。
6.织入:Weaving
织入是将增强添加到目标类具体连接点上的过程,AOP像一台织布机将目标类、增强或者引介通 过AOP编织到一起。
织入方式:
1.编译期织入:
2.类装载期织入:
3.动态代理织入:
Spring中采用动态代理织入。
7.代理:Proxy
一个类被AOP织入增强后,就产生了一个结果类。它是融合了原类和增强逻辑的代理类。
8.切面:Aspect
切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义。
增强的类型:
AOP联盟为增强定义了org.aopalliance.aop.Advice接口。Spring中支持5种类型的增强,按照增强织入的位置分为以下几类:
-
前置增强:
rg.springframework.aop.BeforeAdvice代表前置增强,因为Spring只支持方法级的增强,所以MethodBeforeAdvice是目前可用的前置增强,表示在目标方法执行前实施增强,而BeforeAdvice是为了将来版本扩展需要而定义的。
-
后置增强:
work.aop.AfterReturningAdvice代表后增强,表示在目标方法执行后实施增强。
-
环绕增强:
rg.aopalliance.intercept.MethodInterceptor代表环绕增强,表示在目标方法执行前后实施增强。
-
异常抛出增强:
org.springframework.aop.ThrowsAdvice代表抛出异常增强,表示在目标方法抛出异常后实施增强。
-
引介增强:
org.springframework.aop.IntroductionInterceptor代表引介增强,表示在目标类中添加一些新的方法和属性。
这些增强接口都有一些方法,通过实现这些接口方法,在接口方法中定义横切逻辑,就可以将它们织入目标类方法的相应连接点的位置。
前置增强示例代码:
1.首先新建一个接口:Water
它有两个方法,分别是greetTo和serveTo
public interface Waiter {
void greetTo(String name);
void serveTo(String name);
}
2.实现类:NaiveWaiter 。
public class NaiveWaiter implements Waiter {
public void greetTo(String name) {
System.out.println("greet to " + name + "...");
}
public void serveTo(String name) {
System.out.println("serving " + name + "...");
}
}
3.新建一个前置增强: 只需要集成MethoBeforeAdvice
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object obj) throws Throwable {
String clientName = (String) args[0];
System.out.println("How are you!Mr." + clientName + ".");
}
}
参数说明:
method为目标类的方法;
args为目标类方法的入参;
而obj为目标类实例。当该方法发生异常时,将阻止目标类方法的执行。
4.测试:
public class BeforeAdviceTest {
private Waiter target;
private BeforeAdvice advice;
private ProxyFactory pf;
@BeforeTest
public void init() {
target = new NaiveWaiter();
advice = new GreetingBeforeAdvice();
//①Spring提供的代理工厂
pf = new ProxyFactory();
// ②设置代理目标
pf.setTarget(target);
//③为代理目标添加增强
pf.addAdvice(advice);
}
@Test
public void beforeAdvice() {
Waiter proxy = (Waiter) pf.getProxy();
proxy.greetTo("John");
proxy.serveTo("Tom");
}
}
打印结果:
How are you!Mr.John. //通过增强引入的礼貌用语
greet to John...
How are you!Mr.Tom. //通过增强引入的礼貌用语
serving Tom...
小结:
通过引入一个前置增强,并通过代理类织入增强,在每次调用时,都能将增强代码植入。这样就做到了增强逻辑和业务逻辑的分离。诸如事务,安全等都是类似的原理。