赞
踩
现在想要往 ScopeProxyBeanDemo 类注入 ScopeProxyBean 类,并且要求注入的 ScopeProxyBean 是个 prototype 类型,ScopeProxyBean 类的方法 code() 中打印 this 对象的 hashCode(),验证是否是不一样的对象,代码如下:
然后调用 10 次看下打印的 10 次 hashCode(),发现是一样的,结果如下:
发现明明设置了 prototype 多例模式,注入的 ScopeProxyBean 竟然是同一个对象,导致这个问题的原因是,通过 @Autowired 注入其实只调用了一次 getBean() 赋值,所以这个值已经固定了,那么要解决这个问题,Spring 提供几个方法:
proxyMode = ScopedProxyMode.TARGET_CLASS
(其实底层本质也是自定义 TargetSource),如下所示:那么现在就重点来分析为什么这样可以解决上面出现的问题。
可以分为两大步骤:
从 ConfigurationClassPostProcessor 类的解析开始,源码如下:
进入 resolveScopeMetadata() 方法,源码如下:会在这里解析出 @Scope 配置的 proxyMode,然后封装到元数据 metadata
解析完了 @Scope 之后就要开始去为这个 beanClass 创建一份代理类的 BeanDefinition 定义了,回到上一层代码调用处:
进入 applyScopedProxyMode() 方法,注意哦这里只是在生成一份代理类的 ProxyBeanDefinition 定义,源码如下:
进入 createScopedProxy() 方法,源码如下:
总结一下 createScopedProxy() 方法把原始的 BeanDefinition 的 autowireCandidate 属性修改成了 false,因为改了这个值,所以在 @Autowired 属性注入的时候,就不会考虑到这个原生的 BeanDefinition 定义了
这里自己给造了一份新的这个 bean 的 BeanDefinition,可以理解为这个 bean 的代理 BeanDefinition,此时容器中已经存在两份 bean 的 BeanDefinition,只是原生的 BeanDefinition 已经不能参团了。
然后 BeanDefinition 中的 beanClass 被修改成了 ScopedProxyFactoryBean,这个类起到了关键性作用,既然图纸(ProxyBeanDefinition)造好了,是不是要去根据这份图纸造代理对象了。
那就看到这个类 ScopedProxyFactoryBean 就可以了,进入源码如下:可以看见是非常敏感的类 FactoryBean、和 BeanFactoryAware 。为什么敏感?FactoryBean 规范是通过里面提供的 getObject() 方法造对象的,BeanFactoryAware 规范在 Spring 实例化过程中会回调 BeanFactoryAware 类的 setBeanFactory() API。
所以重点关注这两个类的这两个方法。这样分析源码就非常顺畅,不会一头雾水。
所以直接看 getObject()、setBeanFactory() 这两个API,源码如下:可以看到 getObject() 方法直接返回了 proxy 对象,那么现在就只要去追踪这个 proxy 在哪里生成写入值就可以了。
刚好就是在 setBeanFactory() 方法中,源码如下:所以重点就看 setBeanFactory() 里面的逻辑了
这里重点看下 TargetSource 是谁?是 scopedTargetSource 变量,因为 TargetSource 有一个非常重要的方法: getObject() 会返回实例 bean,上篇文章中我们自己自定义的 TargetSource,就是模仿这里写的。源码如下:
同样的 getTarget() 方法也是直接去调用 getBean() 方法实例化 bean,但是需要注意,这里的 BeanFactory 不是深拷贝的,而是原原本本的 BeanFactory,如果这里还启动了切面功能,配置了注解 @EnableAspectJAutoProxy,@Aspect 该怎么样就是怎么样,不会影响这个功能。自定义的 TargetSource 就不一样了(会踢除 @Aspect 切面功能)
注意到没这里已经创建好了代理对象,所以 @Aspect 你觉得还回去创建一次代理对象么?答案是不会的,源码如下:
在后置处理器中有一个拦截操作,拦截如果实现了 AopInfrastructureBean 接口的类,源码如下:
因为在设置了 @Scope proxyMode=Target_Class 时,是采用的 BeanFactory 方式去获取实例的,刚好 ScopeProxyBean 类的外部 BeanFactory 就是 ScopedProxyFactoryBean 类,这个类刚好实现了 AopInfrastructureBean 接口,所以上面的拦截判断条件成立:
@Aspect 创建代理对象的 if 拦截,如下所示:所以在这一次 Spring 启动过程中不会再去创建一个新的代理对象
但是在 ScopedProxyFactoryBean 类调用 getObject() 方法的时候,会触发一次 getBean() 过程,这个时候会触发 @Aspect 生成代理对象。
okay,这里扯多了,在回到上面的 setBeanFactory() 方法中,源码如下:
这里注意两点:
1、scopedTargetSource 是直接内部 new 了一个对象
2、生成的代理对象直接赋值到 proxy 变量,getObject() 方法就是直接返回这个 proxy 对象的,源码如下:
至此整个解析 @Scope 和 创建 bean 代理过程已经完成了,接下来就是调用方法,然后触发 invoke() 方法,源码如下:
getTargetSource() 方法获取到的就是上面直接在内部 new 出来的 scopedTargetSource 对象,然后调用 getTarget() 方法就会触发 getBean() 流程返回实例 bean,因为配置的 prototype 模型,所以每次都会返回一个新的 bean,至此
在上面调用 10 次 for 循环就会打印出 10个不同的 hashCode 值。
配置 @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 的效果总体概括就是通过 Cglib 代理方式创建 bean
配置 @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 在 BeanDefinition 中的 beanClass 被偷梁换柱了,表面看起来注册的 beanClass 是 ScopeProxyBean,实则已经被换成了 ScopedProxyFactoryBean,并且该类还是一个 BeanFactory 类,那么 ScopeProxyBean 的实例就有该 BeanFactory 生产和管控了
此时容器中是存在两份该类的 BeanDefinition,一份是原始的 BeanDefinition、另一份是代理 BeanDefinition,原始 BeanDefinition 中的属性 autowireCandidate 被设置成 了 false ,不能参与注入
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。