spring @Lazy延遲注入的邏輯實(shí)現(xiàn)
有時(shí)候我們會(huì)在屬性注入的時(shí)候添加@Lazy注解實(shí)現(xiàn)延遲注入,今天咱們通過(guò)閱讀源碼來(lái)分析下原因
一、一個(gè)簡(jiǎn)單的小例子代碼如下:
@Servicepublic class NormalService1 {@Autowired@Lazyprivate MyService myService;public void doSomething() {myService.getName();}}
作用是為了進(jìn)行延遲加載,在NormalService1進(jìn)行屬性注入的時(shí)候,如果MyService還沒(méi)有生成bean也不用擔(dān)心,會(huì)注入一個(gè)代理,但是在實(shí)際運(yùn)行的時(shí)候,會(huì)獲取Spring容器中實(shí)際的MyService,在某些情況下,因?yàn)閟pring生命周期的原因,這個(gè)注解有大用。
二、源碼解讀1. 注入代碼如下(DefaultListableBeanFactory#resolveDependency):
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());if (Optional.class == descriptor.getDependencyType()) {return createOptionalDependency(descriptor, requestingBeanName);}else if (ObjectFactory.class == descriptor.getDependencyType() ||ObjectProvider.class == descriptor.getDependencyType()) {return new DependencyObjectProvider(descriptor, requestingBeanName);}else if (javaxInjectProviderClass == descriptor.getDependencyType()) {return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);}else {//如果注入屬性添加了@Lazy,懶加載,此時(shí)spring會(huì)根據(jù)具體類型搞個(gè)cglib代理類Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);if (result == null) {result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);}return result;}}
很明顯要執(zhí)行g(shù)etLazyResolutionProxyIfNecessary方法,如果加了@Lazy注解,最終會(huì)執(zhí)行buildLazyResolutionProxy方法
protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,'BeanFactory needs to be a DefaultListableBeanFactory');final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();TargetSource ts = new TargetSource() {@Overridepublic Class<?> getTargetClass() {return descriptor.getDependencyType();}@Overridepublic boolean isStatic() {return false;}@Overridepublic Object getTarget() {Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);/**something valid**/return target;}@Overridepublic void releaseTarget(Object target) {}};ProxyFactory pf = new ProxyFactory();pf.setTargetSource(ts);Class<?> dependencyType = descriptor.getDependencyType();if (dependencyType.isInterface()) {pf.addInterface(dependencyType);}return pf.getProxy(beanFactory.getBeanClassLoader());}
可以看到上面這段代碼,其實(shí)就是生成了一個(gè)TargetSource,然后再生成了一個(gè)代理(CGLIB或者JDK),然后作為MyService對(duì)象注入給了NormalService1。那么所謂的執(zhí)行的過(guò)程中才進(jìn)行獲取真正的MyService對(duì)象是什么意思呢?
2. 使用邏輯本文示例代碼使用的是CGLIB代理,其實(shí)是類似的,因?yàn)樽⑷氲腗yService是個(gè)CGLIB代理對(duì)象,那么在執(zhí)行方法的時(shí)候,就會(huì)調(diào)用CglibAopProxy#DynamicAdvisedInterceptor#intercept方法
那么此處其實(shí)調(diào)用的就是上面的
Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
這個(gè)方法就不用認(rèn)真看了,主要功能就是從Spring容器中找到MyService。在之前講@Autowired原理和@Resource注入原理的時(shí)候解釋過(guò)了,不清楚的可以看專欄里其他文章。拿出來(lái)之后會(huì)發(fā)現(xiàn),咱們拿到的target對(duì)象還是一個(gè)CGLIB增加的對(duì)象
那么當(dāng)執(zhí)行方法邏輯時(shí)
由于target是CGLIB對(duì)象,會(huì)再次進(jìn)入到CglibAopProxy#DynamicAdvisedInterceptor#intercept方法。此時(shí)拿到的target對(duì)象類型就不同了
是我們代理之前的target對(duì)象,此時(shí)再次進(jìn)行invoke的時(shí)候,就會(huì)進(jìn)行動(dòng)態(tài)代理的一般邏輯,先查找該方法匹配的所有advice,然后依次調(diào)用,最終調(diào)用target本身對(duì)于方法的執(zhí)行。
總結(jié)所以可以發(fā)現(xiàn)其實(shí)@Lazy只不過(guò)是給spring的代理對(duì)象proxy再進(jìn)行了一次proxy,只不過(guò)沒(méi)有在注入的時(shí)候,就獲取到對(duì)象,而是借用了方法invoke時(shí)通過(guò)proxy的intercept方法getTarget,然后進(jìn)行方法調(diào)用,延遲了對(duì)象的注入。之后每次調(diào)用的時(shí)候都需要從Spring容器中獲取到原生的proxy對(duì)象。
到此這篇關(guān)于spring @Lazy延遲注入的邏輯實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)spring @Lazy延遲注入內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. Jsp servlet驗(yàn)證碼工具類分享2. ASP.NET MVC實(shí)現(xiàn)區(qū)域或城市選擇3. idea設(shè)置提示不區(qū)分大小寫的方法4. 一個(gè)用xslt樣式將xml解析為xhtml的類TransformBinder(兼容FF和IE7.0)5. 解決docker與vmware的沖突問(wèn)題6. 深入理解java虛擬機(jī)-第12章Java內(nèi)存模型與線程7. Python 實(shí)現(xiàn)定積分與二重定積分的操作8. .net core 3.1 Redis安裝和簡(jiǎn)單使用9. XML入門問(wèn)答10. ASP基礎(chǔ)入門第九篇(Global.asa文件的使用)
