Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >面向切面的Spring

面向切面的Spring

作者头像
happyJared
发布于 2018-09-20 02:11:49
发布于 2018-09-20 02:11:49
44801
代码可运行
举报
文章被收录于专栏:happyJaredhappyJared
运行总次数:1
代码可运行

写在前面

  本文是博主在看完面向切面的Spring(《Spring实战》第4章)后的一些实践笔记。   为什么要用AOP呢?作者在书中也明确提到了,使用AOP,可以让代码逻辑更多的去关注自己本身的业务,而不用混杂和关注一些其它的东西。包括:安全,缓存,事务,日志等等。

名词概念

  • 通知(Advice)

  定义了切面做什么和什么时候去做。简单点来说,就是AOP执行时会调用的方法,通知除了定义切面要完成的工作(What),还会定位什么时候(When)去履行这项工作,是在方法调用前,还是调用之后,还是前后都是,还是抛出异常时

在切面定义中,一共有以下五种通知类型

类型

作用

Before

某方法调用之前发出通知

After

某方法完成之后发出通知,不考虑方法运行的结果

AfterReturning

将通知放置在被通知的方法成功执行之后

AfterThrowing

将通知放置在被通知的方法抛出异常之后

Around

通知包裹在被通知的方法的周围,在方法调用之前和之后发出(环绕通知 = 前置 + 目标方法执行 + 后置通知)

  • 切点,也叫切入点(Pointcut)

  上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有十几个连接点了对吧,但是你并不想在所有方法附件都使用通知(使用叫织入,下面再说),你只是想让其中几个,在调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法

  • 连接点,也叫参加点(JoinPoint)

  连接点是切面在应用程序执行过程中插入的地方,可能是方法调用(前、后)的时候,也可能是异常抛出的时候。连接点如果可以说是切点的全集,那么切点就是连接点的子集

  • 切面(Aspect)

  切面其实就是通知和切点的结合。通知说明了干什么和什么时候干(通过方法上使用@Before、@After等就能知道),则切点说明了在哪干(指定到底是哪个方法),这就组成了一个完整的切面定义

Spring对AOP的支持

  • Spring建议在Java中编写AOP,虽然用XML也可以实现
  • Spring通过使用代理类,在运行阶段将切面编织进bean中
  • Spring只支持方法级别的连接点,不像AspectJ还可以通过构造器或属性注入

切点表达式

  切点表达式算是一些比较概念性的知识,下面截了两个图供大家参考参考

切点表达式1

切点表达式2

  看得头晕了吧,不过好在只有execution()是用来执行匹配的,剩下的都是为了限制或定制连接点要匹配的位置   以下是execution()定义的格式(其中,带?号的为可选,否则必须给出) :

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) 

  还是举个真实栗子模仿一下吧

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execution(* com.example.aspectj.UserDao.updateName(..))
  • execution:用于定义什么方法执行时会被触发,这里是指com.example.aspectj包下的UserDao接口中的updateName方法执行时触发
  • * :忽略方法返回值类型
  • (..) :匹配任意参数

实战测试(SpringBoot + JPA)

  1. Create Entity
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Table(name = "tb_user")
@Entity
@Data
public class User {

    @Id
    @GeneratedValue
    private Integer id;

    private String name;

}
  1. Create Dao
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface UserDao extends JpaRepository<User, Integer> {

    @Modifying
    @Transactional
    @Query("update User u set u.name = ?1 where u.id = ?2")
    int updateName(String name, int id);

}
  1. Create Service
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class UserService {

    @Resource
    private UserDao userDao;

    @Transactional
    public void save(User user) {
        userDao.save(user);
    }

    public void update(String name, int id) {
        userDao.updateName(name, id);
    }

}

第一种风格的切面

  1. Create Aspect(使用了@Before、@After、@AfterReturning和@AfterThrowing这四个注解)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Aspect
public class UserAspectjOne {

    @Resource
    private UserService userService;

    @Before("execution(* com.example.aspectj.UserDao.updateName(..))")
    public void before() {
        System.out.println("1.------------before()");
    }

    @After("execution(* com.example.aspectj.UserDao.updateName(..))")
    public void after() {
        System.out.println("1.------------after()");
    }

    @AfterReturning("execution(* com.example.aspectj.UserDao.updateName(..))")
    public void afterReturning() {
        System.out.println("1.------------afterReturning()");
        User user = new User();
        user.setName("afterReturning1");
        userService.save(user);
    }

    @AfterThrowing("execution(* com.example.aspectj.UserDao.updateName(..))")
    public void afterThrowing() {
        System.out.println("1.------------afterThrowing()");
        User user = new User();
        user.setName("afterThrowing1");
        userService.save(user);
    }

}
  1. Create Configuration
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration
// @EnableAspectJAutoProxy //实测可以不添加该注解,因为SpringBoot中已经默认开启了AOP功能
public class AspectjConfiguration {

    @Bean
    public UserAspectjOne userAspectjOne() {
        return new UserAspectjOne();
    }

}

SpringBoot已经默认开启了aop

  1. Test updateName() with UserAspectjOne

    @Test public void testAdd() { User user = new User(); user.setName("jared"); userDao.save(user); }

添加User

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- 6.2 测试正常执行updateName()
``` java
@Test
public void testUpdateName() {
    userService.update("jared qiu", 1);
}
```

- 6.3.1 打印结果 

![输出结果][5]

- 6.3.2 数据库结果

![数据库结果][6]

- 6.4 测试非正常执行updateName(),只需要把UserDao类中updateName()上的@Modifying或者@Transactional注解去掉即可
``` java
@Test
public void testUpdateName() {
    userService.update("error jared qiu", 1);
}
```

- 6.5.1 打印结果 

![输出结果][7]

- 6.5.2 数据库结果

![数据库结果][8]

第二种风格的切面

  1. Create Aspect(依旧使用了@Before、@After、@AfterReturning和@AfterThrowing这四个注解,但新增了@Pointcut注解,把切面的定义抽离了出来进行统一)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Aspect
public class UserAspectjTwo {

    @Resource
    private UserService userService;

    @Pointcut("execution(* com.example.aspectj.UserDao.updateName(..))")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void before() {
        System.out.println("2.------------before()");
    }

    @After("pointcut()")
    public void after() {
        System.out.println("2.------------after()");
    }

    @AfterReturning("pointcut()")
    public void afterReturning() {
        System.out.println("2.------------afterReturning()");
        User user = new User();
        user.setName("afterReturning2");
        userService.save(user);
    }

    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        System.out.println("2.------------afterThrowing()");
        User user = new User();
        user.setName("afterThrowing2");
        userService.save(user);
    }

}
  1. Create Configuration
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration
public class AspectjConfiguration {

    @Bean
    public UserAspectjTwo userAspectjTwo() {
        return new UserAspectjTwo();
    }

}
  1. Test updateName() with UserAspectjTwo
    • 6.1 先往数据库里添加一条数据

    @Test public void testAdd() { User user = new User(); user.setName("jared"); userDao.save(user); }

添加User

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- 6.2 测试正常执行updateName()
``` java
@Test
public void testUpdateName() {
    userService.update("jared qiu", 1);
}
```

- 6.3.1 打印结果 

![输出结果][10]

- 6.3.2 数据库结果

![数据库结果][11]

- 6.4 测试非正常执行updateName(),只需要把UserDao类中updateName()上的@Modifying或者@Transactional注解去掉即可
``` java
@Test
public void testUpdateName() {
    userService.update("error jared qiu", 1);
}
```

- 6.5.1 打印结果 

![输出结果][12]

- 6.5.2 数据库结果

![数据库结果][13]

第三种风格的切面

  1. Create Aspect(使用了@Around这个环绕注解)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Aspect
public class UserAspectjThree {

    @Resource
    private UserService userService;

    /**
     * 方法的返回值类型须与切面所在方法的返回值类型保持一致
     */
    @Around("execution(* com.example.aspectj.UserDao.updateName(..))")
    public int around(ProceedingJoinPoint joinPoint) {
        try {
            System.out.println("3.------------before()");
            System.out.println("3.------------after()");
            joinPoint.proceed();//用于启动目标方法执行(必须)
            System.out.println("3.------------afterReturning()");
            User user = new User();
            user.setName("afterReturning3");
            userService.save(user);
        } catch (Throwable e) {
            System.out.println("3.------------afterThrowing()");
            User user = new User();
            user.setName("afterThrowing3");
            userService.save(user);
        }
        return 1;
    }

}
  1. Create Configuration
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration
public class AspectjConfiguration {

    @Bean
    public UserAspectjThree userAspectjThree() {
        return new UserAspectjThree();
    }

}
  1. Test updateName() with UserAspectjThree
    • 6.1 先往数据库里添加一条数据

    @Test public void testAdd() { User user = new User(); user.setName("jared"); userDao.save(user); }

添加User

  • 6.2 测试正常执行updateName()

@Test public void testUpdateName() { userService.update("jared qiu", 1); }

  • 6.3.1 打印结果

输出结果

  • 6.3.2 数据库结果

数据库结果

  • 6.4 测试非正常执行updateName(),只需要把UserDao类中updateName()上的@Modifying或者@Transactional注解去掉即可

@Test public void testUpdateName() { userService.update("error jared qiu", 1); }

  • 6.5.1 打印结果

输出结果

  • 6.5.2 数据库结果

数据库结果

第四种风格的切面

  1. Create Aspect(依旧使用了@Around这个环绕注解,但加入了@Pointcut注解和传递了参数)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Aspect
public class UserAspectjFour {

    @Resource
    private UserService userService;

    @Pointcut("execution(* com.example.aspectj.UserDao.updateName(String,*)) && args(name,*)")
    public void pointcut(String name) {
    }

    @Around(value = "pointcut(name)", argNames = "joinPoint,name")
    public int around(ProceedingJoinPoint joinPoint, String name) {
        try {
            System.out.println("4.------------before()");
            System.out.println("4.------------after()");
            Object proceed = joinPoint.proceed();
            System.out.println(proceed);
            System.out.println("4.------------afterReturning()");
            User user = new User();
            user.setName("afterReturning4" + name);
            userService.save(user);
        } catch (Throwable e) {
            System.out.println("4.------------afterThrowing()");
            User user = new User();
            user.setName("afterThrowing4" + name);
            userService.save(user);
        }
        return 1;
    }

}
  1. Create Configuration
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration
public class AspectjConfiguration {

    @Bean
    public UserAspectjFour userAspectjFour() {
        return new UserAspectjFour();
    }

}
  1. Test updateName() with UserAspectjFour
    • 6.1 先往数据库里添加一条数据

    @Test public void testAdd() { User user = new User(); user.setName("jared"); userDao.save(user); }

添加User

  • 6.2 测试正常执行updateName()

@Test public void testUpdateName() { userService.update("jared qiu", 1); }

  • 6.3.1 打印结果

输出结果

  • 6.3.2 数据库结果

数据库结果

  • 6.4 测试非正常执行updateName(),只需要把UserDao类中updateName()上的@Modifying或者@Transactional注解去掉即可

@Test public void testUpdateName() { userService.update("error jared qiu", 1); }

  • 6.5.1 打印结果

输出结果

  • 6.5.2 数据库结果

数据库结果

扩展@EnableAspectJAutoProxy

  • 表示开启AOP代理自动配置,如果配@EnableAspectJAutoProxy表示使用cglib进行代理对象的生成;设置@EnableAspectJAutoProxy(exposeProxy=true)表示通过aop框架暴露该代理对象,使得aopContext能够直接访问
  • 从@EnableAspectJAutoProxy的定义可以看出,它引入AspectJAutoProxyRegister.class对象,该对象是基于注解@EnableAspectJAutoProxy注册了一个AnnotationAwareAspectJAutoProxyCreator,通过调用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry),注册了一个aop代理对象生成器

@EnableAspectJAutoProxy

AspectJAutoProxyRegistrar

参考链接

AspectJ Spring AOP系列 Spring AOP中JoinPoint的表达式定义描述

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.03.25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring
Spring 是一个开源的轻量级 Java 开发框架,旨在简化企业级应用开发。它通过控制反转(IoC)和面向切面编程(AOP)实现组件解耦和横切逻辑分离,提升系统的可维护性与扩展性。Spring 提供统一的事务管理、灵活的 Web 开发框架(Spring MVC),并支持与多种第三方技术集成。其生态系统包括 Spring Boot(快速构建应用)、Spring Cloud(微服务架构支持)等,已成为现代 Java 开发的核心技术之一
用户11637817
2025/04/30
1510
Spring AOP面向切面编程:实战篇
无论在学习或者面试的时候,大家都会张口说spring的特性AOP和IOC,有些大神理解的很到位,但是对于大多数初中级工程师来讲还是模糊阶段,可能有时候用到了也不知道,下面我就来举一个租房的例子。
Cheng_Blog
2022/02/25
5310
Spring AOP面向切面编程:实战篇
Spring学习笔记(三) --- Spring AOP
可以在不修改源码的情况下对程序进行增强,AOP可以进行权限校验,日志记录,性能监控,事务控制.
挽风
2021/04/13
3160
Spring学习笔记(三) --- Spring AOP
《Spring实战》读书笔记-第4章 面向切面的Spring
在软件开发中,散布于应用中多的功能被称为横切关注点(cross-cutting concern)。通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的(但往往会直接嵌入到应用的业务逻辑之中)。把这些横切关注点与业务相分离正是面向切面编程(AOP)所要解决的问题。
Java架构师必看
2020/04/10
6030
Spring AOP面向切面编程
spring提供了一种可插拔的组件技术。听起来很高大上,但在我们日常生活中经常遇到这样的场景,比如说我们现在开发了两个软件模块,A和B,假设软件模块A是系统的用户管理模块,而软件模块B是系统的员工管理模块。这两个模块都拥有自己的业务处理类,他们执行的过程也是以上到下依次执行的。现在我对这两个模块提出一个要求,这两个模块从上到下进行业务处理的过程中,我希望都要进行权限过滤,只有拥有权限的用户才可以访问对应的模块。你可能会在运行实际代码前去增加相应的权限判断的业务代码,A模块加一个,B模块加一个,这样做固然没问题。但是有一天,项目经理说我们现在不需要这两块功能了,那该怎么办呢?此时你又该打开它对应的代码,把所有的权限控制代码全都去掉。那在这时候,有没有更好的办法呢?答案是肯定的。Spring AOP面向切面编程就可以很好地解决这个问题。
害恶细君
2022/11/22
5900
Spring AOP面向切面编程
03-Spring5 AOP
第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分
彼岸舞
2021/12/14
3470
03-Spring5 AOP
Spring基础知识之基于注解的AOP
本文介绍了如何使用注解和AspectJ为Spring Boot应用添加AOP功能,并使用@AspectJ注解和AspectJ语法实现环绕通知、获取方法的参数和返回值以及访问被代理类的成员变量,最后介绍了如何通过XML配置文件或注解方式启用AspectJ自动代理功能。
用户1134788
2018/01/05
1.1K0
Spring基础知识之基于注解的AOP
学习Spring框架这一篇就够了
Spring是分层的 Java SE/EE应用 full-stack(全栈的) 轻量级开源框架,以 IOC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。
全栈程序员站长
2022/09/07
1.6K0
学习Spring框架这一篇就够了
Java开发Spring笔记第二天
今日内容 AOP的概述 AOP 的底层实现 Spring 的AOP 使用AspectJ 实现AOP Spring JdbcTemplate 使用 1.2 Spring整合WEB项目. 1.2.1 Sp
Java帮帮
2018/03/19
8750
Java开发Spring笔记第二天
Spring的AOP面向切面 知识点速查
通过实现BeanPostProcessor接口的postProcessBeforeInitialization和postProcessAfterInitialization方法,在初始化前后用代理对象加强原来的类
linxinzhe
2018/07/25
5510
面向切面的Spring
转眼间,快到夏天了,又让我想起来往年盛夏时,被空调、西瓜、冰淇淋支配的恐惧,南方的天气是真的热,在这种天气下,西瓜、冰淇淋可以没有,但是空调是必不可少的。但是空调的缺点是耗电,而电需要钱(这不废话吗)。为了享受凉爽和舒适,我们没有什么办法可以避免这种开销。这是因为每家每户都有一个电表来记录用电量,每个月都会有人来查电表(不是查水表就行),这样电力公司就知道应该收取多少费用了,用户也没办法赖账。
端碗吹水
2020/09/23
6860
面向切面的Spring
Spring核心之面向切面编程AOP
第三参数,实现这个接口InvocationHandler,创建代理对象,写增强的部分
用户9615083
2022/12/25
3810
Spring核心之面向切面编程AOP
【Spring进阶】基于注解的面向切面编程(AOP)详解
面向切面编程(AOP)是一种编程范式,它允许开发者将横切关注点(如日志记录、事务管理、安全性等)与业务逻辑分离,从而提高代码的模块化和可维护性。在Java中,AOP通常通过使用框架如Spring来实现。
王也518
2024/04/22
1.3K0
Spring实战4—面向切面编程主要内容
在南方没有暖气的冬天,太冷了,非常想念北方有暖气的冬天。为了取暖,很多朋友通过空调取暖,但是空调需要耗电,也就需要交不少电费。没家都会有一个电表,每隔一段时间都会有记录员来家里收取这段时间的电费。
阿杜
2018/08/06
1K0
Spring实战4—面向切面编程主要内容
再学习之Spring(面向切面编程).
一、概念 1、理论     把横切关注点和业务逻辑相分离是面向切面编程所要解决的问题。如果要重用通用功能的话,最常见的面向对象技术是继承(inheritance)或 组成(delegation)。但是,如果在整个应用中都使用相同的基类,继承往往会导致一个脆弱的对象体系;而使用组成可能需要对委托对象进行复杂的调用。切面提供了取代继承和委托的另一种可选方案,而且在很多场景下更清晰简洁。Spring AOP 基于动态代理,所以Spring只支持方法连接点,这与一些其他的AOP框架是不同的,例如AspectJ和JB
JMCui
2018/03/16
7710
再学习之Spring(面向切面编程).
spring aop聊点不一样的东西
你有几年没回老家了? 我有三年。 今年怕是又回不去了,有些想家了。。。 你呢? 前言 前几篇文章本打算写spring aop的,但是强忍着没有写(旁白:也有可能是没想好该怎么写?),就是为了今天整个专
苏三说技术
2021/02/05
5390
JAVAEE框架整合技术之Spring02-AOP面向切面编程技术
@PropertySouce是spring3.1开始引入的基于java config的注解。
张哥编程
2024/12/13
1270
JAVAEE框架整合技术之Spring02-AOP面向切面编程技术
Spring(4)——面向切面编程(AOP模块)
Spring AOP 简介 如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用。 AOP 即 Aspect Oriented Program 面向切面编程 首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务 所谓的周边功能,比如性能统计,日志,事务管理等等 周边功能在 Spring 的面向切面编程AOP思想里,即被定义为切面 在面向切面编程AO
我没有三颗心脏
2018/04/26
6800
Spring(4)——面向切面编程(AOP模块)
SpringAOP-什么是面向切面编程?
前言:相信你知道什么是面向对象编程(OOP),但是你了解面向切面编程(AOP)思想吗?AOP作为Spring框架中的一个重要特性,我们一起来打开AOP之门吧!
扎克蕉
2020/05/10
1.5K2
Spring 框架学习(六)面向切面编程 AOP
在软件开发中散布于应用中多处的功能被称为横切关注点(crossing-cutting concern)。通常这也横切关注点一般是与业务逻辑相分离的。而面向切面编程将会解决如何将横切关注点与与业务逻辑分离的问题。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nRIEcgxi-1571404087194)(en-resource://database/9500:1)] 横切关注点往往是影响应用多处的功能
求和小熊猫
2020/11/25
3590
相关推荐
Spring
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验