Spring 源码解析 | Aop 源码实现(一)(上)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 本次我们一起来聊一下 Spring Aop 的原理。本文主要是通过一个简单的 Aop 案例,然后结合 Spring 的源码进行分析, Spring Aop 介入时机; Spring Aop 初始化等。环境介绍:JDK 17Spring 6.0.0-SNAPSHOT

案例介绍


我们在业务中可能用到 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;
}


看到上面的代码我们先梳理一下 这个代理的过程:


  1. 首先获取 advice


  1. 然后判断是否需要代理


  1. 然后执行代理


获取 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;
}


相关文章
|
1天前
|
canal 缓存 关系型数据库
Spring Boot整合canal实现数据一致性解决方案解析-部署+实战
Spring Boot整合canal实现数据一致性解决方案解析-部署+实战
|
1天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
4天前
|
Java 数据库
Javaweb之SpringBootWeb案例之AOP案例的详细解析
Javaweb之SpringBootWeb案例之AOP案例的详细解析
11 0
|
8天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
14天前
|
XML Java 数据格式
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
19 0
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
|
14天前
|
XML Java 数据格式
从入门到精通:Spring基础注解的全面解析
从入门到精通:Spring基础注解的全面解析
31 2
从入门到精通:Spring基础注解的全面解析
|
14天前
|
Java 数据库 Spring
切面编程的艺术:Spring动态代理解析与实战
切面编程的艺术:Spring动态代理解析与实战
26 0
切面编程的艺术:Spring动态代理解析与实战
|
14天前
|
Java 关系型数据库 MySQL
高级对象装配:解析Spring创建复杂对象的秘诀
高级对象装配:解析Spring创建复杂对象的秘诀
27 0
高级对象装配:解析Spring创建复杂对象的秘诀
|
23天前
|
XML Java Maven
Spring之Aop的注解使用
Spring之Aop的注解使用
|
10天前
yolo-world 源码解析(六)(2)
yolo-world 源码解析(六)
19 0

推荐镜像

更多