Spring AOP 详解

3,236 阅读7分钟

一、AOP 简介

    AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP只允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志,事务,安全等。这些功能都是横向应用在业务处理中,而与它们对应的方法与其他代码基本没有联系,如异常处理和透明的持续性也都是如此,不仅增加了大量的代码量,还为程序后期的维护增生很多困难。
    AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,
将核心关注点和横切关注点分离开来。

二、AOP 基本了解和通知方法

    1、切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在spring AOP中,切面可以使用基于模式)或者基于Aspect注解方式来实现。通俗点说就是我们加入的切面类(比如log类),可以这么理解。
    2、连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。通俗的说就是加入切点的那个点
    3、通知(Advice):在切面的某个特定的连接点上执行的动作。其中包括了“around”、“before”和“after”等不同类型的通知(通知的类型将在后面部分进行讨论)。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
    4、切入点(Pointcut):匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
    5、引入(Introduction):用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制。
    6、目标对象(Target Object): 被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
    7、AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
    8、织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

通知方法:

    1.前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
    2.后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
    3.异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
    4.最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
    5.环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。
它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。

三、Spring对AOP的支持

    Spring中AOP代理由Spring的IOC容器负责生成,管理,它的依赖关系也有IOC容器负责。因此,AOP代理可以直接使用容器中的其他bean示例作为目标,这种关系可由IOC容器的依赖注入提供(不熟悉依赖注入的可以看看我之前的博文)。Spring创建代理规则为:
    1、默认使用JDK动态代理来创建AOP代理,这样可以为任何接口示例创建代理
    2、当需要代理的类不是代理接口时,Spring会切换为使用CGLIB代理,也可强制使用CGLIB代理。(强制方法:在XML配置中修改 AOP 属性spring.aop.proxy-target-class=true或在注释中修改@EnableAspectJAutoProxy(proxyTargetClass = true)进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法

四、Spring AOP 栗子

1、定义一个接口

package com.samter.common;
public interface car {
void run(String name);
}

2、接口的实现类

package com.samter.commonImpl;
public class carImpl implements car{
public void run(String name) {
System.out.println("【一辆】"+name+"正在飙车...");
}
}

3、切面

package com.samter.aspect;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class Aspects {
// 第一个*代表任意返回值类型 后面的表示扫描com.samter下的所有包
// 包下的所有类,类里面的所有方法,方法的所有参数
@Pointcut("execution(* com.samter.*.*.*(..))")
public void foundCar() {}
@Before(value="foundCar()")
public void foundBefore() {
System.out.println("【交警】发现一辆车正在飙车...");
}
@AfterReturning("foundCar() && args(name,..)")
public void foundAfter(String name) {
System.out.println("【交警】拦下一辆“"+name+"”...");
}
@AfterReturning("foundCar()")
public void foundAfter() {
System.out.println("【交警】开罚单");
}
}

4、XML配置


5、测试

package com;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.samter.common.Pig;
import com.samter.common.commo.Dog;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Car car = (Car) context.getBean("car");
car.run(“宝马”);
}
}

6、结果

【交警】发现一辆车正在飙车...
一辆宝马正在飙车...
【交警】拦下一辆宝马...
【交警】开罚单

7、后置

1.运行前别忘记导 jar 包(没有去网上下)
2.运行错误时查看是不是类名写错了或者是XML文件没有读取到,XML放在根路径下
3.写测试类是注意不要导错文件包

8、后记

这里只写了前置通知和后置通知,其他通知除环绕通知外用法都差不多,环绕通知大家可以去百度下,
网上许多大神都有详细的栗子。

五、流程图