> 文章列表 > 【学习笔记】SpringAOP的用法全解

【学习笔记】SpringAOP的用法全解

【学习笔记】SpringAOP的用法全解

文章目录

  • Spring的AOP
    • 一、 Spring对AOP的实现包括以下3种方式
      • 什么是AspectJ?
    • 二、使用Spring的AOP
      • 1、准备工作
      • 2、尝试写一个简单的AOP demo
      • 3、代码如下:
        • spring.xml
        • 业务类
        • 切面
        • 测试类
      • 4、复习切面表达式
        • 1)所有方法
        • 2)指定路径下某个包及其子包的所有方法
        • 3)限定public的,某个包及其子包的,带login开头的,且参数是Stirng 的 方法
      • 5、5种不同的通知如何写?
        • 思考1,5种通知都存在时,执行的顺序是什么样的?如果抛出异常,执行的顺序是什么样的?
        • 思考2:切面的先后顺序(如果一个业务存在多个切面,如何排序?如何执行
        • 测试2:同数字或者都无Order注解的情况,是如何执行的
        • 测试2:不同优先级的切面先后执行顺序
      • 6、写法优化(切点优化pointcut)
      • 7、拓展用法,JointPoint
      • 8、全注解开发优化
          • 修改后的配置类
          • 测试类
      • 9、基于XML开发的AOP

Spring的AOP

一、 Spring对AOP的实现包括以下3种方式

  • 第一种方式: Spring框架结合AspectJ框架实现的AOP,基于注解方式。(需要重点掌握, 核心用法
  • 第二种方式: Spring框架结合AspectJ框架实现的AOP,基于XML方式第三种方式:
  • Spring框架自己实现的AOP,基于XML配置方式。(少用不做介绍)

什么是AspectJ?

什么是AspectJ?

(Edlipse组织的一个支持AOP的架。Aspect/架是独立于Spring架之外的一个框架,Spring框架用了AspectJ)

Aspedtu项目起源于洛阿你托 (Palo Ato 研究中心(写为PARC) ,该中由Xeox建团资,reor kczales导,以1997年开力于Aspec开发,1998年第-次发布外部用户,2001年发布1.0 release为了Aspe技术和发展,PAR(在200年3月将Aspec/项目给了dipse织,因为Aspec的发展和受关注度大大超出了PARC的预期,他们已经力继维持它的发展。

二、使用Spring的AOP

1、准备工作

要使用Spring的AOP,需要导入以下依赖

  • spring context 核心依赖 包含 spring-aop
  • spring-aspects 依赖

【学习笔记】SpringAOP的用法全解

并在resource目录下新建配置文件

【学习笔记】SpringAOP的用法全解

2、尝试写一个简单的AOP demo

思路整理

  1. 写一个业务类模拟实际业务

  2. 定义一个切面类 (切面=通知+切点),需要添加 @Aspect 直接,告诉spring这是一个切面类

  3. 将上述的两个类,纳入Spring容器中 (注解 + 添加扫描配置信息)

  4. 在切面类中定义具体的通知

    • 通知就是增强,就是具体要编写的增强代码
    • 这里通知Advice以方法的形式出现,因为方法中可以写代码
  5. 在该方法上加入 @Before 注解,表示这是一个 前置通知

    • 这个注解注解写 ——> 切点表达式
  6. 在spring配置中,开启aspecj的自动代理 <aop:aspectj-autoproxy />

    • 开启自动dialing后,Spring容器会自动扫描该类上是否带有@Aspect注解,如果有,则生成代理对象。
    • 这里标签可以添加一个属性proxy-target-class
      • 为true时表示强制使用CGLIB
      • 反之,则根据接口情况使用JDK动态代理或者CGLIB
  7. 测试

3、代码如下:

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!--组件扫描--><context:component-scan base-package="com.zhc.aop"/><!--开启aspectj的自动代理--><aop:aspectj-autoproxy  proxy-target-class="true"/></beans>

业务类

package com.zhc.aop.service;import org.springframework.stereotype.Service;@Service("userService")
public class UserService {public void login(){System.out.println("系统正在登陆。。。");}
}

切面类

package com.zhc.aop.aspects;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Component("logAspect")
@Aspect     // 声明这是一个切面
public class LogAspect {@Before("execution(* com.zhc.aop.service..*(..))")public void beforePoint(){System.out.println("前置通知");}
}

测试类

import com.zhc.aop.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Client {@Testpublic void TestProxy(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");UserService userService = applicationContext.getBean("userService", UserService.class);userService.login();}
}

测试结果

【学习笔记】SpringAOP的用法全解

4、复习切面表达式

切面表达式在这章中比较重要,迅速复习一遍即可,要记得几个常用的表达式

【学习笔记】SpringAOP的用法全解

1)所有方法

权限修饰符略,异常略,全限定名略

execution(* *(..))

2)指定路径下某个包及其子包的所有方法

excution(* com.zhc..*(..))

3)限定public的,某个包及其子包的,带login开头的,且参数是Stirng 的 方法

excution(public * com.zhc..login*(String))

5、5种不同的通知如何写?

  • 前置 —— @Before
  • 后置—— @AfterReturning
  • 环绕——@Around
  • 异常——@AfterThrowing
  • 最终——@After

注意 ,后置通知的注解并不是@After哦,别搞混 了。

写法其实差不太多,需要注意是的环绕通知的写法。

【学习笔记】SpringAOP的用法全解

思考1,5种通知都存在时,执行的顺序是什么样的?如果抛出异常,执行的顺序是什么样的?

引用老杜的案例:无异常的执行顺序如下

【学习笔记】SpringAOP的用法全解

一旦捕获异常,后置通知和后环绕就被抛弃了!

  • 后环绕也有特殊之处——如果是try…catch内捕获的话,后环绕也会执行

【学习笔记】SpringAOP的用法全解

思考2:切面的先后顺序(如果一个业务存在多个切面,如何排序?如何执行

在实际业务中,往往存在多个切面,如何做好排序呢?

其实Spring的开发者已经想到了这种情况,解决方法也很简单——

在切面类上加入注解@Order(int num),Order注解的数字越小,优先级越高!

  • 这里老杜没有提到的细节,比如同数字或者都无Order注解的情况,以及不同优先级的切面先后执行顺序是怎么样的?(比如环绕通知的先后执行顺序是什么样的?

测试2:同数字或者都无Order注解的情况,是如何执行的

结论:不是随机的,而是按照某个顺序进行执行,规律不明但是固定

测试2:不同优先级的切面先后执行顺序

测试设置 log的order为0,auth的order为1,测试结果如下:

结论:不同切面的执行顺序的:

  • 优先级高的前置/前环绕通知 会先执行
  • 优先级高的后置/后环绕通知 会后执行

【学习笔记】SpringAOP的用法全解

6、写法优化(切点优化pointcut)

写的切面多了,发现切面表达式重复写了很多,这里老杜介绍了 如何优化这个问题,具体实现如下

  • 在切面类中定义一个方法(任意名称)
  • 在该方法上加入@Pointcut注解,并在注解内写入 切面表达式
  • 在其他的切面方法上使用注解,就可以直接传入该方法名即可

这个简化的写法也可以跨类使用,只需要写上全限定类名(包名+类名)即可

优化后的切面类

package com.zhc.aop.aspects;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("logAspect")
@Aspect     // 声明这是一个切面
public class LogAspect {// 优化写法@Pointcut("execution(* com.zhc.aop.service.UserService.*(..))")public void pointcut(){}@Before("pointcut()")   // 前置通知public void beforePoint(JoinPoint joinPoint){System.out.println("前置通知");
//        System.out.println(joinPoint);  // 打印结果:execution(void com.zhc.aop.service.UserService.loginA())}@AfterReturning("pointcut()")public void afterPoint(){System.out.println("后置通知");}@Around("pointcut()")public void aroundPoint(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("环绕前。。。");joinPoint.proceed();System.out.println("环绕后。。。");}@AfterThrowing("pointcut()")public void throwPoint(){System.out.println("异常通知");}@After("pointcut()")public void finallyPoint(){System.out.println("最终通知");}
}

7、拓展用法,JointPoint

前面的环绕通知有用到了一个 ProcessJoinPoint ,其实在编写其他类型的通知类时,也可以通过JoinPoint来获取目标方法

  • 比如直接尝试打印该joinpoint对象——可以看出来打印的结果就是一个切面表达式:

    打印结果:execution(void com.zhc.aop.service.UserService.loginA())
    

8、全注解开发优化

可以优化掉配置文件spring.xml

声明一个配置类,配置思路如下

  1. 声明一个配置类 加上@Configuration
  2. 声明组件扫描
  3. 声明aspectj的自动扫描
  4. 修改对应的测试/调用代码
修改后的配置类
package com.zhc.aop.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@ComponentScan("com.zhc.aop")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}
测试类
@Test
public void TestProxyByAnno(){ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);UserService userService = applicationContext.getBean("userService", UserService.class);userService.loginA();}

9、基于XML开发的AOP

略,作为了解。主要是就是xml文件的书写和阅读,截图老杜写好的xml文件

【学习笔记】SpringAOP的用法全解

依然推荐大家去看动力节点的视频,更有助于理解