单元测试案例

  1. 注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface RLock {
    /**
     * 锁名称,代表一个加锁场景
     */
    String name() default LockConstant.DEFAULT_LOCK_NAME;

    /**
     * 锁类型,默认公平锁
     */
    LockType lockType() default LockType.FAIR;

    /**
     * 加锁等待时间
     */
    long waitTime() default LockConstant.DEFAULT_LOCK_WAIT_TIME;

    /**
     * 锁最长持有时间,超过时间会自动释放
     */
    long leaseTime() default LockConstant.DEFAULT_LOCK_LEASE_TIME;

    /**
     * leaseTime时间单位
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;

    /**
     * 锁KEY,支持SpEL
     */
    String key() default "";
}

2.切面

@Order(-1)
@Aspect
public class HexinLockAspect {
    /**
     * SpEL表达式parser
     */
    private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

    private final HexinLockProviderManager hexinLockProviderManager;

    public HexinLockAspect(HexinLockProviderManager hexinLockProviderManager) {
        this.hexinLockProviderManager = hexinLockProviderManager;
    }

    @Around(value = "@annotation(rLock)")
    public Object process(ProceedingJoinPoint joinPoint, RLock rLock) throws Throwable {
        HexinLockProvider lockProvider = hexinLockProviderManager.getProvider(rLock.lockType());
        if (Objects.isNull(lockProvider)) {
            throw new HexinLockException("No lock provider of type: " + rLock.lockType());
        }
        String key = evaluateKey(joinPoint, rLock.key());
        HexinLock lock = lockProvider.getLock(rLock.name(), key);
        boolean flag = false;
        try {
            flag = lock.tryLock(rLock.waitTime(), rLock.leaseTime(), rLock.timeUnit());
            if (flag) {
                return joinPoint.proceed();
            } else {
                throw new HexinLockException("Lock failed.");
            }
        } finally {
            if (flag && lock.isLocked()) {
                lock.unlock();
            }
        }
    }

    /**
     * 分布式锁KEY解析,基于SpEL
     *
     * @param joinPoint 切点
     * @param keyExpr   表达式
     * @return key
     */
    public String evaluateKey(ProceedingJoinPoint joinPoint, String keyExpr) {
        String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        Object[] params = joinPoint.getArgs();
        EvaluationContext ctx = new StandardEvaluationContext();
        for (int i = 0; i < paramNames.length; i++) {
            ctx.setVariable(paramNames[i], params[i]);
        }
        Expression expression = EXPRESSION_PARSER.parseExpression(keyExpr);
        return expression.getValue(ctx, String.class);
    }
}

3.单测

@Slf4j
public class LockAnnotationTest {
    @Mock
    private HexinLockProviderManager hexinLockProviderManager;

    private LockAnnotationDemo lockAnnotationDemo;

    @BeforeEach
    public void setUp() {        
		MockitoAnnotations.initMocks(this);
        RedissonClient redissonClient = new MockRedissonClient();
        Mockito.when(hexinLockProviderManager.getProvider(LockType.FAIR))                
			.thenReturn(new FairLockProviderImpl(redissonClient));
		// 使用AspectJProxyFactory为待测类创建代理对象
        AspectJProxyFactory factory = new AspectJProxyFactory(new LockAnnotationDemo());
        factory.addAspect(new HexinLockAspect(hexinLockProviderManager));
        lockAnnotationDemo = factory.getProxy();
    }
    @Test
    void testAnnotationLock() throws InterruptedException {        
		CountDownLatch latch = new CountDownLatch(1);
        StopWatch watch1 = new StopWatch();
        StopWatch watch2 = new StopWatch();
        log.info("Start...");
        // 先提交第一个线程进锁
        CompletableFuture.runAsync(() -> {            
			watch1.start();
            lockAnnotationDemo.lock("1");
            watch1.stop();
        });
        // 再提交第二个线程进锁
        CompletableFuture.runAsync(() -> {            
			watch2.start();
            lockAnnotationDemo.lock("1");
            watch2.stop();
        });
        // 等待3秒,待两个任务释放锁
        latch.await(3000, TimeUnit.MILLISECONDS);
        latch.countDown();
        log.info("End...");
        // 取两个等待时间最大值,有一个任务因为锁阻塞会等待2秒以上才对
        long maxWait = Math.max(watch1.getTotalTimeMillis(), watch2.getTotalTimeMillis());
        log.info("max wait:{}", maxWait);
        Assertions.assertTrue(maxWait >= 2000);
    }
    /**
     * Demo类
     */
    public static class LockAnnotationDemo {
        @RLock(name = "LOCK", key = "#key")
        public void lock(String key) {
            CountDownLatch latch = new CountDownLatch(1);
            try {                
				log.info("Lock");
                latch.await(1000, TimeUnit.MILLISECONDS);
                latch.countDown();
            } catch (InterruptedException ignored) {
            }
        }
    }
}

你可能感兴趣的:(私人干货,单元测试)