AOP 面向切面编程
在不改变原始设计的基础上进行功能增强,


导入坐标
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底层是通过代理对象的方式实现的。


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+.*(..))*
|

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)); }
|