一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して10日目です。クリックしてイベントの詳細をご覧ください。
ソースコード公開シリーズ{2}目次
ソースコードが公開されました!Spring IOCの自動配線アノテーションが公開されました!ソースコードが公開され
ました!SpringでのBeanスキャンの原則が公開されました!ソースコードが公開されました
!SpringとMybatisの統合の原則が公開されました!
ソースコードが公開されました!SpringでのAOPプログラミング
の原則!プログラミングの原則実装!(2)アドバイスクラスの登録プロセスのソースコードが
公開されました!SpringでのAOPプログラミングの原理!(3)アスペクトクラスの作成プロセス(1)
序文
前の章では、postProcessBeforeInstantiationメソッドは、このメソッドが実行された後、nullを返します。PostProcessorであるため、beforeメソッドとafterメソッドがあります。
正式に開始
ここにpostProcessAfterInitializationメソッドを入力して確認してください。
@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;
}
复制代码
この時点で、Beanのinitメソッドが実行されているので、この時点で、Ifが入ります。getCachekeyは最終的にBeanNameを返します。
EarlyProxyReferencesプロパティでcacheKeyを削除すると、nullが取得されるため、この時点で内部Ifも入力され、warpIfNecessaryが呼び出されます。方法が何をするのか、私はここでわかりません、そしてそれから見下ろします。
ワープの解釈IfNecessaryメソッド
まず、メソッドのパラメーターを見てみましょう。
Object wrapIfNecessary(Object bean, String beanName, Object cacheKey)
复制代码
ここに3つの正式なパラメーターがあります。名前と意味を参照してください。これ以上は説明しません。コードの最初の部分を見てください。入力した後、分析するために1つずついくつかのIfを見ることができます:
最初のIF
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
复制代码
先是判断了Beanname,然后去targetsourcedBeans中判断是否有该值,按我说,我没讲过这个属性,应该是empty的。 断点查看一下,果然如此,所以继续下一个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。
后续的代码逻辑
这里先简单阅读一下当前的代码,通过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方法中,接着往下追。 看到上方的代码块,有两个List,大致可以猜出是做什么用的。第一个List是获取当前Spring中的所有的切入点。第二个集合是经过过滤的,只有当前类的切入点的一个集合。
findCandidateAdvisors方法我们在before方法中,有涉足到,这里就不讲了,原理其实就是把Spring中的所有Bean遍历一遍,找出所有切入类,然后根据类再找出所有切入点。我们暂时先看一下第二个方法。
findAdvisorsThatCanApply
这里方法,明眼人都可以看出来,关键在于中间那行代码,其他的看方法就知道是往上下文中塞入一些东西,我们不管他,接着往下追。
这里先看第一段代码,在这个里面,遍历了candiadateAdvisors,在其中去判断是否instanceof于IntroductionAdvisor,我们这里的Advisor(切入点)都是InstantiationModelAwarePointcutAdvisorImpl,所以不继承自这个接口,此处不会走。
在这一段代码中,有一个成员变量,hasIntroductions,由于刚刚的eligibleAdvisors集合中并没有添加值,所以此时他是Empty的,取反即为false。
随后判断candidate是否继承自introductionAdvisor。此时不继承,接着判断,调用canApply方法,由此可见关键方法在于此方法中,我们接着往下追。
查看这里的代码比较简单,两个判断调用不同的方法,看到这里很明显,会进入第二个If中去,想一下我们刚刚提到的,我们Advisor的类型。InstantiationModelAwarePointcutAdvisorImpl,毋庸置疑会进入第二个。我们追进去查看。
进入方法后,我们引来第一个判断,通过pc获取到classFilter,然后matches当前要注册的Class。还记得Pc是什么吗,切入点。 这里通过Debug帮诸位回顾一下。这个If很明显就是根据表达式来判断当前类是否为切入类。不是的话直接放回false。我们这里接着往下看。
这个If,讲真的我没太看明白他是做什么的,不过可以大致看出来,是用来做方法匹配的。有一个行注释,这里要翻译一下
No need to iterate the methods if we're matching any method anyway...
如果我们仍然匹配任何方法,则无需迭代方法......
看这个翻译就明白是什么意思了。那我们接着往下看代码。
随后创建了个一个方法匹配器,想来是用来做迭代方法用的了。
到这里就知道了,最后是通过反射,获取到类中的所有方法,遍历Method对象,用方法匹配器去判断是否为有效的。最终一路返回true,回到FindAdvisorsThatCanApply方法。 最终加入到了eligibleAdvisors中,返回了回去。最终回到了warpIfNecessary方法中,我们接着往下看。
warpIfNecessary方法后续
接下来也就是正式的,创建代理的环节了。
由于我们这里刚刚拿到切入点集合,所以这里不为null,会进入If。想advisedBeans中打入了一个标记,这里的value有一点变化,是为True。
后续进入了CreateProxy方法。
createProxy方法解读
看方法前,先看方法的入参。此时方法进入,给定了四个参数。
- beanClass
- beanName
- specificInterceptors 切入点集合
- tagetSouce new了一个targetsource,把执行完init方法的Bean作为入参传了进去
方法一进入,迎面而来一个If,这个If是会进入的,因为我们一开始创建的工厂是DefaultListableBeanFactory看名字就是同类。但是里面做了什么讲真的,我不是没搞懂,在里面设置了一个attr > originalTargetClass,可能后面有用到吧,这里先跳过。
再看第二段代码,这里开头创建了一个proxyFacotry,也就是说,后面可能会使用这个代理工厂来创建代理对象。随后进入了一个If判断,我点进去查看后直接返回了false。所以进入else代码,此处调用了shouldProxyTargetClass方法。 这里代码追进去后,发现是与xml中的属性进行equals判断,很明显我没有设置该属性,返回false,这里也进入Else环节调用了evaluateProxyInterfaces方法。 最近去查看,发现就是普通的根据Class获取继承的接口,随后像ProxyFactory中添加了接口,这里猜测,这个接口后面要应用到创建Proxy对象中的。
在这一段代码中,根据传入进来切入点集合,又再次了build了切入点,随后给ProxyFactory放了一些配置。至于customerProxyFactory,点击进去看了,是个模板方法。
在这最后一段代码中,getProxy了,所以此时我们继续往里追。 看到这里,先去创建一个aopProxy。所以此时需要去看一下是如何创建的。往里追代码。 看到这里就明了了,为什么人们常说,AOP的实现方式有哪些,一种是CGLIB,一种是JDK了。这里有个点值得注意,看当前的方法写,是这样的。如果原来的class是接口,或者原来的class是代理class,再或者是lambda的话,就算配置了cglib他还是会用jdk的动态代理。
好的,我们回到这里,AopProxy已经创建完毕,接下来就getProxy了。
我们这里默认是JDK模式,看这个方法是直接调用JDK的实例化方法了。
ここで好奇心をそそられる人もいるかもしれませんが、彼はBeanの実行の前後にどのように介入したのでしょうか。原理は実際には非常に単純です。リフレクションを介してクラスを呼び出すメソッドがinvokeメソッドを介している場合、newProxyInstanceメソッドを呼び出すときに、3番目のメソッドがthisを渡すことに注意してください。クリックすると、このメソッドにはインターフェイスが必要であることがわかります。現在のJdkDynamicAopProxyは、このインターフェースのinvokeメソッドを調べることにより、このインターフェースを実装しています。次に、この時点で、invokeメソッドを直接見つけることができます。
最終検査により、コードはここに配置され、エージェント自体のクラスであるtargetClassを介してチェーンの束が取得されます。これが責任の連鎖です。次に、一連の責任チェーンをMethodInvocationクラスにカプセル化します。それから彼のproceedメソッドが呼び出され、私たちは追跡を続けます。ここを追いかけた後、ここにある可能性が高く、エントリポイントのコードがここで実行されます。
終わります
この記事はここで終わります。これは最もAOPの最終章です。読んだ後、それがあなたに役立つかどうかはわかりません。CGLib側の登録プロセスに興味がある場合は、自分で読んでみてください。
私はこれを見ました、親指を立てて行ってください、宝物〜
結びの言葉
記事を書く目的はあなたがあなたの知識を統合するのを助けることです。あなたはコメントエリアで悪いことや間違ったことを指摘することができます。記事を読んで、それがあなたに役立つと思うなら、親指を立てることができます。疑問がある質問を見つけた場合、または理解できない場合は、コメントしてWeChatに追加してください。すべてを知れ。もちろん、みんなと友達になって、お互いに学びたいです。