曹工说Spring Boot源码(18)-- Spring AOP源码分析三部曲,终于快讲完了 (aop:config完整解析【下】) (4)

一个切点+一个切面方法 基本就等于一个Advisor对象。比如,一个before方法,算一个;在一个after,又算一个。

2.2 BeanPostProcessor的postProcessAfterInitialization

这是第二个时间点,在这里,检查bean要不要被拦截,生成代理。大家可以简单理解,因为每个bean的创建过程,都要被它处理,它呢,就会检查,这个bean,是不是匹配切点,如果匹配,就生成代理。

举个例子,以前看过一个小说,是京官去很偏远的地方上任,路上被人杀了,并被另外一个人拿了印章,到了地方,靠着印章,招摇撞骗,当了地方官。假设切点是:每个过路人;切面:偷天换日。那这个PostProcessor,就是那伙人,拦截每个过路人,并判断是不是那个倒霉的京官,如果是,就杀了并且换个人拿了印章去当地方官。

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { // 返回生成的代理对象 return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }

这里的下面这句,bean是原始bean,wrapIfNecessary返回的,就作为最终的bean。如果你不需要切这个bean,那你就返回它本身;如果要代理,你就返回另一个代理对象即可。

return wrapIfNecessary(bean, beanName, cacheKey);

我们仔细看这个方法内部:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (beanName != null && 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; } // 获取前面2.1章节里的Advisor对象,并保存到Object[] specificInterceptors 里 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != null) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // 创建代理,注意,Advisor数组,已经被传进去了。 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; }

大家看上面的注释吧,这里获取了ioc容器里的Advisor对象,如果advisor数组不为null,则创建代理,并返回代理。大家看下面的debug图就理解了。

曹工说Spring Boot源码(18)-- Spring AOP源码分析三部曲,终于快讲完了 (aop:config完整解析【下】)

创建代理的过程就简单了,基本就是:有接口就搞个jdk 动态代理,否则就cglib代理。

我做了个小测试,

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (bean.getClass().getName().equals("foo.Performer")) { return "hahha"; } ...其他代码省略,主要加了上面那个if return bean; } public final class Main { public static void main(String[] args) { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( "context-namespace-test-aop.xml"); Perform performer = (Perform) ctx.getBean(Perform.class); performer.sing(); } }

然后在倒数第二行,获取Perform类型的bean时,报错了。为啥呢?因为本来foo.Performer实现了Perform接口,但现在,我用一个string作为代理返回去了,所以就没有实现Perform接口的bean存在了

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [foo.Perform] is defined at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:296) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1196) at foo.Main.main(Main.java:23) 总结

spring aop这个东西,还是不简单,我本来打算这一讲把全部内容说清楚;但现在发现,创建代理这部分,还是得放到下一篇。

至于源码那些,在第16篇里给了地址的,大家可以看看。有啥问题,及时联系我。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypydw.html