1、什么是AOP
AOP(Aspect Oriented Programming)面向切面编程,一种编程范式。在不改动原始设计的基础上进行功能增强。
2、AOP的核心概念
(1)连接点:连接点就是可进行功能增强的方法。下列几个方法就是连接点。
public void select(){
System.out.println("userDao select...");
}
public void delete(){
System.out.println("userDao delete...");
}
public void save(){
System.out.println("userDao save...");
}
public void update(){
System.out.println("userDao update...");
}
(2)切入点:切入点就是需要进行功能增强的方法。比如在上述连接点中只对select方法和delete方法进行增强,那么这两个方法就是切入点。
public void select(){
System.out.println("userDao select...");
}
public void delete(){
System.out.println("userDao delete...");
}
(3)通知:通知是指进行功能增强的代码,假设上述切入点需要使用enhance方法进行增强,那么enhance就是一个通知。
public void enhance(){
System.out.println("我是增强功能");
}
(4)切面:在确定切入点和通知后,需要将切入点和通知进行绑定,以确保对对应的方法进行增强,那么这种进行切入点和通知绑定的操作就是切面。
3、AOP的基本使用
(1)导入依赖坐标
spring依赖包中含有aop的依赖包,但还需要额外导入一个aspectjweaver依赖包。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
(2)定义连接点
package com.example.dao;
import org.springframework.stereotype.Component;
@Component
public class UserDao {
public void select(){
System.out.println("userDao select...");
}
public void delete(){
System.out.println("userDao delete...");
}
public void save(){
System.out.println("userDao save...");
}
public void update(){
System.out.println("userDao update...");
}
}
(3)创建通知
package com.example.aop;
public class MyAOP {
public void enhance(){
System.out.println("enhance running...");
}
}
(4)定义切入点
package com.example.aop;
import org.aspectj.lang.annotation.Pointcut;
public class MyAOP {
@Pointcut("execution(void com.example.dao.UserDao.select())")
private void pt(){}
public void enhance(){
System.out.println("enhance running...");
}
}
(5)绑定切入点和通知
package com.example.aop;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
public class MyAOP {
@Pointcut("execution(void com.example.dao.UserDao.select())")
private void pt(){}
@Before("pt()")
public void enhance(){
System.out.println("enhance running...");
}
}
(6)将AOP类定义成bean交给Spring管理并告知Spring此类为AOP类
package com.example.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAOP {
@Pointcut("execution(void com.example.dao.UserDao.select())")
private void pt(){}
@Before("pt()")
public void enhance(){
System.out.println("enhance running...");
}
}
(7)开启Spring对AOP注解驱动支持
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 SpringConfig {
}
4、AOP切入点表达式
execution(public void com.example.dao.UserDao.select(int))
使用通配符:
(1)* :匹配任意字符
// 匹配所有以 "get" 开头的方法
@Before("execution(* get*(..))")
// 匹配 com.example.service 包下的所有类的所有方法
@Before("execution(* com.example.service.*.*(..))")
// 匹配 UserService 类中的所有方法
@Before("execution(* com.example.service.UserService.*(..))")
(2).. :匹配任意层级的子包或任意参数,该通配符不能单独使用。
// 匹配 com.example 包及其子包下的所有类的所有方法
@Before("execution(* com.example..*.*(..))")
// 匹配所有方法,不管参数是什么
@Before("execution(* *(..))")
// 匹配第一个参数是 String,后面可以有任意参数的方法
@Before("execution(* *(String, ..))")
(3)+ :匹配子类或实现类
// 匹配 UserDao 接口的所有实现类的方法
@Before("execution(* com.example.dao.UserDao+.*(..))")
// 匹配 Repository 注解的所有子类
@Before("execution(* (@org.springframework.stereotype.Repository +).*(..))")
5、AOP通知类型
(1)原始方法执行前
@Before("pt1()")
public void beforeEnhance(){
System.out.println("beforeEnhance running...");
}
(2)原始方法执行后
@After("pt2()")
public void afterEnhance(){
System.out.println("beforeEnhance running...");
}
(3)环绕执行方法
其中pjp.proceed()表示原始方法的执行。
@Around("pt3()")
public void aroundEnhance(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around before running...");
pjp.proceed();
System.out.println("around after running...");
}
若原始方法有返回值,则增强方法也需定义对应的返回值,并在调用原始方法后,经过增强的原始方法返回值为增强方法的返回值。例如原始方法返回值为100,增强方法返回值为200,调用原始方法后得到的值为200。
@Around("pt4()")
public int aroundEnhanceReturn(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around before running...");
pjp.proceed();
System.out.println("around after running...");
return 200;
}
若要返回同原始方法一样的返回值,则需使用Oject对象接收pjp.proceed()的返回值,并将接到的返回值返回。
@Around("pt4()")
public Object aroundEnhanceReturn(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around before running...");
Object ob = pjp.proceed();
System.out.println("around after running...");
return ob;
}
6、AOP获取参数
使用JoinPoint接口的getArgs()方法获取原始方法的参数,该接口是ProceedingJoinPoint接口的父接口,所以ProceedingJoinPoint也可以调用getArgs()方法获取原始方法的参数。
@Before("pt1()")
public void beforeEnhance(JoinPoint jp){
Object[] args = jp.getArgs();
System.out.println(args.toString());
System.out.println("beforeEnhance running...");
}
7、AOP原始方法执行后获取原始方法的返回值
returning的值须与Object类型的变量值一致。若有JoinPoint参数,,则JoinPoint必须在方法参数第一位置。
@AfterReturning(value = "pt2()",returning = "obj")
public void afterEnhance(Object obj){
System.out.println("afterEnhance running..."+obj);
}