spring aop详解

1. 什么是AOP

AOP全称是aspect-oriented programing 面向切面编程。用于解决横向关注点的问题,横向关注点是指多个模块或者模块中的多个功能需要共享的功能,如日志记录、事务管理、安全控制等等。即重复性的代码抽象出来,形成可复用的代码模块。

AOP核心术语

1)连接点(join point):指程序执行的某个特定位置,也成为可切入点。这些方法都是连接点,可以切入。

在这里插入图片描述

如上图,几个方法就是连接点。

2)切点(pointcut):指实际被切入的那些方法,也就是连接点中,真正被切入的那些点。
在这里插入图片描述

如图,这六个方法就是“连接点”,但只有4个红色箭头的方法是“切入点”

3)通知/增强(advice)

指在切入点所执行的相关处理,例如上图中的“安全”、“日志”,就是advice。增强分为五大类,包括before、after、after returning、after throwing、around(方法执行前后)。

4)切面(aspect):由切点+增强组成。

5)织入(weaving):将增强添加到具体切入点的过程,在spring中就是动态代理的实现过程。

6)目标对象(target):指被增强的对象,也就是被切入的方法代码。

举例:数据库操作

在这里插入图片描述

在这里插入图片描述

2. 动手创建AOP实例

开发思路,分为四个步骤:

  1. 创建一个服务,其中有一个方法,作为连接点
  2. 创建一个before advice,就是前置增强
  3. 在配置文件中配置好连接点、增强,实现调用方法前执行增强逻辑。
  4. 测试是否生效
2.1 创建连接点
public class ProductService {
    public void add() {
        System.out.println("添加产品...");
    }
}
2.2 创建前置增强
public class LogAspect {
    public void beforeMethod() {
        System.out.println("执行业务逻辑前,记录日志...");
    }
}// aop中的增强就是普通的方法
3.3 配置切面
<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="productService" class="com.demo.aop.service.ProductService"/>
    <bean id="logAspect" class="com.demo.aop.aspect.LogAspect"/>
    // 配置AOP切面
    <aop:config>
        // 定义aspect切面,关联了前面定义的logAspect这个bean
        <aop:aspect id="test" ref="logAspect">
            // 定义切点,选择连接点add作为切点
            <aop:pointcut id="productServicePointcut" expression="execution(public void com.demo.aop.service.ProductService.add())"/>
            // 使用logAspect中的beforeMethod方法作为切点关联的增强
            <aop:before method="beforeMethod" pointcut-ref="productServicePointcut"/>
        </aop:aspect>
    </aop:config>
</beans>
3.4 测试
public class Test {
    public static void main(String[] args) {
        // 加载配置文件spring.xml,初始化Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        // 从容器中获取productService这个bean
        ProductService productService = context.getBean("productService", ProductService.class);
        // 调用add()方法,由于配置了切面,在执行add方法之前,会先执行LogAspect中的before()方法。
        productService.add();
    }
}

输出:
执行业务逻辑前,记录日志...
添加产品

3. spring注解实现

前面我们做Aspect切面都是使用的配置文件,随着切面的增加,配置信息会越来越多,比较麻烦,后续提高效率就要使用spring AOP注解了,非常简单,下面我们来实践一下:

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 组件扫描路径 -->
    <context:component-scan base-package="com.demo.aop"/>

    <!-- 开启AOP -->
    <aop:aspectj-autoproxy/>
</beans>
@Service("transferService")
public class TransferService {
    // 转账
    public void transfer(String accountFrom, String accountTo, Integer amount) {
        if(amount < 1){
            throw new NullPointerException("转账金额错误 !!!");
        }
        System.out.printf("转账业务:账户 %s 向账户 %s 转账 %d 元 \n", accountFrom, accountTo, amount);
    }
}
@Aspect
@Component
public class LogAspect2 {
    @Pointcut("execution(* com..TransferService.*(..))")
    public void pointcutMethod() {
        System.out.println("测试会输出什么");
    }

    // 这个增强用于pointcutMethod这个切入点。
    @Before("pointcutMethod()")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("【AOP -- before】业务处理之前,连接点:" +
                joinPoint.getSignature().getName());
    }
}
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-annotation.xml");
        TransferService transferService = context.getBean("transferService", TransferService.class);
        transferService.transfer("张三","李四",2000);
    }
}

输出:
【AOP -- before】业务处理之前,连接点:transfer
转账业务:账户 张三 向账户 李四 转账 2000

4. Aspect顺序性

实际开发环境中,一个连接点可以有多个增强,例如一个方法需要添加安全验证、日志等通用功能,在多个增强之间,可能会有执行顺序的要求,那如何保证多个增强的执行顺序呢?使用@Order注解即可,数字越小,越优先。

@Aspect
@Order(1)
@Component
public class LogAspect2 {
    @Pointcut("execution(* com..TransferService.*(..))")
    public void pointcutMethod() {
    }

    // 这个增强用于pointcutMethod这个切入点。
    @Before("pointcutMethod()")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("权限验证..." +
                joinPoint.getSignature().getName());
    }
}

@Aspect
@Order(0)
@Component
public class LogAspect3 {
    @Pointcut("execution(* com..TransferService.*(..))")
    public void pointcutMethod() {
    }

    // 这个增强用于pointcutMethod这个切入点。
    @Before("pointcutMethod()")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("记录日志..." +
                joinPoint.getSignature().getName());
    }
}

输出:
记录日志...transfer
权限验证...transfer
转账业务:账户 张三 向账户 李四 转账 2000

5. 总结

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值