源码解析Spring AOP的加载与生效

本次博主主要进行Spring AOP这里的解析,因为在工作中使用后,却不知道背后的实现原理并在使用的过程中发现了一些认知缺陷,所以决定写这么一篇文章以供大家参考参考,进入正题。

本次博主使用了@Aspect、@Around、@PointCut注解实现了一些小的需求,大家想必都用过,我就简单的举个例子吧。

ini 复制代码
  1 @Aspect
  2 @Component
  3 public class CrmCacheAspect {
  4 
  5     @Autowired
  6     StringRedisTemplate stringRedisTemplate;
  7 
  8     private ConcurrentHashMap<String, ICacheResultParser> parserMap = new ConcurrentHashMap();
  9 
 10     private ConcurrentHashMap<String, IKeyGenerator> generatorMap = new ConcurrentHashMap();
 11 
 12     private ConcurrentHashMap<String,Boolean> keyMap = new ConcurrentHashMap<>();
 13     @Pointcut("@annotation(com.bjh.hms.crm.annotation.CrmCache)")
 14     public void pointCut(){}
 15 
 16     @Around("pointCut() && @annotation(crmCache)")
 17     public Object joinPoint(ProceedingJoinPoint joinPoint, CrmCache crmCache) throws InstantiationException, IllegalAccessException {
 18         String value = "";
 19         String key = "";
 20         Object result = "";
 21         try {
 22             key = getKey(crmCache,joinPoint);
 23             value = stringRedisTemplate.opsForValue().get(key);
 24         } catch (Exception e) {
 25             XxlJobHelper.log("获取缓存{}失败:{}",crmCache.key(),e);
 26         } finally {
 27             if (StringUtils.isBlank(value)) {
 28                 value = synchronizeCache(key, joinPoint, crmCache);
 29             }
 30             result = getResult(crmCache, value, joinPoint);
 31         }
 32         return result;
 33     }
 34 
 35     private Object getResult(CrmCache crmCache,
 36                              String value,
 37                              ProceedingJoinPoint joinPoint) throws InstantiationException, IllegalAccessException {
 38         if (value == null) {
 39             return null;
 40         }
 41         String name = crmCache.parser().getName();
 42         ICacheResultParser iCacheResultParser;
 43         if (parserMap.containsKey(name)) {
 44             iCacheResultParser = parserMap.get(name);
 45         } else {
 46             iCacheResultParser = crmCache.parser().newInstance();
 47             parserMap.put(name,iCacheResultParser);
 48         }
 49         MethodSignature signature = (MethodSignature) joinPoint.getSignature();
 50         Class returnType = signature.getReturnType();
 51         Object parse = iCacheResultParser.parse(value, returnType);
 52         return parse;
 53     }
 54 
 55     /**
 56      * Title: 解决redis并发穿透
 57      * @author 2021/8/13 17:15
 58      * @return java.lang.String
 59      */
 60     private String synchronizeCache(String key,
 61                                     ProceedingJoinPoint joinPoint,
 62                                     CrmCache crmCache) {
 63         String value = "";
 64         //暂停100-200ms,线程顺序执行
 65         try {
 66             Thread.sleep((int)(Math.random()*(200 - 100 + 1) + 100));
 67         } catch (InterruptedException e) {
 68             XxlJobHelper.log("synchronizeCache error {}", ExceptionUtil.stacktraceToString(e));
 69         }
 70         while (StringUtils.isBlank(value = stringRedisTemplate.opsForValue().get(key))
 71         && (keyMap.get(key) == null || keyMap.get(key))){
 72             //防止重复调用
 73             if (keyMap.get(key) == null || !keyMap.get(key)) {
 74                 keyMap.put(key,true);
 75                 Object proceed = null;
 76                 try {
 77                     proceed = joinPoint.proceed();
 78                 } catch (Throwable e) {
 79                     XxlJobHelper.log("处理失败:{}",ExceptionUtil.stacktraceToString(e));
 80                 }
 81                 putValueByRedis(key,proceed,crmCache);
 82                 keyMap.put(key,false);
 83             }
 84         }
 85         keyMap.remove(key);
 86         return value;
 87     }
 88 
 89     private void putValueByRedis(String key, Object value, CrmCache crmCache) {
 90         if (value == null) {
 91             return;
 92         }
 93         if (value instanceof String) {
 94             stringRedisTemplate.opsForValue().set(key, value.toString());
 95         } else {
 96             String jsonString = JSONObject.toJSONString(value);
 97             stringRedisTemplate.opsForValue().set(key,jsonString);
 98         }
 99         //-1代表不过期
100         if (crmCache.expire() != -1) {
101             stringRedisTemplate.expire(key, crmCache.expire(), TimeUnit.MINUTES);
102         }
103     }
104 
105     private String getKey(CrmCache crmCache, ProceedingJoinPoint joinPoint) throws InstantiationException, IllegalAccessException {
106         MethodSignature signature = (MethodSignature) joinPoint.getSignature();
107         Method method = signature.getMethod();
108         Object[] args = joinPoint.getArgs();
109         String iKeyGeneratorName = crmCache.generator().getName();
110         String key = crmCache.key();
111         IKeyGenerator iKeyGenerator = null;
112         if (generatorMap.containsKey(iKeyGeneratorName)) {
113             iKeyGenerator = generatorMap.get(iKeyGeneratorName);
114         } else {
115             iKeyGenerator = crmCache.generator().newInstance();
116             generatorMap.put(iKeyGeneratorName,iKeyGenerator);
117         }
118         return iKeyGenerator.generate(key,method,args);
119     }
120 
121 }

本例子主要是对结果与请求进行解析缓存,spring其实有自带的,但是不可以使用缓存时间,有缓存时间又需要引入其他依赖包,公司内部私服又是内网访问的,所以就自写了一个简单的注解实现了缓存有限时间功能。这不是重点,我们来分析一下注解是如何加载进来的,又是如何被spring走进来解析的吧。

讲解之前,博主还是一如既往的为大家画了几张草图,以便大家防止看代码看晕,先来第一张:aspect注解源码分析加载与生效

www.processon.com/view/link/6...

我们开始走代码,我们直接走bean的创建开始,如果有小伙伴不知道整个bean创建流程的话,可以看一下博主以前的画 的草图脑补一下:

www.processon.com/view/link/5...

代码走起,任意的bean创建都可以,如果看不了静态代码,自行debug就可以了。\

scss 复制代码
 1     protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
 2         Object bean = null;
 3         if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
 4             // 确定是否有aspect注解
 5             if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
 6                 Class<?> targetType = determineTargetType(beanName, mbd);
 7                 if (targetType != null) {
 8                     //解析注解
 9                     bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
10                     if (bean != null) {
11                         //此处会给有自定义注解的bean创建代理类返回
12                         bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
13                     }
14                 }
15             }
16             mbd.beforeInstantiationResolved = (bean != null);
17         }
18         return bean;
19     }

我们分析一下hasInstantiationAwareBeanPostProcessors方法,看看是如何走进来的,从方法名字可以看出,是否有InstantiationAwareBeanPostProcessors后置处理器,那我们本身并没有去填加这个类,那怎么就有了呢,原因就在我们引入aop包依赖后,有一个默认的自动配置AopAutoConfiguration,EnableAspectJAutoProxy注解中间引入了一个AspectJAutoProxyRegistrar类,实现这个registerBeanDefinitions方法后,引入了一个AnnotationAwareAspectJAutoProxyCreator类,这个类就是AspectJAutoProxyRegistrar的实现类,所以hasInstantiationAwareBeanPostProcessors方法走通了。

再看一下applyBeanPostProcessorsBeforeInstantiation方法解析注解流程。

kotlin 复制代码
 1 public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
 2         Object cacheKey = getCacheKey(beanClass, beanName);
 3 
 4         if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
 5             if (this.advisedBeans.containsKey(cacheKey)) {
 6                 return null;
 7             }//我们主要分析一下shouldSkip方法
 8             if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
 9                 this.advisedBeans.put(cacheKey, Boolean.FALSE);
10                 return null;
11             }
12         }
13 
14         // Create proxy here if we have a custom TargetSource.
15         // Suppresses unnecessary default instantiation of the target bean:
16         // The TargetSource will handle target instances in a custom fashion.
17         TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
18         if (targetSource != null) {
19             if (StringUtils.hasLength(beanName)) {
20                 this.targetSourcedBeans.add(beanName);
21             }
22             Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
23             Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
24             this.proxyTypes.put(cacheKey, proxy.getClass());
25             return proxy;
26         }
27 
28         return null;
29     }
scss 复制代码
 1     protected boolean shouldSkip(Class<?> beanClass, String beanName) {
 2         // TODO: Consider optimization by caching the list of the aspect names
 3         //主要这里获取了注解
 4         List<Advisor> candidateAdvisors = findCandidateAdvisors();
 5         for (Advisor advisor : candidateAdvisors) {
 6             if (advisor instanceof AspectJPointcutAdvisor &&
 7                     ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
 8                 return true;
 9             }
10         }
11         return super.shouldSkip(beanClass, beanName);
12     }
kotlin 复制代码
 1     //此方法分为两步
 2     protected List<Advisor> findCandidateAdvisors() {
 3         // Add all the Spring advisors found according to superclass rules.
 4         //第一步从bean工厂中找到所有Advisor的实现类
 5         List<Advisor> advisors = super.findCandidateAdvisors();
 6         // Build Advisors for all AspectJ aspects in the bean factory.
 7         if (this.aspectJAdvisorsBuilder != null) {
 8         //主要是第二步:从bean工厂中找到所有带有@aspect注解的类
 9             advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
10         }
11         return advisors;
12     }

我们直接看第二步即可

kotlin 复制代码
 1     public List<Advisor> buildAspectJAdvisors() {
 2         List<String> aspectNames = this.aspectBeanNames;
 3 
 4         if (aspectNames == null) {
 5             synchronized (this) {
 6                 aspectNames = this.aspectBeanNames;
 7                 if (aspectNames == null) {
 8                     List<Advisor> advisors = new ArrayList<>();
 9                     aspectNames = new ArrayList<>();
10                     //获取所有类
11                     String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
12                             this.beanFactory, Object.class, true, false);
13                     for (String beanName : beanNames) {
14                         if (!isEligibleBean(beanName)) {
15                             continue;
16                         }
17                         // We must be careful not to instantiate beans eagerly as in this case they
18                         // would be cached by the Spring container but would not have been weaved.
19                         Class<?> beanType = this.beanFactory.getType(beanName);
20                         if (beanType == null) {
21                             continue;
22                         }
23                         //改类是否是我们写的aspect注解类
24                         if (this.advisorFactory.isAspect(beanType)) {
25                             aspectNames.add(beanName);
26                             AspectMetadata amd = new AspectMetadata(beanType, beanName);
27                             if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
28                                 MetadataAwareAspectInstanceFactory factory =
29                                         new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
30                                 //开始在这里解析
31                                 List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
32                                 if (this.beanFactory.isSingleton(beanName)) {
33                                     this.advisorsCache.put(beanName, classAdvisors);
34                                 }
35                                 else {
36                                     this.aspectFactoryCache.put(beanName, factory);
37                                 }
38                                 advisors.addAll(classAdvisors);
39         .......
40         return advisors;
41     }
scss 复制代码
 1     public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
 2     //获取我们的注解类
 3         Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
 4         //获取名称
 5         String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
 6         validate(aspectClass);
 7 
 8         // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
 9         // so that it will only instantiate once.
10         MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
11                 new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
12 
13         List<Advisor> advisors = new ArrayList<>();
14         //这里循环获取我们类的方法,找到除Pointcut注解外的注解方法
15         for (Method method : getAdvisorMethods(aspectClass)) {
16         //解析方法,如果找到Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class注解,则返回InstantiationModelAwarePointcutAdvisorImpl生成类
17             Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
18             if (advisor != null) {
19                 advisors.add(advisor);
20             }
21         }
22 
23         .......
24 
25         return advisors;
26     }

自此,我们的注解就解析完成了,不过心细的同学发现了,直解析了除Pointcut注解外的注解,Pointcut直接没有解析啊,这个注解一般我们都配置在了Around等注解里面,会有解析类去解析这个方法的。我们看看实例化后的后置处理器逻辑再

ini 复制代码
 1     @Override
 2     public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
 3             throws BeansException {
 4 
 5         Object result = existingBean;
 6         //遍历所有后置处理器,但是我们只看AbstractAutoProxyCreator类的
 7         for (BeanPostProcessor processor : getBeanPostProcessors()) {
 8             Object current = processor.postProcessAfterInitialization(result, beanName);
 9             if (current == null) {
10                 return result;
11             }
12             result = current;
13         }
14         return result;
15     }
kotlin 复制代码
 1     //调用此方法
 2     protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
 3         if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
 4             return bean;
 5         }
 6         if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
 7             return bean;
 8         }
 9         if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
10             this.advisedBeans.put(cacheKey, Boolean.FALSE);
11             return bean;
12         }
13 
14         // Create proxy if we have advice.
15         //是否有注解
16         Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
17         if (specificInterceptors != DO_NOT_PROXY) {
18             this.advisedBeans.put(cacheKey, Boolean.TRUE);
19             //有则创建代理类返回
20             Object proxy = createProxy(
21                     bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
22             this.proxyTypes.put(cacheKey, proxy.getClass());
23             return proxy;
24         }
25 
26         this.advisedBeans.put(cacheKey, Boolean.FALSE);
27         return bean;
28     }

为了清晰逻辑,中间的环节代码就不看了,直接看一下返回的是啥。

arduino 复制代码
 1     public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
 2     //config.isProxyTargetClass()这个默认时true,为什么走cglib代理呢?
 3         if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
 4             Class<?> targetClass = config.getTargetClass();
 5             if (targetClass == null) {
 6                 throw new AopConfigException("TargetSource cannot determine target class: " +
 7                         "Either an interface or a target is required for proxy creation.");
 8             }
 9             if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
10                 return new JdkDynamicAopProxy(config);
11             }
12             return new ObjenesisCglibAopProxy(config);
13         }
14         else {
15             return new JdkDynamicAopProxy(config);
16         }
17     }

为什么spring默认走cglib代理呢?我们大家可能还知道一个注解是@EnableAspectJAutoProxy,其实这个才是控制的开关。如果我们写成false的话是走jdk代理的,但是为什么我们自己的配置类配置EnableAspectJAutoProxy注解了也是无效的呢?这时候就要看一下AopAutoConfiguration自动配置类了,为了防止大家看晕,博主也画了一张草图:

www.processon.com/view/link/6...

swift 复制代码
 1 public class AopAutoConfiguration {
 2     //jdk和cglib都有注解,但是默认只有一个生效了,就是CglibAutoProxyConfiguration,因为ConditionalOnProperty注解说明了一起
 3     @Configuration
 4     @EnableAspectJAutoProxy(proxyTargetClass = false)
 5     @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
 6     public static class JdkDynamicAutoProxyConfiguration {
 7 
 8     }
 9 
10     @Configuration
11     @EnableAspectJAutoProxy(proxyTargetClass = true)
12     @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
13     public static class CglibAutoProxyConfiguration {
14 
15     }
16 
17 }

当我们不去在配置文件中明确标明spring.aop.proxy-target-class属性时,只有就是CglibAutoProxyConfiguration是生效的,怕有些小伙伴不知道ConditionalOnProperty注解的作用,博主就简单带带大家看一下,熟悉同学可以自行略过,在spring解析配置类时,就会解析该注解

less 复制代码
 1     //这是校验配置类的时候解析的,路径-》org.springframework.context.annotation.ConditionEvaluator#shouldSkip
 2     public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
 3     //由于jdk和cglib类都有Conditional的子注解,所以都通过了
 4         if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
 5             return false;
 6         }
 7 
 8         if (phase == null) {
 9             if (metadata instanceof AnnotationMetadata &&
10                     ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
11                 return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
12             }
13             return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
14         }
15 
16         List<Condition> conditions = new ArrayList<>();
17         //找到ConditionalOnProperty注解
18         for (String[] conditionClasses : getConditionClasses(metadata)) {
19             for (String conditionClass : conditionClasses) {
20                 Condition condition = getCondition(conditionClass, this.context.getClassLoader());
21                 conditions.add(condition);
22             }
23         }
24 
25         AnnotationAwareOrderComparator.sort(conditions);
26 
27         for (Condition condition : conditions) {
28             ConfigurationPhase requiredPhase = null;
29             if (condition instanceof ConfigurationCondition) {
30                 requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
31             }
32             //开始检验是否匹配
33             if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
34                 return true;
35             }
36         }
37 
38         return false;
39     }
scss 复制代码
 1     public final boolean matches(ConditionContext context,
 2             AnnotatedTypeMetadata metadata) {
 3         String classOrMethodName = getClassOrMethodName(metadata);
 4         try {
 5         //走这里查看OnPropertyCondition匹配即可
 6             ConditionOutcome outcome = getMatchOutcome(context, metadata);
 7             logOutcome(classOrMethodName, outcome);
 8             recordEvaluation(context, classOrMethodName, outcome);
 9             return outcome.isMatch();
10         }
11         ......
12     }
typescript 复制代码
 1         private void collectProperties(PropertyResolver resolver, List<String> missing,
 2                 List<String> nonMatching) {
 3             for (String name : this.names) {
 4                 String key = this.prefix + name;
 5                 if (resolver.containsProperty(key)) {
 6                     if (!isMatch(resolver.getProperty(key), this.havingValue)) {
 7                         nonMatching.add(name);
 8                     }
 9                 }
10                 else {
11                 //直接查看关键代码,如果配置文件中没有该属性,查看是否注解中写了matchIfMissing属性,而我们的cglib是true,所以,不会missing,而是装配起来了,所以默认走cglib代理
12                     if (!this.matchIfMissing) {
13                         missing.add(name);
14                     }
15                 }
16             }
17         }

现在我们的注解不仅加载完了,而且被注解表明的也生成了代理类,我们看看切面注解是如何生效的,我们就以cglib举例了,jdk类似

typescript 复制代码
 1 //CglibAopProxy
 2 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
 3             Object oldProxy = null;
 4             boolean setProxyContext = false;
 5             Object target = null;
 6             TargetSource targetSource = this.advised.getTargetSource();
 7             try {
 8                 .....
 9                 //获取是否有拦截链,并不是我们的请求拦截器,这里把切面认为是一种拦截器了
10                 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
11                 Object retVal;
12                 // Check whether we only have one InvokerInterceptor: that is,
13                 // no real advice, but just reflective invocation of the target.
14                 if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
15                     // We can skip creating a MethodInvocation: just invoke the target directly.
16                     // Note that the final invoker must be an InvokerInterceptor, so we know
17                     // it does nothing but a reflective operation on the target, and no hot
18                     // swapping or fancy proxying.
19                     Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
20                     retVal = methodProxy.invoke(target, argsToUse);
21                 }
22                 else {
23                     // We need to create a method invocation...
24                     //主要就是走后面的.proceed()方法
25                     retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
26                 }
27                 retVal = processReturnType(proxy, target, method, retVal);
28                 return retVal;
29             .....
30                 }
31             }
32         }
kotlin 复制代码
 1     //这里就像走我们的请求过滤器一样,每个拦截器都走一遍,最后都调用proceed()再回到这个方法,直到++this.currentInterceptorIndex到头终止
 2     public Object proceed() throws Throwable {
 3         //    We start with an index of -1 and increment early.
 4         if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
 5             return invokeJoinpoint();
 6         }
 7 
 8         Object interceptorOrInterceptionAdvice =
 9                 this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
10         if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
11             // Evaluate dynamic method matcher here: static part will already have
12             // been evaluated and found to match.
13             InterceptorAndDynamicMethodMatcher dm =
14                     (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
15             if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
16             //别的我们不看,就看我们自己定义的@around
17                 return dm.interceptor.invoke(this);
18             }
19             else {
20                 // Dynamic matching failed.
21                 // Skip this interceptor and invoke the next in the chain.
22                 return proceed();
23             }
24         }
25         else {
26             // It's an interceptor, so we just invoke it: The pointcut will have
27             // been evaluated statically before this object was constructed.
28             return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
29         }
30     }

调用反射的时候,就会发现我们的around中才会去解析pointcut方法,因为我们在around注解里面写了,具体设这个类PointcutParser#parsePointcutExpression去进行解析的,将pointcut的表达式放入到around中作为参数传递。

对此,Spring AOP就全部讲解完毕了,里面为了减少文章篇幅,去掉了一些中间的跳转代码,具体可以看一下,博主发的草图,草图中所以的逻辑都很清晰,也贴了一些关键性的逻辑代码。希望大家可以在深入了解了解。


相关推荐
程序猿阿越3 天前
Kafka源码(六)消费者消费
java·后端·源码阅读
zh_xuan9 天前
Android android.util.LruCache源码阅读
android·源码阅读·lrucache
魏思凡12 天前
爆肝一万多字,我准备了寿司 kotlin 协程原理
kotlin·源码阅读
白鲸开源16 天前
一文掌握 Apache SeaTunnel 构建系统与分发基础架构
大数据·开源·源码阅读
Tans51 个月前
Androidx Fragment 源码阅读笔记(下)
android jetpack·源码阅读
Tans51 个月前
Androidx Fragment 源码阅读笔记(上)
android jetpack·源码阅读
Tans51 个月前
Androidx Lifecycle 源码阅读笔记
android·android jetpack·源码阅读
凡小烦1 个月前
LeakCanary源码解析
源码阅读·leakcanary
程序猿阿越1 个月前
Kafka源码(四)发送消息-服务端
java·后端·源码阅读
CYRUS_STUDIO1 个月前
Android 源码如何导入 Android Studio?踩坑与解决方案详解
android·android studio·源码阅读