1 什么是 AOP ?
AOP ,即面向切面编程。其作用为对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2 AOP 的作用
将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,将它们独立到非业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
3 部分 AOP 常用术语
切面(Aspect)
Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
连接点(Joint point)
表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。Joint point 是所有可能被织入 Advice 的候选的点, 在 Spring AOP中, 则可以认为所有方法执行点都是 Joint point,通俗的讲,连接点即表示类里面可以被增强的方法。
通知(Advice):定义了切面何时调用
- Before:在方法被调用之前调用通知
- After:在方法完成之后调用通知,无论方法执行是否成功
- After-returning:在方法成功执行之后调用通知
- After-throwing:在方法抛出异常后调用通知
- Around:通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
切点(PointCut)
切点定义了何处,切点的定义会匹配通知所要织入的一个或多个连接点,我们通常使用明确的类的方法名称来指定这些切点,或是利用正则表达式定义匹配的类和方法名称来指定这些切点。PointCut 的作用就是提供一组规则来匹配 joinpoint, 给满足规则的 joinpoint 添加 Advice。
目标对象(Target)
织入 Advice 的目标对象.。
织入(Weaving)
将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
4 Spring AOP 代理对象的生成
AOP 技术应该如何实现?这就不得不说我们以前学习过的一种设计模式了 – 代理模式。实际上实现 AOP 的方法有两种,一是静态织入,使编译器在编译期间织入有关方面的代码;二是动态代理,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行。
Spring AOP 采用的是动态代理方式,提供了JDKProxy 和 Cglib 两种方式来生成代理对象。
JDKProxy 和 Cglib 的异同
- JDK动态代理一般针对实现了接口的类生成代理,若目标没有实现接口,则默认会采用CGLIB代理。若目标实现了接口,使用JDK实现代理。
- 两种动态代理本质上都是字节码组装。
- CGLib的效率没有使用JDK代理机制高,速度平均要慢8倍左右。
5 实战
AOP 在 Spring 中有两种配置方式,一是 xml 配置的方式,二是自动注解的模式。我们平时一般不会使用 xml 配置的方式,故我们今天只讲解自动注解的模式。
导入依赖如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>edu.szu</groupId>
<artifactId>AOPTest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>AOPTest</name>
<url>http://maven.apache.org</url>
<properties>
<spring.version>5.1.5.RELEASE</spring.version>
<aspectj.version>1.9.0</aspectj.version>
<cglib.version>2.2</cglib.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
</dependencies>
</project>
然后我们新建一个配置文件 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="edu.szu.AOPTest"/>
<!-- 激活自动代理功能 -->
<aop:aspectj-autoproxy />
</beans>
然后创建一个类
@Component
public class BookManager {
public void addBook() {
System.out.println("增加书本");
}
public void deleteBook() {
System.out.println("删除书本");
}
public void updateBook() {
System.out.println("修改书本");
}
public void selectBook() {
System.out.println("查询书本");
}
}
新建一个切面,@Aspect 注解表示这是一个切面类。
@Aspect
@Component
public class AspectJAdvice {
// 配置切点
@Pointcut("execution(* edu.szu.AOPTest.BookManager.addBook(..))")
private void aspectJMethod(){};
// 配置连接点 方法开始执行时通知
@Before("aspectJMethod()")
public void doBefore(JoinPoint joinPoint){
System.out.println("doBefore()");
}
// 环绕通知
@Around("aspectJMethod()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("doAround()开始");
//核心逻辑
Object retval = pjp.proceed();
System.out.println("doAround()结束");
return retval;
}
// 方法执行完后通知
@After(value="aspectJMethod()")
public void doAfter(JoinPoint joinPoint){
System.out.println("doAfter()");
}
// 执行成功后通知
@AfterReturning(value="aspectJMethod()")
public void doReturn(JoinPoint joinPoint){
System.out.println("doReturn()");
}
// 抛出异常后通知
@AfterThrowing(value="aspectJMethod()", throwing="e")
public void doThrowing(JoinPoint joinPoint,Exception e){
System.out.println("doThrowing() " + e);
}
}
测试一下
public class Test {
public static void main(String[] args) {
System.out.println("开始测试!");
BeanFactory factory=new ClassPathXmlApplicationContext("applicationContext.xml");
BookManager bookManager=(BookManager) factory.getBean(BookManager.class);
bookManager.addBook();
}
}
结果如图

可以看到,如果增强的方法中不抛出异常的话,@AfterThrowing 标注的方法不会执行,其他四种执行的顺序如图所示。

现在我们修改一下 addBook()方法,增加一个异常,看看执行的结果如何。
public void addBook() {
System.out.println("增加书本");
int i = 1/0;
}

我们可以清晰的看到,当增强的方法抛出异常时,环绕通知后面那一半跟执行成功通知不再执行,并执行抛出异常后通知。

本文深入解析Spring AOP的原理及应用,介绍AOP概念、作用、术语,对比JDKProxy与Cglib,演示AOP在Spring中的配置与实战。
5152

被折叠的 条评论
为什么被折叠?



