一篇搞懂Spring AOP+案例(修改PointCut参数)

本文介绍了如何在Spring框架中使用AOP解决在项目中突发的折扣活动需求,包括AOP的概念、切面、Joinpoint、Advice、Pointcut等关键概念,以及如何通过定义切入点和通知增强处理,实现在不修改源代码情况下进行折扣计算。
摘要由CSDN通过智能技术生成

引言

在之前做的项目中,突然冒出来一个从未提及的需求(这是很常见的情况),原因是商家要经常做打折活动,这就意味着要在每种类型的订单接口中添加代码进行计算,或者就是在POJO类中添加字段。我果断选择了使用AOP来解决。本片文章为大家介绍AOP以及AOP的使用。

Spring AOP

面向切面的编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象的编程(OOP)。OOP中模块化的关键单位是类,而AOP中模块化的单位是切面。切面使跨越多种类型和对象的关注点(如事务管理)模块化。常被应用于事务处理、日志管理、权限控制、异常处理等。

简单的说,就是在不修改源代码的前提下,为系统中的业务添加特定功能。

AOP概念

  • Aspect(切面): 一个跨越多个类的关注点的模块化。事务管理是企业级Java应用中横切关注点的一个很好的例子。在Spring AOP中,切面是通过使用常规类(基于 schema 的方法)或使用 @Aspect 注解的常规类(@AspectJ 风格)实现的。

  • Join point: 程序执行过程中的一个点,俗称连接点。在Spring AOP中,一个连接点总是代表一个方法的执行。

  • Advice: 指的是拦截后要做的事情,切入点增强的内容。

  • Pointcut: 切入点,意思是要对哪些方法进行拦截或者添加功能。

  • Target object: 指代理的目标对象。

  • AOP proxy: 一个由AOP框架创建的对象,以实现切面契约(advice 方法执行等)。在Spring框架中,AOP代理是一个JDK动态代理或CGLIB代理。

  • Weaving(织入): 将aspect与其他应用程序类型或对象连接起来,以创建一个 advice 对象。

 AOP提供的advice

  • Before advice: 在连接点之前运行的Advice ,但它不具备以下能力 阻止执行流进行到 join point 的能力(除非它抛出一个异常)。

  • After returning advice: 在一个连接点正常完成后运行的Advice (例如,如果一个方法返回时没有抛出一个异常)。

  • After (finally) advice: 无论连接点以何种方式退出(正常或特殊返回),都要运行该advice。

  • Around advice: 围绕一个连接点的advice,如方法调用。这是最强大的一种advice。Around advice可以在方法调用之前和之后执行自定义行为。它还负责选择是否继续进行连接点或通过返回自己的返回值或抛出一个异常来缩短advice方法的执行。

 编写AOP

AOP编写很简单,大致分为三步。需求:MQ中存在  ”123456“:“川菜“:”20“  的数据消费者消费消息的时候发现该条数据记录的商品要进行打折吗,在不修改源代码的情况下,修改为”123456“:“川菜“:”15“ 。

导入AOP相关依赖

<!--aop依赖1:aspectjrt -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.5</version>
</dependency>
 
<!--aop依赖2: aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

 如果使用的是SpringBoot则不需要引入,会自动装配。

定义切面,简单来说就是挑选要增强的业务模块。

这里我选择消费者的模块。

2.定义切入点,就是在要增强的业务模块中挑选接口或者方法。

在这里我选择对MqListener中的所有方法进行加强,同时定义切入点。

package cn.cxj.mq.listeners;


import cn.cxj.mq.pojo.order;
import cn.cxj.mq.service.orderService;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class MqListener {




    @Autowired
    private orderService orderService;


    //声明队列    mq的容器工厂
//    @RabbitListener(bindings = @QueueBinding(value = @Queue(name="demo.queue"),exchange = @Exchange(name = "direct",type = ExchangeTypes.DIRECT),key = {"粤菜"}) ,containerFactory = "customContainerFactory")
    @Async("pooltoconsumer")
    @RabbitListener(bindings = @QueueBinding(value = @Queue(name="demo.queue"),exchange = @Exchange(name = "direct",type = ExchangeTypes.DIRECT),key = {"粤菜"}) )
    public void yuecai(String msg)
    {
        //拆分消息
        System.out.println(msg);
        String[] split = msg.split(":");
        order order = new order(split[0], split[1],Integer.valueOf(split[2]));
        System.out.println(order.toString());

        //保存MYSQL
        orderService.save(order);

        //测试是否多个消费者
        System.out.println("线程" + Thread.currentThread().getName() + " 执行异步任务" +"-------------"+"粤菜"+"----"+split[1]);

    }
//    @RabbitListener(bindings = @QueueBinding(value = @Queue(name="demo.queue2"),exchange = @Exchange(name = "direct",type = ExchangeTypes.DIRECT),key = {"川菜"}) ,containerFactory = "customContainerFactory")
    @Async("pooltoconsumer")
    @RabbitListener(bindings = @QueueBinding(value = @Queue(name="demo.queue2"),exchange = @Exchange(name = "direct",type = ExchangeTypes.DIRECT),key = {"川菜"}) )
    public void chuancai(String msg)
    {

        System.out.println("111111"+msg);
        //拆分消息
        String[] split = msg.split(":");
        order order = new order(split[0], split[1],Integer.valueOf(split[2]));
        System.out.println(order.toString());
        //保存MYSQL
        orderService.save(order);
        //测试是否多个消费者
        System.out.println("线程" + Thread.currentThread().getName() + " 执行异步任务" +"----"+"川菜"+"----"+split[1]);

    }


}

    @Pointcut("execution(* cn.itcast.mq.controller.Test.*(..))")
    public void log(){

    }

3.定义AOP通知进行增强处理。

    @Around("log()")
    public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {

        Object[] args = joinPoint.getArgs();
        args[0]="123456:川菜:15";
        log.info("method {} start"+joinPoint.getSignature().getName());
        return joinPoint.proceed(args);
    }

运行服务,查看结果,数据修改成功!

总结

通过本篇文章的学习,相信大家对AOP的基本概念和基本使用有了深刻的印象。AOP提高了代码的可拓展性,是Spring中重要的一种思想,希望大家可以灵活运用!

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,我们需要定义一个注解 @RequiresPermissions,用于标记需要权限控制的方法。代码如下: ``` @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequiresPermissions { String[] value(); } ``` 然后,我们需要定义一个切面类,用于拦截被 @RequiresPermissions 标记的方法,并进行权限控制的逻辑。代码如下: ``` @Component @Aspect public class PermissionAspect { @Autowired private PermissionService permissionService; @Pointcut("@annotation(com.example.demo.annotation.RequiresPermissions)") public void requiresPermissionsPointcut() {} @Around("requiresPermissionsPointcut()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { // 获取当前用户信息 User currentUser = UserContext.getCurrentUser(); // 获取方法上的权限标记 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); RequiresPermissions annotation = signature.getMethod().getAnnotation(RequiresPermissions.class); String[] permissions = annotation.value(); // 校验权限 boolean hasPermission = permissionService.checkPermissions(currentUser, permissions); if (!hasPermission) { throw new UnauthorizedException("没有访问权限"); } // 执行原方法 return joinPoint.proceed(); } } ``` 在上述代码中,我们通过 @Pointcut 注解定义了一个切点,用于匹配被 @RequiresPermissions 标记的方法。然后,在 @Around 注解的方法中,我们获取了当前用户信息,以及方法上的权限标记。接着,我们调用 PermissionService 的 checkPermissions 方法,校验当前用户是否拥有对应的权限。如果权限校验失败,我们抛出 UnauthorizedException 异常,表示没有访问权限。否则,我们执行原方法,并返回执行结果。 最后,我们定义一个 PermissionService 接口,用于查询用户权限信息。具体实现可以根据实际情况进行编写。代码如下: ``` public interface PermissionService { /** * 校验用户是否拥有指定的权限 * * @param user 当前用户 * @param permissions 权限列表 * @return 是否拥有指定权限 */ boolean checkPermissions(User user, String[] permissions); } ``` 综上,我们通过 Spring AOP 和自定义注解,模拟实现了 Shiro 框架的权限控制功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值