【Spring】AOP——使用@around实现面向切面的方法增强

工作业务中,有大量分布式加锁的重复代码,存在两个问题,一是代码重复率高,二是容易产生霰弹式修改,使用注解和AOP可以实现代码复用,简化分布式锁加锁和解锁流程。 @around注解是AspectJ框架提供的,允许我们在目标方法的执行前后进行代码增强。下面通过一个示例来介绍如何使用@around注解以及自定义注解实现加解锁(ReenTrantLock)简化。

  1. 改造前原代码 很经典的桥段,初始化10个线程,每个线程分别执行m++1000次,若想得到10000的结果,则必须加锁同步,加锁则需要在执行m++前进行加锁,在finally语句块中执行解锁操作,因此可以通过@around注解实现简化。

    java 复制代码
    @Service
    @Slf4j
    public class HelloService {
        private int m = 0;
    
        private static int nums = 0;
    
        public ReentrantLock lock;
    
        // spel表达式
        private static final String LOCK_KEY = "'asset_tenant_lock_prefix:' +" + "T(org.example.Common.ContextHelper).getTenantInfo().getOrgId()";
    
        @Autowired
        HelloService helloService;
        public String doSomeSum() throws InterruptedException {
            nums++;
            log.info("调用次数:{}", nums);
            m = 0;
            CountDownLatch countDownLatch = new CountDownLatch(10);
            lock = new ReentrantLock();
            for (int i = 0; i < 10; i++) {
                Thread thread = new Thread(() -> {
                    for (int j = 0; j < 1000; j++) {
                        helloService.increM();
                    }
                    countDownLatch.countDown();
                });
                thread.start();
            }
            countDownLatch.await();
            return String.valueOf(m);
        }
        @DistributeLock(key = LOCK_KEY)
        public void increM() {
            lock.lock();
            try {
                m++;
            } finally {
                lock.unlock();
            }
        }
    }
  2. 改造第一步,创建一个注解 注解名为DistributeLock,有一个熟悉key,(这个例子是为了模仿使用Redis分布式锁)

java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeLock {
    String key();
}

3.改造第二步,定义一个切面类 切面类中,定义一个增强方法useDistributeLock,参数joinPoint是切入点,distributeLock是切入点形参,用于传入key。这里我们key使用的是Spel表达式,使用SpelExpressionParser可以求得最终的key值,joinPoint.proceed()执行的则是上文提到的原方法的执行内容,m++。

java 复制代码
@Component
@Slf4j
public class DistributeLockAspect {
    private static ReentrantLock lock = new ReentrantLock();

    @Around("@annotation(distributeLock)")
    public Object useDistributeLock(ProceedingJoinPoint joinPoint, DistributeLock distributeLock) throws Throwable {
        lock.lock();
        org.springframework.expression.ExpressionParser parser
                = new SpelExpressionParser();
        String lockKey = (String) parser.parseExpression(distributeLock.key()).getValue();
        log.info(lockKey);
        try {
            return joinPoint.proceed();
        } finally {
            lock.unlock();
        }
    }
}

4.改造第三步,使用 使用很简单,只需要在需要增强的方法上加上我们创建的注解,并且给注解的属性Key一个字符串形式的Spel表达式即可实现加解锁操作。代码行数大大减少。

java 复制代码
public class HelloService {
    private int m = 0;

    private static int nums = 0;

    public ReentrantLock lock;

    // spel表达式
    private static final String LOCK_KEY = "'asset_tenant_lock_prefix:' +" + "T(org.example.Common.ContextHelper).getTenantInfo().getOrgId()";

    @Autowired
    HelloService helloService;
    public String doSomeSum() throws InterruptedException {
        nums++;
        log.info("调用次数:{}", nums);
        m = 0;
        CountDownLatch countDownLatch = new CountDownLatch(10);
        lock = new ReentrantLock();
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    helloService.increM();
                }
                countDownLatch.countDown();
            });
            thread.start();
        }
        countDownLatch.await();
        return String.valueOf(m);
    }
    @DistributeLock(key = LOCK_KEY)
    public void increM() {
        m++;
    }
}

5.最终测试结果 最终的测试结果是10000,说明加锁成功

来看看我们日志打印的Lock_key值是否符合预期,符合预期

相关推荐
北极无雪19 分钟前
Spring源码学习(拓展篇):SpringMVC中的异常处理
java·开发语言·数据库·学习·spring·servlet
VXbishe26 分钟前
(附源码)基于springboot的“我来找房”微信小程序的设计与实现-计算机毕设 23157
java·python·微信小程序·node.js·c#·php·课程设计
YONG823_API1 小时前
电商平台数据批量获取自动抓取的实现方法分享(API)
java·大数据·开发语言·数据库·爬虫·网络爬虫
扬子鳄0081 小时前
java注解的处理器
java
张某布响丸辣1 小时前
探索消息中间件:RabbitMQ深度解析
分布式·rabbitmq
Amagi.1 小时前
Spring中Bean的作用域
java·后端·spring
wclass-zhengge1 小时前
RabbitMQ篇(死信交换机)
分布式·rabbitmq
2402_857589361 小时前
Spring Boot新闻推荐系统设计与实现
java·spring boot·后端
繁依Fanyi1 小时前
旅游心动盲盒:开启个性化旅行新体验
java·服务器·python·算法·eclipse·tomcat·旅游
J老熊2 小时前
Spring Cloud Netflix Eureka 注册中心讲解和案例示范
java·后端·spring·spring cloud·面试·eureka·系统架构