案例介绍
我们在业务中可能用到 Redis 作为分布式锁,如果我们手动去 lock, unLock。 这样做的缺点就是代码量比较大,结合我们当前的 Aop 原理背景, 我们可以对我们业务开发中的 Lock 做一个简单的封装。
案例代码
代码如下所示:
@Component @Aspect public class DistributedLockAspect { private static final Logger logger = LoggerFactory.getLogger(DistributedLockAspect.class); @Pointcut("@annotation(com.summer.test.distributedlock.DistributedLock)") public void distributedLockPointCut() { // distributedLockPointCut } @Around("distributedLockPointCut()") public Object distributedLockAround(ProceedingJoinPoint pjp) throws Throwable { Method method = ((MethodSignature) pjp.getSignature()).getMethod(); DistributedLock ub = method.getAnnotation(DistributedLock.class); String lockKey = lockKey(ub.key(), pjp); long timeOut = ub.timeOut(); logger.debug("Start Redis Distributed Lock, Lock key:{}", lockKey); try { // todo redis lock Object result = pjp.proceed(); logger.debug("End Redis Distributed Lock, Lock key:{}", lockKey); return result; } catch (Throwable ex) { logger.warn("Error Redis Distributed Lock, Lock key:{}", lockKey); throw ex; } } private String lockKey(String key, ProceedingJoinPoint joinPoint) { SpelExpressionParser parser = new SpelExpressionParser(); DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer(); // 通过joinPoint获取被注解方法 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); // 使用spring的DefaultParameterNameDiscoverer获取方法形参名数组 String[] paramNames = nameDiscoverer.getParameterNames(method); // 解析过后的Spring表达式对象 Expression expression = parser.parseExpression(key); // spring的表达式上下文对象 EvaluationContext context = new StandardEvaluationContext(); // 通过joinPoint获取被注解方法的形参 Object[] args = joinPoint.getArgs(); // 给上下文赋值 if (paramNames != null) { for (int i = 0; i < args.length; i++) { context.setVariable(paramNames[i], args[i]); } } // 表达式从上下文中计算出实际参数值 return Objects.requireNonNull(expression.getValue(context)).toString(); } }
案例总结
在上述代码中我有用到 @Aspect
、@Pointcut
、 @Around
这些概念我先不展开介绍。我们先看看如何使用。具体的概念介绍可以参考 Spring 官网, 或者我之前的文章 Spring Aop 详解 。
@DistributedLock(key = "'user_service:user_opt:' + #userModel.name") public void save(UserModel userModel) { // todo }
通过 @DistributedLock
降低了代码的复杂度,而且这样做也利于我们公共组件的封装。
Spring Aop 介入时机
方法入口
Spring Aop 是在 Spring 依赖注入完成之后,初始化 Bean 方法AbstractAutowireCapableBeanFactory.java#initializeBean�
中执行的。我们先来看看具体的代码。
// bean 初始化方法 protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { invokeAwareMethods(beanName, bean); Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // 初始化之前 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { // 执行初始化方法 invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { // 初始化之后,这里会判断是否需要执行 Aop 代理 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
核心处理类
核心处理类是 AbstractAutoProxyCreator
它是实现了 BeanPostProcessor
我们可以先看看它的 postProcessAfterInitialization
方法
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
到了这里我们就可以很容易知道 wrapIfNecessary
就是核心的逻辑处理。
Spring Aop 初始化
我们先来看看 wrapIfNecessary
的具体实现:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); // 判断是否需要进行 Aop // Object[] DO_NOT_PROXY = null; if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // 创建代理对象 // new SingletonTargetSource(bean) 被代理对象,就是 Bean Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
看到上面的代码我们先梳理一下 这个代理的过程:
- 首先获取 advice
- 然后判断是否需要代理
- 然后执行代理
获取 Advice
获取的入口方法是 getAdvicesAndAdvisorsForBean
protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { // 寻找匹配的 advisor List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); }
然后通过 findEligibleAdvisors
寻找匹配的 advisor
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { // 找到所有的 Advisor List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 筛选 List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { // 排序 eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }