文章目录
Spring AOP
AOP(Aspect Oriented Programming):面向切面编程,可以把各类的公共行为封装到一个可重用模块,并将其命名为 Aspect,即切面,减少系统的重复代码,降低模块之间的耦合。
常用的主要功能:日志记录,性能统计,安全控制,事务处理,异常处理。
相关术语
- 通知(Advice):指拦截到连接点之后要执行的代码,通知又分为五种:
- 前置通知(Before):在目标方法被调用之前调用通知
- 后置通知(After):在目标方法完成之后调用通知
- 返回通知(After-returning):在目标方法成功执行之后调用通知
- 异常通知(After-throwing):在目标方法抛出异常后调用通知
- 环绕通知(Around):在目标方法被调用前后执行通知
- 连接点(Join point):指被拦截到的方法
- 切点(Pointcut):对连接点进行拦截的定义
- 切面(Aspect):是通知和切点的结合,是对横切关注点的抽象
- 引入(Introduction):允许添加新方法或属性到现有的类中
- 织入(Weaving):将切面应用到目标对象并创建新的代理对象的过程
- 编译器:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ 就是以这种方式织入切面
- 类加载期:切面在目标类加载到 JVM 时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ 5 的加载时织入(load-time weaving,LTW)就支持以这种方式织入切面
- 运行期:切面在应用运行的某个时刻被织入。一般在织入切面时,AOP 容器会为目标对象动态创建一个代理对象。Spring AOP 就是以这种方式织入切面
- 目标对象(Target object):被一个或者多个切面所通知的对象,这个对象永远是一个被代理对象
AspectJ 切点表达式
AspectJ 指示器 | 描述 |
---|---|
arg() | 限制连接点匹配参数为指定类型的执行方法 |
@args() | 限制连接点匹配参数由指定注解标注的执行方法 |
execution() | 用于匹配是连接点的执行方法 |
this | 限制连接点匹配 AOP 代理的 bean 引用为指定类型的类 |
target | 限制连接点匹配目标对象为指定类型的类 |
@target() | 限制连接点匹配由指定注解标注的执行对象 |
within() | 限制连接点匹配指定的类型 |
@within() | 限制连接点匹配指定注解标注的类型 |
@annotation | 限定匹配带有指定注解的连接点 |
通配符
*
匹配任意数量的字符+
匹配指定类及其子类..
匹配任意数的子包或参数
运算符
&&
、and
与操作符||
、or
或操作符!
、not
非操作符
使用注解声明切面
注解 | 描述 |
---|---|
@EnableAspectJAutoProxy | 启用自动代理功能 |
@AspectJ | 标注切面 |
@Pointcut | 标注可重用的切点 |
@After | 通知方法会在目标方法调用之后执行 |
@AfterReturning | 通知方法在目标方法返回后调用 |
@AfterThrowing | 通知方法在目标方法抛出异常后调用 |
@Around | 通知方法将目标方法封装起来 |
@Before | 通知方法会在目标方法调用之前执行 |
1. 声明切面
@Aspect
:表明类 Audience 是一个切面。
@Pointcut
:在切面内定义可重用的切点。
@Around
:环绕通知必须接收一个 ProceedingJointPoint
参数。通过调用 ProceedingJointPoint
的 proceed()
方法调用被通知的目标方法,否则会阻塞对被通知目标方法的访问。
@Aspect
@Component
public class Audience {
@Pointcut("execution(* com.chen.aop.Concert.perform(..))")
public void performance(){
}
@Before("performance()")
public void before(){
System.out.println("Before performance.");
}
@After("performance()")
public void after(){
System.out.println("After performance.");
}
@AfterReturning("performance()")
public void applause(){
System.out.println("CLAP CLAP CLAP!!!");
}
@AfterThrowing("performance()")
public void demandRefund(){
System.out.println("Demanding a refund.");
}
@Around("performance()")
public void log(ProceedingJoinPoint joinPoint){
System.out.println("Log info.");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
System.out.println("Log error.");
}
}
}
定义 Performance 接口及其实现类 Concert
public interface Performance {
void perform();
}
@Component
public class Concert implements Performance {
@Override
public void perform() {
System.out.println("Concert perform.");
// throw new RuntimeException();
}
}
2. 自动化装配 Bean
@EnableAspectJAutoProxy
:启动自动代理功能。proxyTargetClass
默认为 false,表示使用JDK动态代理技术织入通知;当配置为 true 时,表示使用CGLib动态代理技术织入通知。
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig {
}
3. 测试
public class PerformanceTest {
@Test
public void test() {
ApplicationContext context = new AnnotationConfigApplicationContext(ConcertConfig.class);
Performance performance = context.getBean(Performance.class);
performance.perform();
}
}
使用 XML 配置声明切面
AOP 配置元素 | 用途 |
---|---|
<aop:advisor> |
定义 AOP 通知 |
<aop:after> |
定义 AOP 后置通知 |
<aop:after-returning> |
定义 AOP 返回通知 |
<aop:after-throwing> |
定义 AOP 异常通知 |
<aop:around> |
定义 AOP 环绕通知 |
<aop:aspect> |
定义一个切面 |
<aop:aspectj-autoproxy> |
启用 @AspectJ 注解驱动的切面 |
<aop:before> |
定义 AOP 前置通知 |
<aop:config> |
顶层的 AOP 配置元素 |
<aop:declare-parents> |
以透明的方式为被通知的对象引入额外的接口 |
<aop:pointcut> |
定义一个切点 |
<?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"
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">
<bean class="com.chen.aop.Concert"/>
<bean id="audience" class="com.chen.aop.Audience"/>
<aop:aspectj-autoproxy/>
<aop:config>
<aop:aspect ref="audience">
<aop:pointcut id="performance" expression="execution(* com.chen.aop.Performance.perform(..))"/>
<aop:before pointcut-ref="performance" method="before"/>
<aop:after pointcut-ref="performance" method="after"/>
<aop:after-returning pointcut-ref="performance" method="applause"/>
<aop:after-throwing pointcut-ref="performance" method="demandRefund"/>
<aop:around pointcut-ref="performance" method="log"/>
</aop:aspect>
</aop:config>
</beans>
Spring AOP 的两种代理方式
- 基于 JDK 接口的动态代理
- 基于 CGLib 动态生成子类的代理
如果目标对象实现了接口,则默认采用 JDK 动态代理
如果目标对象没有实现接口,则采用 CGLib 动态代理
如果目标对象实现了接口,且强制 CGLib 代理,则使用 CGLib 代理
Spring AOP 与 AspectJ 区别
Spring 在运行时通知对象
代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标类。
当代理拦截到方法调用时,在调用目标对象方法之前,会执行切面逻辑。
扫描二维码关注公众号,回复:
6656486 查看本文章
Spring 只支持方法级别的连接点
Spring 基于 JDK 动态代理,因此只支持方法连接点
AspectJ、JBoss 支持方法、字段和构造器连接点
应用
- 事务
@Transational
- 安全校验
@PreAuthorize
- 缓存
@Cacheable