• 【Spring篇】AOP案例


    🍓系列专栏:Spring系列专栏

    🍉个人主页:个人主页

    一、案例:业务层接口执行效率

    1.需求分析

    这个需求也比较简单,前面我们在介绍 AOP 的时候已经演示过 :
    需求 : 任意业务层接口执行均可显示其执行效率(执行时长)
    这个案例的目的是查看每个业务层执行的时间,这样就可以监控出哪个业务比较耗时,将其查找出来方便优化。
    具体实现的思路 :
    (1) 开始执行方法之前记录一个时间
    (2) 执行方法
    (3) 执行完方法之后记录一个时间
    (4) 用后一个时间减去前一个时间的差值,就是我们需要的结果。
    所以要在方法执行的前后添加业务,经过分析我们将采用 环绕通知
    说明 : 原始方法如果只执行一次,时间太快,两个时间差可能为 0 ,所以我们要执行万次来计算时间

    2.环境准备

    • 创建一个Maven项目
    • pom.xml添加Spring依赖
    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    4. <modelVersion>4.0.0modelVersion>
    5. <groupId>com.itheimagroupId>
    6. <artifactId>spring_21_case_interface_run_speedartifactId>
    7. <version>1.0-SNAPSHOTversion>
    8. <dependencies>
    9. <dependency>
    10. <groupId>org.springframeworkgroupId>
    11. <artifactId>spring-contextartifactId>
    12. <version>5.2.10.RELEASEversion>
    13. dependency>
    14. <dependency>
    15. <groupId>org.springframeworkgroupId>
    16. <artifactId>spring-jdbcartifactId>
    17. <version>5.2.10.RELEASEversion>
    18. dependency>
    19. <dependency>
    20. <groupId>org.springframeworkgroupId>
    21. <artifactId>spring-testartifactId>
    22. <version>5.2.10.RELEASEversion>
    23. dependency>
    24. <dependency>
    25. <groupId>org.aspectjgroupId>
    26. <artifactId>aspectjweaverartifactId>
    27. <version>1.9.4version>
    28. dependency>
    29. <dependency>
    30. <groupId>mysqlgroupId>
    31. <artifactId>mysql-connector-javaartifactId>
    32. <version>5.1.47version>
    33. dependency>
    34. <dependency>
    35. <groupId>com.alibabagroupId>
    36. <artifactId>druidartifactId>
    37. <version>1.1.16version>
    38. dependency>
    39. <dependency>
    40. <groupId>org.mybatisgroupId>
    41. <artifactId>mybatisartifactId>
    42. <version>3.5.6version>
    43. dependency>
    44. <dependency>
    45. <groupId>org.mybatisgroupId>
    46. <artifactId>mybatis-springartifactId>
    47. <version>1.3.0version>
    48. dependency>
    49. <dependency>
    50. <groupId>junitgroupId>
    51. <artifactId>junitartifactId>
    52. <version>4.12version>
    53. <scope>testscope>
    54. dependency>
    55. dependencies>
    56. project>
    添加 AccountService AccountServiceImpl AccountDao Account
    1. public interface AccountService {
    2. void save(Account account);
    3. void delete(Integer id);
    4. void update(Account account);
    5. List findAll();
    6. Account findById(Integer id);
    7. }
    1. @Service
    2. public class AccountServiceImpl implements AccountService {
    3. @Autowired
    4. private AccountDao accountDao;
    5. public void save(Account account) {
    6. accountDao.save(account);
    7. }
    8. public void update(Account account){
    9. accountDao.update(account);
    10. }
    11. public void delete(Integer id) {
    12. accountDao.delete(id);
    13. }
    14. public Account findById(Integer id) {
    15. return accountDao.findById(id);
    16. }
    17. public List findAll() {
    18. return accountDao.findAll();
    19. }
    20. }
    1. public interface AccountDao {
    2. @Insert("insert into tbl_account(name,money)values(#{name},#{money})")
    3. void save(Account account);
    4. @Delete("delete from tbl_account where id = #{id} ")
    5. void delete(Integer id);
    6. @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")
    7. void update(Account account);
    8. @Select("select * from tbl_account")
    9. List findAll();
    10. @Select("select * from tbl_account where id = #{id} ")
    11. Account findById(Integer id);
    12. }
    1. import java.io.Serializable;
    2. public class Account implements Serializable {
    3. private Integer id;
    4. private String name;
    5. private Double money;
    6. public Integer getId() {
    7. return id;
    8. }
    9. public void setId(Integer id) {
    10. this.id = id;
    11. }
    12. public String getName() {
    13. return name;
    14. }
    15. public void setName(String name) {
    16. this.name = name;
    17. }
    18. public Double getMoney() {
    19. return money;
    20. }
    21. public void setMoney(Double money) {
    22. this.money = money;
    23. }
    24. @Override
    25. public String toString() {
    26. return "Account{" +
    27. "id=" + id +
    28. ", name='" + name + '\'' +
    29. ", money=" + money +
    30. '}';
    31. }
    32. }
    resources 下提供一个 jdbc.properties
    1. jdbc.driver=com.mysql.jdbc.Driver
    2. jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
    3. jdbc.username=root
    4. jdbc.password=root
    创建相关配置类
    1. @Configuration
    2. @ComponentScan("com.itheima")
    3. @PropertySource("classpath:jdbc.properties")
    4. @Import({JdbcConfig.class,MybatisConfig.class})
    5. public class SpringConfig {
    6. }
    1. public class JdbcConfig {
    2. @Value("${jdbc.driver}")
    3. private String driver;
    4. @Value("${jdbc.url}")
    5. private String url;
    6. @Value("${jdbc.username}")
    7. private String userName;
    8. @Value("${jdbc.password}")
    9. private String password;
    10. @Bean
    11. public DataSource dataSource(){
    12. DruidDataSource ds = new DruidDataSource();
    13. ds.setDriverClassName(driver);
    14. ds.setUrl(url);
    15. ds.setUsername(userName);
    16. ds.setPassword(password);
    17. return ds;
    18. }
    19. }
    1. public class MybatisConfig {
    2. @Bean
    3. public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
    4. SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
    5. ssfb.setTypeAliasesPackage("com.itheima.domain");
    6. ssfb.setDataSource(dataSource);
    7. return ssfb;
    8. }
    9. @Bean
    10. public MapperScannerConfigurer mapperScannerConfigurer(){
    11. MapperScannerConfigurer msc = new MapperScannerConfigurer();
    12. msc.setBasePackage("com.itheima.dao");
    13. return msc;
    14. }
    15. }
    编写Spring整合Junit的测试类
    1. @RunWith(SpringJUnit4ClassRunner.class)
    2. @ContextConfiguration(classes = SpringConfig.class)
    3. public class AccountServiceTestCase {
    4. @Autowired
    5. private AccountService accountService;
    6. @Test
    7. public void testFindById(){
    8. Account ac = accountService.findById(2);
    9. }
    10. @Test
    11. public void testFindAll(){
    12. List all = accountService.findAll();
    13. }
    14. }
    最终创建好的项目结构如下 :

    3.功能开发

    步骤 1: 开启 SpringAOP 的注解功能
    Spring 的主配置文件 SpringConfig 类中添加注解
    @EnableAspectJAutoProxy
    步骤 2: 创建 AOP 的通知类
    该类要被 Spring 管理,需要添加 @Component
    要标识该类是一个 AOP 的切面类,需要添加 @Aspect
    配置切入点表达式,需要添加一个方法,并添加 @Pointcut
    1. @Component
    2. @Aspect
    3. public class ProjectAdvice {
    4. //匹配业务层的所有方法
    5. @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    6. private void servicePt(){}
    7. }
    步骤 3: 添加环绕通知
    runSpeed() 方法上添加 @Around, 完成核心业务,记录万次执行的时间
    1. @Component
    2. @Aspect
    3. public class ProjectAdvice {
    4. //匹配业务层的所有方法
    5. @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    6. private void servicePt(){}
    7. //设置环绕通知,在原始操作的运行前后记录执行时间
    8. @Around("ProjectAdvice.servicePt()")
    9. public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {
    10. //获取执行的签名对象
    11. Signature signature = pjp.getSignature();
    12. String className = signature.getDeclaringTypeName();
    13. String methodName = signature.getName();
    14. long start = System.currentTimeMillis();
    15. for (int i = 0; i < 10000; i++) {
    16. pjp.proceed();
    17. }
    18. long end = System.currentTimeMillis();
    19. System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");
    20. }
    21. }
    步骤4 : 运行单元测试类

     

    补充说明
    当前测试的接口执行效率仅仅是一个理论值,并不是一次完整的执行过程。
    这块只是通过该案例把 AOP 的使用进行了学习,具体的实际值是有很多因素共同决定的。

    二、AOP通知获取数据

    目前我们写 AOP 仅仅是在原始方法前后追加一些操作,接下来我们要说说 AOP 中数据相关的内容,我们将从获取参数 获取返回值 获取异常 三个方面来研究切入点的相关信息。
    前面我们介绍通知类型的时候总共讲了五种,那么对于这五种类型都会有参数,返回值和异常吗 ?
    我们先来一个个分析下 :
    获取切入点方法的参数,所有的通知类型都可以获取参数
    • JoinPoint:适用于前置、后置、返回后、抛出异常后通知
    • ProceedingJoinPoint:适用于环绕通知
    获取切入点方法返回值,前置和抛出异常后通知是没有返回值,后置通知可有可无,所以不做研究
    • 返回后通知
    • 环绕通知
    获取切入点方法运行异常信息,前置和返回后通知是不会有,后置通知可有可无,所以不做研究
    • 抛出异常后通知
    • 环绕通知

    1.环境准备

    • 创建一个Maven项目
    • pom.xml添加Spring依赖
    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframeworkgroupId>
    4. <artifactId>spring-contextartifactId>
    5. <version>5.2.10.RELEASEversion>
    6. dependency>
    7. <dependency>
    8. <groupId>org.aspectjgroupId>
    9. <artifactId>aspectjweaverartifactId>
    10. <version>1.9.4version>
    11. dependency>
    12. dependencies>
    添加 BookDao BookDaoImpl
    1. public interface BookDao {
    2. public String findName(int id);
    3. }
    4. @Repository
    5. public class BookDaoImpl implements BookDao {
    6. public String findName(int id) {
    7. System.out.println("id:"+id);
    8. return "itcast";
    9. }
    10. }
    创建 Spring 的配置类
    1. @Configuration
    2. @ComponentScan("com.itheima")
    3. public class SpringConfig {
    4. }
    编写通知类
    1. @Component
    2. @Aspect
    3. public class MyAdvice {
    4. @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
    5. private void pt(){}
    6. @Before("pt()")
    7. public void before() {
    8. System.out.println("before advice ..." );
    9. }
    10. @After("pt()")
    11. public void after() {
    12. System.out.println("after advice ...");
    13. }
    14. @Around("pt()")
    15. public Object around() throws Throwable{
    16. Object ret = pjp.proceed();
    17. return ret;
    18. }
    19. @AfterReturning("pt()")
    20. public void afterReturning() {
    21. System.out.println("afterReturning advice ...");
    22. }
    23. @AfterThrowing("pt()")
    24. public void afterThrowing() {
    25. System.out.println("afterThrowing advice ...");
    26. }
    27. }
    编写 App 运行类
    1. public class App {
    2. public static void main(String[] args) {
    3. ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    4. BookDao bookDao = ctx.getBean(BookDao.class);
    5. String name = bookDao.findName(100);
    6. System.out.println(name);
    7. }
    8. }
    最终创建好的项目结构如下 :

    2.获取参数 

    非环绕通知获取方式
    在方法上添加 JoinPoint, 通过 JoinPoint 来获取参数
    1. @Component
    2. @Aspect
    3. public class MyAdvice {
    4. @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
    5. private void pt(){}
    6. @Before("pt()")
    7. public void before(JoinPoint jp)
    8. Object[] args = jp.getArgs();
    9. System.out.println(Arrays.toString(args));
    10. System.out.println("before advice ..." );
    11. }
    12. //...其他的略
    13. }
    运行 App 类,可以获取如下内容,说明参数 100 已经被获取

    思考 : 方法的参数只有一个,为什么获取的是一个数组 ?
    因为参数的个数是不固定的,所以使用数组更通配些。
    如果将参数改成两个会是什么效果呢 ?
    (1) 修改 BookDao 接口和 BookDaoImpl 实现类
    1. public interface BookDao {
    2. public String findName(int id,String password);
    3. }
    4. @Repository
    5. public class BookDaoImpl implements BookDao {
    6. public String findName(int id,String password) {
    7. System.out.println("id:"+id);
    8. return "itcast";
    9. }
    10. }
    (2) 修改 App 类,调用方法传入多个参数
    1. public class App {
    2. public static void main(String[] args) {
    3. ApplicationContext ctx = new
    4. AnnotationConfigApplicationContext(SpringConfig.class);
    5. BookDao bookDao = ctx.getBean(BookDao.class);
    6. String name = bookDao.findName(100,"itheima");
    7. System.out.println(name);
    8. }
    9. }
    (3) 运行 App ,查看结果 , 说明两个参数都已经被获取到

    说明 :
    使用 JoinPoint 的方式获取参数适用于 前置 后置 返回后 抛出异常后通知。
    环绕通知获取方式
    环绕通知使用的是 ProceedingJoinPoint ,因为 ProceedingJoinPoint JoinPoint 类的子
    类,所以对于 ProceedingJoinPoint 类中应该也会有对应的 getArgs() 方法,我们去验证下 :
    1. @Component
    2. @Aspect
    3. public class MyAdvice {
    4. @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
    5. private void pt(){}
    6. @Around("pt()")
    7. public Object around(ProceedingJoinPoint pjp)throws Throwable {
    8. Object[] args = pjp.getArgs();
    9. System.out.println(Arrays.toString(args));
    10. Object ret = pjp.proceed();
    11. return ret;
    12. }
    13. //其他的略
    14. }
    运行 App 后查看运行结果,说明 ProceedingJoinPoint 也是可以通过 getArgs() 获取参数

     

    注意 :
    pjp.proceed() 方法是有两个构造方法,分别是 :

    •  调用无参数的proceed,当原始方法有参数,会在调用的过程中自动传入参数
    • 所以调用这两个方法的任意一个都可以完成功能
    • 但是当需要修改原始方法的参数时,就只能采用带有参数的方法,如下:
    1. @Component
    2. @Aspect
    3. public class MyAdvice {
    4. @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
    5. private void pt(){}
    6. @Around("pt()")
    7. public Object around(ProceedingJoinPoint pjp) throws Throwable{
    8. Object[] args = pjp.getArgs();
    9. System.out.println(Arrays.toString(args));
    10. args[0] = 666;
    11. Object ret = pjp.proceed(args);
    12. return ret;
    13. }
    14. //其他的略
    15. }
    有了这个特性后,我们就可以在环绕通知中对原始方法的参数进行拦截过滤,避免由于参数的
    问题导致程序无法正确运行,保证代码的健壮性。

    3.获取返回值

    对于返回值,只有返回后 AfterReturing 和环绕 Around 这两个通知类型可以获取,具体如何获取 ?
    环绕通知获取返回值
    1. @Component
    2. @Aspect
    3. public class MyAdvice {
    4. @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
    5. private void pt(){}
    6. @Around("pt()")
    7. public Object around(ProceedingJoinPoint pjp) throws Throwable{
    8. Object[] args = pjp.getArgs();
    9. System.out.println(Arrays.toString(args));
    10. args[0] = 666;
    11. Object ret = pjp.proceed(args);
    12. return ret;
    13. }
    14. //其他的略
    15. }
    上述代码中, ret 就是方法的返回值,我们是可以直接获取,不但可以获取,如果需要还可以进行修改。
    返回后通知获取返回值
    1. @Component
    2. @Aspect
    3. public class MyAdvice {
    4. @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
    5. private void pt(){}
    6. @AfterReturning(value = "pt()",returning = "ret")
    7. public void afterReturning(Object ret) {
    8. System.out.println("afterReturning advice ..."+ret);
    9. }
    10. //其他的略
    11. }
    注意 :
    (1) 参数名的问题

    (2)afterReturning 方法参数类型的问题
    参数类型可以写成 String ,但是为了能匹配更多的参数类型,建议写成 Object 类型
    (3)afterReturning 方法参数的顺序问题

    运行App后查看运行结果,说明返回值已经被获取到

    4.获取异常 

    对于获取抛出的异常,只有抛出异常后 AfterThrowing 和环绕 Around 这两个通知类型可以获取,具
    体如何获取 ?
    环绕通知获取异常
    这块比较简单,以前我们是抛出异常,现在只需要将异常捕获,就可以获取到原始方法的异常信息了
    1. @Component
    2. @Aspect
    3. public class MyAdvice {
    4. @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
    5. private void pt(){}
    6. @Around("pt()")
    7. public Object around(ProceedingJoinPoint pjp){
    8. Object[] args = pjp.getArgs();
    9. System.out.println(Arrays.toString(args));
    10. args[0] = 666;
    11. Object ret = null;
    12. try{
    13. ret = pjp.proceed(args);
    14. }catch(Throwable throwable){
    15. t.printStackTrace();
    16. }
    17. return ret;
    18. }
    19. //其他的略
    20. }
    catch 方法中就可以获取到异常,至于获取到异常以后该如何处理,这个就和你的业务需求有关
    了。
    抛出异常后通知获取异常
    1. @Component
    2. @Aspect
    3. public class MyAdvice {
    4. @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
    5. private void pt(){}
    6. @AfterThrowing(value = "pt()",throwing = "t")
    7. public void afterThrowing(Throwable t) {
    8. System.out.println("afterThrowing advice ..."+t);
    9. }
    10. //其他的略
    11. }
    如何让原始方法抛出异常,方式有很多,
    1. @Repository
    2. public class BookDaoImpl implements BookDao {
    3. public String findName(int id,String password) {
    4. System.out.println("id:"+id);
    5. if(true){
    6. throw new NullPointerException();
    7. }
    8. return "itcast";
    9. }
    10. }
    注意 :

    运行App后,查看控制台,就能看的异常信息被打印到控制台 

    至此,AOP通知如何获取数据就已经讲解完了,数据中包含参数返回值异常(了解) 

    三、百度网盘密码数据兼容处理

    1.需求分析

    需求: 对百度网盘分享链接输入密码时尾部多输入的空格做兼容处理。

    问题描述 :
    点击链接,会提示,请输入提取码,如下图所示

    • 当我们从别人发给我们的内容中复制提取码的时候,有时候会多复制到一些空格,直接粘贴到百度的提取码输入框
    • 但是百度那边记录的提取码是没有空格的
    • 这个时候如果不做处理,直接对比的话,就会引发提取码不一致,导致无法访问百度盘上的内容
    • 所以多输入一个空格可能会导致项目的功能无法正常使用。
    此时我们就想能不能将输入的参数先帮用户去掉空格再操作呢 ?
    • 答案是可以的,我们只需要在业务方法执行之前对所有的输入参数进行格式处理——trim() 是对所有的参数都需要去除空格么?
    • 也没有必要,一般只需要针对字符串处理即可。

    • 以后涉及到需要去除前后空格的业务可能会有很多,这个去空格的代码是每个业务都写么?
    • 可以考虑使用AOP来统一处理。
    AOP 有五种通知类型,该使用哪种呢 ?
    我们的需求是将原始方法的参数处理后在参与原始方法的调用,能做这件事的就只有环绕通知。
    综上所述,我们需要考虑两件事 :
    ①:在业务方法执行之前对所有的输入参数进行格式处理 —— trim()

    ②:使用处理后的参数调用原始方法——环绕通知中存在对原始方法的调用

    2.具体实现

    添加 ResourcesService ResourcesServiceImpl,ResourcesDao ResourcesDaoImpl
    1. public interface ResourcesService {
    2. public boolean openURL(String url ,String password);
    3. }
    1. @Service
    2. public class ResourcesServiceImpl implements ResourcesService {
    3. @Autowired
    4. private ResourcesDao resourcesDao;
    5. public boolean openURL(String url, String password) {
    6. return resourcesDao.readResources(url,password);
    7. }
    8. }
    1. public interface ResourcesDao {
    2. boolean readResources(String url, String password);
    3. }
    1. @Repository
    2. public class ResourcesDaoImpl implements ResourcesDao {
    3. public boolean readResources(String url, String password) {
    4. System.out.println(password.length());
    5. //模拟校验
    6. return password.equals("root");
    7. }
    8. }
    创建 Spring 的配置类
    1. @Configuration
    2. @ComponentScan("com.itheima")
    3. @EnableAspectJAutoProxy
    4. public class SpringConfig {
    5. }
    AOP
    1. @Component
    2. @Aspect
    3. public class DataAdvice {
    4. @Pointcut("execution(boolean com.itheima.service.*Service.*(*,*))")
    5. private void servicePt(){}
    6. @Around("DataAdvice.servicePt()")
    7. public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {
    8. Object[] args = pjp.getArgs();
    9. for (int i = 0; i < args.length; i++) {
    10. //判断参数是不是字符串
    11. if(args[i].getClass().equals(String.class)){
    12. args[i] = args[i].toString().trim();
    13. }
    14. }
    15. Object ret = pjp.proceed(args);
    16. return ret;
    17. }
    18. }
    编写 App 运行类
    1. public class App {
    2. public static void main(String[] args) {
    3. ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    4. ResourcesService resourcesService = ctx.getBean(ResourcesService.class);
    5. boolean flag = resourcesService.openURL("http://pan.baidu.com/haha", "root ");
    6. System.out.println(flag);
    7. }
    8. }

    四、AOP总结

    AOP的知识就已经讲解完了,接下来对于AOP的知识进行一个总结:

    1.AOP的核心概念

    概念: AOP(Aspect Oriented Programming) 面向切面编程,一种编程范式
    作用:在不惊动原始设计的基础上为方法进行功能 增强
    核心概念
    • 代理(Proxy):SpringAOP的核心本质是采用代理模式实现的
    • 连接点(JoinPoint):在SpringAOP中,理解为任意方法的执行
    • 切入点(Pointcut):匹配连接点的式子,也是具有共性功能的方法描述
    • 通知(Advice):若干个方法的共性功能,在切入点处执行,最终体现为一个方法
    • 切面(Aspect):描述通知与切入点的对应关系
    • 目标对象(Target):被代理的原始对象成为目标对象

    2.切入点表达式

    切入点表达式标准格式:动作关键字 ( 访问修饰符 返回值 包名 . / 接口名 . 方法名(参数)异常
    )
     execution(* com.itheima.service.*Service.*(..))
    切入点表达式描述通配符:
    • 作用:用于快速描述,范围描述
    • *:匹配任意符号(常用)
    • .. :匹配多个连续的任意符号(常用)
    • +:匹配子类类型

    切入点表达式书写技巧

    1. 标准规范 开发
    2. 查询操作的返回值建议使用 * 匹配
    3. 减少使用 .. 的形式描述包
    4. 对接口 进行描述 ,使用 * 表示模块名,例如 UserService 的匹配描述为 *Service
    5. 方法名书写保留动词,例如get ,使用 * 表示名词,例如 getById 匹配描述为 getBy*
    6. 参数根据实际情况灵活调整

    3.五种通知类型

    • 前置通知
    • 后置通知
    • 环绕通知(重点)
    1. 环绕通知依赖形参ProceedingJoinPoint才能实现对原始方法的调用
    2. 环绕通知可以隔离原始方法的调用执行
    3. 环绕通知返回值设置为Object类型
    4. 环绕通知中可以对原始方法调用过程中出现的异常进行处理
    • 返回后通知
    • 抛出异常后通知

    4.通知中获取参数

    获取切入点方法的参数,所有的通知类型都可以获取参数
    • JoinPoint:适用于前置、后置、返回后、抛出异常后通知
    • ProceedingJoinPoint:适用于环绕通知
    获取切入点方法返回值,前置和抛出异常后通知是没有返回值,后置通知可有可无,所以不做研究
    • 返回后通知
    • 环绕通知
    获取切入点方法运行异常信息,前置和返回后通知是不会有,后置通知可有可无,所以不做研究
    • 抛出异常后通知
    • 环绕通知

    笔记来自: 黑马程序员SSM框架教程

  • 相关阅读:
    Go中的编程模式:Pipeline
    7 LayUI
    一文快速学会linux shell 编程基础!!!
    Java类的主动加载和被动加载
    很遗憾,没有一篇文章能讲清楚分布式事务
    VUE3 之 动画与过渡的实现 - 这个系列的教程通俗易懂,适合新手
    时序逻辑电路
    css3-定位
    体育场馆智能化系统满足了哪些需求?
    Java读取文件的N种方法
  • 原文地址:https://blog.csdn.net/Javascript_tsj/article/details/130254152