Spring AOP
AOP
AOP是什么
是一种面向切面编程的思想,传统的OOP开发中的代码逻辑是自上而下的,而这些过程会产生一些横切性问题,这些横切性的问题和我们的主业务逻辑关系不大,这些横切性问题不会影响到主逻辑实现的,但是会散落到代码的各个部分,难以维护。AOP是处理一些横切性问题,AOP的编程思想就是把这些问题和主业务逻辑分开,达到与主业务逻辑解耦的目的。使代码的重用性和开发效率更高。
AOP的应用场景
- 日志记录
- 权限验证
- 事务管理
Spring AOP和AspectJ
AOP,Spring AOP,AspectJ三者之间的关系
- AOP是一种思想。
- SpringAop、AspectJ都是AOP的实现,Spring AOP有自己的语法,但是语法复杂,所以Spring AOP借助了AspectJ的注解,但是底层实现还是自己的。
- Spring AOP提供了两种编程风格
@AspectJ support ------------>利用aspectj的注解
Schema-based AOP support ----------->xml aop:config 命名空间
Spring AOP的底层技术
使用了动态代理,包含jdk动态代理和Cglib动态代理。并且在初始化时期织入。
Spring AOP的一些基本概念
-
aspect:切面
- 在类上声明 @Aspect
- 交给Spring管理 @Component
-
pointcut:切点
- 连接点的集合
- 告诉通知连接点在哪里
- 切点表达式决定joinpoint的数量
-
joinpoint:连接点
- 目标对象的方法
- 要关注和增强的方法,也就是我们要作用的点。
-
weaving:织入,把代理逻辑加入到目标对象上的过程
-
target:目标对象(原始对象)
-
aop proxy: 代理对象,包含了原始对象的代码和增加后的代码的那个对象
-
advice:通知(位置 + logic)
-
advice通知类型
- Before 连接点执行之前,无论执行正常还是异常,都会进行前置通知
- After 连接点执行之后,无论执行正常还是异常,都会进行后置通知
- After throwing 连接点执行抛出异常的时候,异常通知
- After returning 连接点正常执行完成的时候,返回通知
- Around advice 围绕连接点执行,例如方法调用。这是最有用的切面方式。around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续加入点还是通过返回自己的返回值或抛出异常来快速建议的方法执行。环绕通知
- 通知的优先顺序:环绕通知(前)->前置通知->目标方法->环绕通知(后)->后置通知->返回通知/异常通知
- Spring只支持方法级的增强
Spring AOP案例
- 创建maven项目,并引入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
</dependencies>
- 使用Java Config启用@AspectJ支持
package com.example.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.example")
@EnableAspectJAutoProxy
public class AppConfig {
}
- 声明一个切面类
package com.example.config;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 声明一个切面
* 此切面一定要交给Spring管理
*/
@Aspect
@Component
public class TxAspect {
@Pointcut("execution(* com.example.dao.*.*(..))")
public void pointCut(){
}
@After("com.example.config.TxAspect.pointCut()")
public void after(){
System.out.println("after...");
}
/**
* before通知,在pintCut切入点前执行
*/
@Before("com.example.config.TxAspect.pointCut()")
public void before(){
System.out.println("before...");
}
@AfterThrowing("com.example.config.TxAspect.pointCut()")
public void doRecoveryActions(){
System.out.println("异常");
}
}
- 新建dao接口和IndexDao类
package com.example.dao;
public interface Index {
void query();
}
package com.example.dao;
import org.springframework.stereotype.Component;
@Component
public class IndexDao implements Index {
@Override
public void query(){
System.out.println("dao----query");
System.out.println(1/0);
}
}
- 新建测试类
package com.example.test;
import com.example.config.AppConfig;
import com.example.dao.Index;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Index dao1 = context.getBean(Index.class);
dao1.query();
}
}
- 运行结果如下