AOP 面向切面编程

在不改变原始设计的基础上进行功能增强,

image.png

image.png

导入坐标

1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.22</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
</dependency>
</dependencies>

AOP案例

在BookDao里有共性方法

1
2
3
4
5
6
7
8
9
10
11
12
13
@Repository
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println(System.currentTimeMillis());
System.out.println("book dao save...");
}

@Override
public void update() {
System.out.println("book dao update...");
}
}

抽取出来建立advice类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
@Aspect
public class MyAdvice {
/*定义切入点*/
@Pointcut("execution(void cola.dao.BookDao.update())")
private void pt() {
}

/*定义共性功能*/
@Before("pt()")
public void method() {
System.out.println(System.currentTimeMillis());
}
}

在config中声明注解方法实现AOP

1
2
3
4
5
@Configuration
@ComponentScan("cola")
@EnableAspectJAutoProxy
public class SpringConfig {
}

实现

在切入点update方法中就可以实现共性方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Repository
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println(System.currentTimeMillis());
System.out.println("book dao save...");
}

@Override
public void update() {
System.out.println("book dao update...");
}
}

AOP工作流程

注意AOP底层是通过代理对象的方式实现的。

image.png

image.png

AOP切入点表达式

1
execution(public void cola.dao.BookDao.update())

可以使用通配符描述切入点,快速描述

*:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现,切入点表达式标准格式∶动作关键字(访问修饰符﹐返回值﹐包名.类/接口名.方法名(参数)异常名),匹配cola包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法。

1
execution (public * cola.*.UserService.find*(*))

..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写,匹配com包下的任意包中的UserService类或接口中所有名称为findByld的方法

1
execution (public User cola..UserService.findById (..) )

+:专用于匹配子类类型

1
execution(* *..*Service+.*(..))*

image.png

AOP通知类型

注意由于代理模式,环绕通知如果需要有返回值,则需要提供返回值,且这个返回值会覆盖原切入点中产生的返回值。如果环绕通知中不使用ProceedingJoinPoint,将跳过原始方法的执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Component
@Aspect
public class MyAdvice {
/*定义切入点*/
@Pointcut("execution(void cola.dao.BookDao.update())")
private void pt() {
}
@Pointcut("execution(int cola.dao.BookDao.select())")
private void pt1() {
}

@Before("pt()")
public void before() {
System.out.println(System.currentTimeMillis());
}
@After("pt()")
public void after() {
System.out.println(System.currentTimeMillis());
}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(System.currentTimeMillis());
Object ret = pjp.proceed();
System.out.println(System.currentTimeMillis());
return ret;
}
@Around("pt1()")
public Object around1(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(System.currentTimeMillis());
Integer num = (Integer) pjp.proceed();
System.out.println(System.currentTimeMillis());
return num;
}
}

AOP通知获取数据

加入JoinPoint形参调用参数。ProceedingJoinPoint一样可以,而且可以改变传入的参数后再用proceed方法传回原方法中执行。

1
2
3
4
5
@Before("pt1()")
public void before(JoinPoint jp) {
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
}