Spring配置AOP | 总字数: 2.4k | 阅读时长: 10分钟 | 浏览量: |
Spring配置面向切面编程 AOP 什么是AOP 我也是刚学,就说一下我的个人理解,肯定是有点儿问题的
AOP就是面向切面编程英文的缩写,它的目的就是用来增强已经开发好的功能的,并且不用更改之前的功能代码
AOP的概念中有一些名词,我的解释有些模糊
连接点在Spring的AOP中,连接点就是一个类中所有的方法 切入点就是你需要增强的方法 通知需要增强的功能 切面是通知与切入点的关系 如何实现的 配置好规则后,Spring会将需要增强的类的bean替换成代理对象,使用的其实是代理类,不是目标对象
在Spring中配置AOP 导入所需要的坐标 spring-aop在导入spring-context坐标时就已经被包含在其中了,所以不用导此坐标
还需要一个面向切面编程的实现包,导入aspectjweaver 坐标,我使用的是1.9.6
版本
1 2 3 4 5 6 <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.6</version > </dependency >
创建切面类 创建org.example.aop.MyAspect切面类
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 package org.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 MyAspect { @Pointcut("execution(public * org.example.service.impl.UserServiceImpl.find*(..))") private void servicePointCut () {} @Before("MyAspect.servicePointCut()") public void before () { System.out.println("我是前置通知" ); } }
增加一些配置 在SpringConfig配置类中添加org.example.aop包的扫描,并开启切面自动代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package org.example.config;import org.springframework.context.annotation.*;@Configuration @ComponentScan({"org.example.service","org.example.aop"}) @PropertySource({"classpath:jdbc.properties"}) @Import({JdbcConfig.class, MybatisConfig.class}) @EnableAspectJAutoProxy public class SpringConfig {}
测试是否成功 运行启动类
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 package org.example;import org.example.config.SpringConfig;import org.example.domain.User;import org.example.service.UserService;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import java.util.List;public class App { public static void main ( String[] args ) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (SpringConfig.class); UserService service = context.getBean(UserService.class); List<User> users = service.findAll(); System.out.println(users); context.registerShutdownHook(); } }
如果执行了前置通知方法表示配置正确,以下是我的输出
1 2 3 4 5 6 UserServiceImpl被创建了 我是前置通知 十二月 09, 2022 1:14:44 下午 com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl info 信息: {dataSource-1} inited [User{id=1, name='马昆', gender='male', age=18, phone='19960796404', birthday=2001-06-24}, User{id=2, name='马强', gender='female', age=23, phone='18328195555', birthday=1999-09-24}, User{id=3, name='罗海人', gender='female', age=22, phone='13547682222', birthday=2000-11-03}] UserServiceImpl要被销毁了
常用的一些通知 所有案例
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 package org.example.aop;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;@Component @Aspect public class MyAspect { @Pointcut("execution(public * org.example.service.impl.UserServiceImpl.find*(..))") private void servicePointCut () {} @Pointcut("execution(public void org.example.service.impl.UserServiceImpl.testException())") private void serviceExceptionPointCut () {} @Before("MyAspect.servicePointCut()") public void before () { System.out.println("我是前置通知" ); } @After("MyAspect.servicePointCut()") public void after () { System.out.println("我是后置通知" ); } @Around("MyAspect.servicePointCut()") public Object around (ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Signature signature = proceedingJoinPoint.getSignature(); String declaringTypeName = signature.getDeclaringTypeName(); String name = signature.getName(); long start = System.currentTimeMillis(); Object result = proceedingJoinPoint.proceed(); long end = System.currentTimeMillis(); System.out.println(declaringTypeName + "." + name + "使用了" + (end - start) + "ms" ); return result; } @AfterReturning(value = "MyAspect.servicePointCut()", returning = "result") public Object afterReturn (JoinPoint joinPoint, Object result) { Signature signature = joinPoint.getSignature(); System.out.println("这是" + signature.getName() + "方法" ); System.out.println(result); return result; } @AfterThrowing(value = "MyAspect.serviceExceptionPointCut()", throwing = "throwable") public void afterException (JoinPoint joinPoint, Throwable throwable) { System.out.println(joinPoint.getSignature().getName() + "发生了" + throwable.getMessage() + "异常" ); } }
前置通知 用于切入点方法执行前
1 2 3 4 5 @Before("MyAspect.servicePointCut()") public void before () { System.out.println("我是前置通知" ); }
后置通知 用于切入点方法执行后
1 2 3 4 5 @After("MyAspect.servicePointCut()") public void after () { System.out.println("我是后置通知" ); }
环绕通知 这个通知非常常用
用于包裹住切入点方法的执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Around("MyAspect.servicePointCut()") public Object around (ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Signature signature = proceedingJoinPoint.getSignature(); String declaringTypeName = signature.getDeclaringTypeName(); String name = signature.getName(); long start = System.currentTimeMillis(); Object result = proceedingJoinPoint.proceed(); long end = System.currentTimeMillis(); System.out.println(declaringTypeName + "." + name + "使用了" + (end - start) + "ms" ); return result; }
后置返回通知 用于方法执行完成后
1 2 3 4 5 6 7 8 @AfterReturning(value = "MyAspect.servicePointCut()", returning = "result") public Object afterReturn (JoinPoint joinPoint, Object result) { Signature signature = joinPoint.getSignature(); System.out.println("这是" + signature.getName() + "方法" ); System.out.println(result); return result; }
后置异常通知 用于方法抛出异常后
1 2 3 4 5 @AfterThrowing(value = "MyAspect.serviceExceptionPointCut()", throwing = "throwable") public void afterException (JoinPoint joinPoint, Throwable throwable) { System.out.println(joinPoint.getSignature().getName() + "发生了" + throwable.getMessage() + "异常" ); }
需要定义一个会抛异常的切入点
1 2 3 public void testException () { int i = 1 / 0 ; }
执行结果 配置上述所有的通知后,执行启动类代码
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 package org.example;import org.example.config.SpringConfig;import org.example.domain.User;import org.example.service.UserService;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import java.util.List;public class App { public static void main ( String[] args ) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (SpringConfig.class); UserService service = context.getBean(UserService.class); List<User> users = service.findAll(); service.testException(); context.registerShutdownHook(); } }
执行结果如下
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 UserServiceImpl被创建了 我是前置通知 十二月 09, 2022 1:47:51 下午 com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl info 信息: {dataSource-1} inited 这是findAll方法 [User{id=1, name='马昆', gender='male', age=18, phone='19960796404', birthday=2001-06-24}, User{id=2, name='马强', gender='female', age=23, phone='18328195555', birthday=1999-09-24}, User{id=3, name='罗海人', gender='female', age=22, phone='13547682222', birthday=2000-11-03}] 我是后置通知 org.example.service.UserService.findAll使用了222ms testException发生了/ by zero异常 Exception in thread "main" java.lang.ArithmeticException: / by zero at org.example.service.impl.UserServiceImpl.testException(UserServiceImpl.java:47) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:64) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) at com.sun.proxy.$ Proxy37.testException(Unknown Source) at org.example.App.main(App.java:18) 进程已结束,退出代码1
切入点的匹配规则 *
表示单位内随意..
表示任意个随意1 2 3 4 5 6 7 @Pointcut("execution(public * org.example.service.impl.UserServiceImpl.find*(..))") private void servicePointCut () {}@Pointcut("execution(* * ..find*(..))") private void servicePointCut () {}