Le code source dévoilé ! Le principe de la programmation AOP au printemps ! (4) Achèvement

Prenez l'habitude d'écrire ensemble ! C'est le 10ème jour de ma participation au "Nuggets Daily New Plan · April Update Challenge", cliquez pour voir les détails de l'événement .

Série révélant le code source | Table des matières

Le code source dévoilé ! L'annotation autowired de Spring IOC !
Le code source dévoilé ! Le principe du Bean scanning dans Spring ! Le code source dévoilé ! Le principe de
l'intégration Spring et Mybatis !
Le code source dévoilé ! Le principe de la programmation AOP dans Spring
! Le principe de la programmation (2) Le code source du processus d'inscription de la classe Conseil se
dévoile ! Le principe de la programmation AOP dans Spring ! (3) Le processus de création de la classe aspect (1)

avant-propos

Dans le chapitre précédent, la méthode postProcessBeforeInstantiation, après l'exécution de cette méthode, renvoie une valeur nulle. Puisqu'il s'agit d'un PostProcessor, il existe une méthode avant et une méthode après.

Commencer officiellement

Entrez la méthode postProcessAfterInitialization ici pour jeter un coup d'œil.

@Override
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;
}
复制代码

À ce stade, la méthode init du Bean a été exécutée, donc à ce moment, If entrera. getCachekey renvoie enfin BeanName.

Supprimer cacheKey dans la propriété earlyProxyReferences obtiendra un null, donc il entrera également le If interne à ce moment et appellera warpIfNecessary. Ce que fait la méthode, je ne sais pas ici, puis regarde en bas.

Interprétation de la méthode warpIfNecessary

Voyons d'abord les paramètres de la méthode.

Object wrapIfNecessary(Object bean, String beanName, Object cacheKey)
复制代码

Voici trois paramètres formels, voir le nom et la signification, donc je n'expliquerai pas plus. Regardez simplement le premier morceau de code. image.pngAprès être entré, vous pouvez voir plusieurs Si, un par un à analyser :
le premier SI

if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
   return bean;
}
复制代码

先是判断了Beanname,然后去targetsourcedBeans中判断是否有该值,按我说,我没讲过这个属性,应该是empty的。 image.png 断点查看一下,果然如此,所以继续下一个If

第二个IF

if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
   return bean;
}
复制代码

在这个If中,可以看到拿着cacheKey去advisedBeans中获取,然后和False比较。这里犹豫我们当前的Bean是被切入类,所以此时不在此容器中存在,所以不为空。

第三个IF

if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}
复制代码

这个If和after中的代码一样,可以直接跳过,因为不会进入,此处返回false。

后续的代码逻辑

image.png 这里先简单阅读一下当前的代码,通过bean,拿到了切入点的一个集合。
随后判断切入点集合是否为空,不为Null进入代理逻辑。
随后像advisedBeans中打入一个标记,最后返回Bean。

此处阅读的重点有两个,一个是getAdvicesAndAdvisorsForBean方法,另外一个是createProxy方法。这里一个一个看

getAdvicesAndAdvisorsForBean 方法

@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
      Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

   List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
   if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
   }
   return advisors.toArray();
}
复制代码

这里的方法很简单,主要实现逻辑在findEligibleAdvisors方法中,接着往下追。 image.png 看到上方的代码块,有两个List,大致可以猜出是做什么用的。第一个List是获取当前Spring中的所有的切入点。第二个集合是经过过滤的,只有当前类的切入点的一个集合。

findCandidateAdvisors方法我们在before方法中,有涉足到,这里就不讲了,原理其实就是把Spring中的所有Bean遍历一遍,找出所有切入类,然后根据类再找出所有切入点。我们暂时先看一下第二个方法。

findAdvisorsThatCanApply

image.png 这里方法,明眼人都可以看出来,关键在于中间那行代码,其他的看方法就知道是往上下文中塞入一些东西,我们不管他,接着往下追。

image.png 这里先看第一段代码,在这个里面,遍历了candiadateAdvisors,在其中去判断是否instanceof于IntroductionAdvisor,我们这里的Advisor(切入点)都是InstantiationModelAwarePointcutAdvisorImpl,所以不继承自这个接口,此处不会走。

image.png 在这一段代码中,有一个成员变量,hasIntroductions,由于刚刚的eligibleAdvisors集合中并没有添加值,所以此时他是Empty的,取反即为false。
随后判断candidate是否继承自introductionAdvisor。此时不继承,接着判断,调用canApply方法,由此可见关键方法在于此方法中,我们接着往下追。

image.png 查看这里的代码比较简单,两个判断调用不同的方法,看到这里很明显,会进入第二个If中去,想一下我们刚刚提到的,我们Advisor的类型。InstantiationModelAwarePointcutAdvisorImpl,毋庸置疑会进入第二个。我们追进去查看。

image.png 进入方法后,我们引来第一个判断,通过pc获取到classFilter,然后matches当前要注册的Class。还记得Pc是什么吗,切入点。 image.png 这里通过Debug帮诸位回顾一下。这个If很明显就是根据表达式来判断当前类是否为切入类。不是的话直接放回false。我们这里接着往下看。

image.png 这个If,讲真的我没太看明白他是做什么的,不过可以大致看出来,是用来做方法匹配的。有一个行注释,这里要翻译一下

No need to iterate the methods if we're matching any method anyway...
如果我们仍然匹配任何方法,则无需迭代方法......

看这个翻译就明白是什么意思了。那我们接着往下看代码。

image.png 随后创建了个一个方法匹配器,想来是用来做迭代方法用的了。

image.png 到这里就知道了,最后是通过反射,获取到类中的所有方法,遍历Method对象,用方法匹配器去判断是否为有效的。最终一路返回true,回到FindAdvisorsThatCanApply方法。 image.png 最终加入到了eligibleAdvisors中,返回了回去。最终回到了warpIfNecessary方法中,我们接着往下看。

warpIfNecessary方法后续

image.png 接下来也就是正式的,创建代理的环节了。

由于我们这里刚刚拿到切入点集合,所以这里不为null,会进入If。想advisedBeans中打入了一个标记,这里的value有一点变化,是为True。
后续进入了CreateProxy方法。

createProxy方法解读

看方法前,先看方法的入参。此时方法进入,给定了四个参数。

  1. beanClass
  2. beanName
  3. specificInterceptors 切入点集合
  4. tagetSouce new了一个targetsource,把执行完init方法的Bean作为入参传了进去

image.png 方法一进入,迎面而来一个If,这个If是会进入的,因为我们一开始创建的工厂是DefaultListableBeanFactory看名字就是同类。但是里面做了什么讲真的,我不是没搞懂,在里面设置了一个attr > originalTargetClass,可能后面有用到吧,这里先跳过。

image.png 再看第二段代码,这里开头创建了一个proxyFacotry,也就是说,后面可能会使用这个代理工厂来创建代理对象。随后进入了一个If判断,我点进去查看后直接返回了false。所以进入else代码,此处调用了shouldProxyTargetClass方法。 image.png 这里代码追进去后,发现是与xml中的属性进行equals判断,很明显我没有设置该属性,返回false,这里也进入Else环节调用了evaluateProxyInterfaces方法。 image.png 最近去查看,发现就是普通的根据Class获取继承的接口,随后像ProxyFactory中添加了接口,这里猜测,这个接口后面要应用到创建Proxy对象中的。

image.png 在这一段代码中,根据传入进来切入点集合,又再次了build了切入点,随后给ProxyFactory放了一些配置。至于customerProxyFactory,点击进去看了,是个模板方法。

image.png 在这最后一段代码中,getProxy了,所以此时我们继续往里追。 image.png 看到这里,先去创建一个aopProxy。所以此时需要去看一下是如何创建的。往里追代码。 image.png 看到这里就明了了,为什么人们常说,AOP的实现方式有哪些,一种是CGLIB,一种是JDK了。这里有个点值得注意,看当前的方法写,是这样的。如果原来的class是接口,或者原来的class是代理class,再或者是lambda的话,就算配置了cglib他还是会用jdk的动态代理。

image.png 好的,我们回到这里,AopProxy已经创建完毕,接下来就getProxy了。

image.png 我们这里默认是JDK模式,看这个方法是直接调用JDK的实例化方法了。

Certaines personnes peuvent être curieuses ici, comment est-il intervenu avant et après l'exécution de Bean. Le principe est en fait assez simple. Si la méthode d'appel d'une classe par réflexion passe par la méthode invoke, notez ici que lors de l'appel de la méthode newProxyInstance, la troisième méthode passe dans un this.Après avoir cliqué dessus, on constate que cette méthode nécessite une interface, donc évidemment, Notre JdkDynamicAopProxy actuel implémente cette interface en examinant une méthode d'appel dans cette interface. Ensuite, il est clair à ce stade que nous localisons directement la méthode d'appel.

image.pngLors de l'inspection finale, le code se trouve ici et un ensemble de chaînes est obtenu via la classe cible, qui est la classe de l'agent lui-même. C'est la chaîne de responsabilité. Ensuite, encapsulez un ensemble de chaînes de responsabilité dans la classe MethodInvocation. Ensuite, sa méthode de procédure est appelée et nous continuons à poursuivre. image.pngAprès avoir chassé ici, il y a une forte probabilité qu'il soit ici, et le code du point d'entrée est exécuté ici.

arriver à une fin

Cet article se termine ici. C'est le chapitre final le plus AOP. Je ne sais pas si cela vous sera bénéfique après l'avoir lu. Si vous êtes intéressé par le processus d'inscription du côté de CGLib, vous pouvez essayer de le lire vous-même.

J'ai vu ça, s'il vous plaît, donnez un coup de pouce et partez, trésor ~

remarques finales

Le but de la rédaction d'articles est de vous aider à consolider vos connaissances, vous pouvez signaler des choses mauvaises ou fausses dans la zone de commentaires. Si vous lisez l'article et pensez qu'il vous est utile, vous pouvez lui donner un coup de pouce. Si vous trouvez des questions qui ont des doutes, ou si vous ne comprenez pas, vous pouvez commenter et m'ajouter sur WeChat, et vous devez tout savoir. Bien sûr, j'espère aussi me lier d'amitié avec tout le monde et apprendre les uns des autres.

Je suppose que tu aimes

Origine juejin.im/post/7085437712420831245
conseillé
Classement