后端SpringSpring-AOP
cmyangAOP面向切面编程
切面的逻辑和业务逻辑进行隔离,降低耦合度,提高代码可重用性
1. 动态代理
AOP通过动态代理来实现,分为两种代理方式,(1)JDK动态代理,(2)CGLIB动态代理
1.1 JDK动态代理
有接口通过JDK动态代理,通过实现接口来创建代理对象
1.2 demo
定义一个接口
1 2 3 4
| public interface TestService { Integer add(String str); Boolean delete(String str); }
|
创建实现类
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class TestServiceImpl implements TestService { @Override public Integer add(String str) { System.out.println("执行add方法:" + str); return 1; }
@Override public Boolean delete(String str) { System.out.println("执行删除方法:" + str); return true; } }
|
创建代理类并执行
1 2 3 4 5 6 7 8 9 10 11 12
| public static void main(String[] args) { TestService testService = new TestServiceImpl(); TestService testServiceProxy = (TestService) Proxy.newProxyInstance(JdkProxyTest.class.getClassLoader(), new Class[]{TestService.class}, (proxy, method, args1) -> { System.out.println("方法执行前执行:" + method.getName()); Object result = method.invoke(testService, args1); System.out.println("方法执行后执行:" + result); return result; }); testServiceProxy.add("hello"); System.out.println("---------------------------"); testServiceProxy.delete("world"); }
|
- 通过Proxy.newProxyInstance来创建代理类,第一个参数为类加载器,第二个参数为需要代理的接口,第三个参数为代理方法,需要自己实现InvocationHandler接口。
执行结果
1 2 3 4 5 6 7
| 方法执行前执行:add 执行add方法:hello 方法执行后执行:1 --------------------------- 方法执行前执行:delete 执行删除方法:world 方法执行后执行:true
|
1.3 CGLIB动态代理
没有接口时,需要使用CGLIB动态代理,通过创建被代理类的子类作为代理对象
2. 基本概念
连接点
切入点
通知
- 增强的逻辑就是通知
- 通知的类型
- 前置通知
- 后置通知,正常执行完通知
- 异常通知
- 环绕通知
- 方法执行完的最终通知,不管有没有异常
切面
3. 切面表达式
表达式模板:execution([权限修饰符][返回值][全类名].[方法名]([参数]))
例如:execution(public void cn.aacopy.learn.spring.aop.UserService.add())
- 通配符可以用*表示
- 修饰符可以省略
- 方法参数可以用
..
表示
4. demo
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.22</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.22</version> </dependency>
|
1 2 3 4 5
| @Configuration @ComponentScan(basePackages = "cn.aacopy") @EnableAspectJAutoProxy public class MyConfig { }
|
1 2 3 4 5 6 7
| @Component public class UserService { public String add(String str) { System.out.println("执行add方法" + str); return str + " @aacopy"; } }
|
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
| @Component @Aspect public class AopTest {
@Before("execution(public String cn.aacopy.learn.spring.aop.UserService.add(..))") public void before(JoinPoint joinPoint) { System.out.println("前置通知执行..." + joinPoint.getSignature().toShortString()); }
@Around("execution(* cn.aacopy.learn.spring.aop.UserService.*(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕通知前..."); Object result = proceedingJoinPoint.proceed(); System.out.println("环绕通知后..." + result); }
@AfterReturning("execution(* cn.aacopy.learn.spring.aop.UserService.*(..))") public void afterReturning() { System.out.println("后置通知执行..."); }
@After("execution(* cn.aacopy.learn.spring.aop.UserService.*(..))") public void after() { System.out.println("最终通知执行..."); }
@AfterThrowing("execution(* cn.aacopy.learn.spring.aop.UserService.*(..))") public void afterThrowing() { System.out.println("异常通知执行..."); } }
|
1 2 3 4 5
| public static void main( String[] args ) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class); UserService userService = applicationContext.getBean(UserService.class); userService.add("hello"); }
|
4.1 提取公共模板
- 编写一个方法,上面使用@Pointcut注解,其他方法直接使用方法名即可,方便统一管理
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
| @Component @Aspect public class AopTest {
@Pointcut("execution(* cn.aacopy.learn.spring.aop.UserService.*(..))") public void pointCutTemplate() {
}
@Before("execution(public String cn.aacopy.learn.spring.aop.UserService.add(..))") public void before(JoinPoint joinPoint) { System.out.println("前置通知执行..." + joinPoint.getSignature().toShortString()); }
@Around("pointCutTemplate()") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕通知前..."); Object result = proceedingJoinPoint.proceed(); System.out.println("环绕通知后..." + result); }
@AfterReturning("pointCutTemplate()") public void afterReturning() { System.out.println("后置通知执行..."); }
@After("execution(* cn.aacopy.learn.spring.aop.UserService.*(..))") public void after() { System.out.println("最终通知执行..."); }
@AfterThrowing("execution(* cn.aacopy.learn.spring.aop.UserService.*(..))") @Async public void afterThrowing() { System.out.println("异常通知执行..."); } }
|
- 如果需要异步执行,只需要在启动类上开启异步@EnableAsync,并在方法上添加@Async注解
5. 优先级
多个AOP切面,设置优先级,可以在切面类上添加@Order注解,值小的,优先执行
1 2 3 4
| @Component @Aspect @Order(-1) public class AopTest {
|
6. 通过注解方式实现切面
1 2 3 4
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RefreshCache { }
|
1 2 3 4
| @RefreshCache public Result<RoleVo> add(@RequestBody RoleAddDto roleAddDto) { return this.success(roleService.add(roleAddDto)); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Component @Aspect @Slf4j public class RefreshCacheAspect {
@Resource private ResourceCacheService resourceCacheService;
@AfterReturning("@annotation(com.xxx.cache.RefreshCache)") @Async public void refreshCache(JoinPoint joinPoint) { log.info("{}执行完成,开始异步刷新缓存...", joinPoint.getSignature().toShortString()); long l1 = System.currentTimeMillis(); resourceCacheService.doCacheAll(); long l2 = System.currentTimeMillis(); log.info("异步刷新Resource缓存成功,耗时:{}ms", (l2-l1)); } }
|