==> 學習彙總(持續更新)
==> 從零搭建後端基礎設施系列(一)-- 背景介紹
@Lazy
註解爲什麼會失效?它並沒有失效,一直都是生效着的,之所以認爲它失效了,是沒有用對它,沒有理解它!
不想看分析的,可以直接飛到總結
我想讓B最後再實例化,因爲實例化的時候,會爲B創建代理,並且加入增強器。但是有些情況,實例化其它類的時候,某個增強器還未生成,這時候其它類又使用到了B,導致B在增強器之前實例化了,最後B就加入不了增強器了。下面2個使用@Lazy的CASE,都會發生什麼?B又能不能在最後再實例化?來個圖可能會更清楚我提出的問題。其實問題來源自【追根究底】 爲什麼@Transactional註解失效了?,從這個問題,引起我對@Lazy
原理的探究。
以下所有CASE的測試入口,都是這個
@SpringBootTest
class LazyDemoApplicationTests {
@Autowired
A a;
@Test
void contextLoads() {
a.sayA();
}
}
-
CASE1
B會在A之後實例化嗎?@Component public class A { @Autowired B b; public void sayA(){ b.sayB(); } } @Lazy @Component public class B { public void sayB(){} }
答案是會
- CASE2
B會在A初始化的時候實例化嗎?
答案是不會的@Component public class A { @Lazy @Autowired B b; public void sayA(){ b.sayB(); } } @Component public class B { public void sayB(){} }
- CASE3
@Component public class A { @Lazy @Autowired B b; public void sayA(){ b.sayB(); } } @Lazy @Component public class B { public void sayB(){} }
-
CASE1分析
首先,以菜鳥的思維先揣測一下,因爲B類加了@Lazy
,所以必定會晚於A實例化,就算A裏用了B。
好,我們跟進源碼一看便知!
第一階段線路,refreshContext
->refresh
->finishBeanFactoryInitialization
->preInstantiateSingletons
@Override public void preInstantiateSingletons() throws BeansException { …… // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); …… } else { getBean(beanName); } } } …… }
看英文註釋,也可以知道,就是從這裏開始實例化所有非懶加載的類的。
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit())
條件表示,非抽象類並且是單例,而且不是非懶加載的bean,就getBean
,否則啥也不做。所以可以判斷出,B是不會在這一步實例化的。那麼我接着往下看A的實例化會發生什麼。
第二階段線路,getBean
->doGetBean
->createBean
->doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } …… // Initialize the bean instance. Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { …… } …… return exposedObject; }
看註釋也能看出,第一步先實例化
bean A
,然後初始化它,初始化的時候,需要爲它注入B。由此,引出第三階段路線。
第三階段線路,populateBean
->postProcessProperties -- AutowiredAnnotationBeanPostProcessor
->inject -- AutowiredFieldElement
->resolveDependency
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { …… else { Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); if (result == null) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } }
從截出來的代碼可以看出,獲取xxx,獲取不到,就想另一種辦法獲取xxx。接着看此次的重點
getLazyResolutionProxyIfNecessary
方法public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) { return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null); }
它會先判斷這個bean是否標記了
@Lazy
protected boolean isLazy(DependencyDescriptor descriptor) { for (Annotation ann : descriptor.getAnnotations()) { Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class); if (lazy != null && lazy.value()) { return true; } } MethodParameter methodParam = descriptor.getMethodParameter(); if (methodParam != null) { Method method = methodParam.getMethod(); if (method == null || void.class == method.getReturnType()) { Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class); if (lazy != null && lazy.value()) { return true; } } } return false; }
CASE1中,A裏面的B並沒有標記
@Lazy
,所以不會走這一步,直接返回null。然後通過doResolveDependency
這個方法去實例化B。
所以CASE1,B在A之前實例化了。 -
CASE2分析
首先,以菜鳥的思維先揣測一下,因爲A中的B加了@Lazy
,所以必定會晚於A實例化,就B類沒有標@Lazy
。
由CASE1的分析得出,必定會走buildLazyResolutionProxy
這個方法,看這個方法的名字,似乎是要爲B創建一個代理?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() { @Override public Class<?> getTargetClass() { return descriptor.getDependencyType(); } @Override public boolean isStatic() { return false; } @Override public Object getTarget() { Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null); if (target == null) { Class<?> type = getTargetClass(); if (Map.class == type) { return Collections.emptyMap(); } else if (List.class == type) { return Collections.emptyList(); } else if (Set.class == type || Collection.class == type) { return Collections.emptySet(); } throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(), "Optional dependency not present for lazy injection point"); } return target; } @Override public 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()); }
接下來看看
TargetSource
是這個什麼東西,但是現在又說不清楚,等後面說,所以我們跳過這一大段,看最後的,很明顯,爲B創建一個代
理,記住,這裏並沒有走bean的實例化。好了,A就算初始化完了,接下來該到B了,看圖debug!
跟進去
嘗試從緩存中拿B的單例,如果存在,就會直接返回,如果不存在,就走createBean
創建。
返回是null,也再次說明了,剛纔B並沒有被創建,只是創建了個代理對象而已!所以CASE2,B在A之後實例化。
接下來看看哪裏會用到TargetSource
,跟着調試一下
接着,單步跟進去,可以看到,b確實是一個代理對象
再單步跟進去,這裏拿到的就是剛纔那個TargetSource
,然後調用getTarget
的時候,會走到剛纔那裏。
再單步跟進去,beanFactory.doResolveDependency
接着就會嘗試去創建B(有興趣的同學可以一步步跟到這裏,因爲鏈路太長,所以就不一一截圖了)
但是,發現B已經創建好了,直接從緩存中拿到了
可以看到,確實返回了B的實例
經過上面的分析,可以知道,不管B在A實例化之前有沒有創建,A中的B都是一個代理對象,不是真實實例,只有當使用到了纔會去創建或者拿到已經存在的實例! -
CASE3分析
由CASE2分析得知,B只有在使用的時候纔會被實例化。並且和CASE2不同的是,由於類上也加了@Lazy
,所以在preInstantiateSingletons
中不會進入if中。 -
總結
@Lazy
註解永遠遵循一條規則,如果被@Lazy
標記的bean,只要使用到了,就會被實例化。這和@Lazy
想要表達的並不矛盾,只是和你使用的方式產生矛盾,你想人家最後才加載,但是你又在別的bean加載的時候,使用到了這個懶加載的bean,那它還不得乖乖加載?不然程序就起不來了。- 如果是某個bean中某個字段被標記爲
@Lazy
,那麼初始化這個bean的時候,會爲這個字段創建一個代理,這僅僅只是一個代理,並沒有走bean的創建過程。 @Lazy
處理的情況只有兩種,第一種是在類上直接標記,那麼在preInstantiateSingletons
方法中就不會去getBean
,也就是直接跳過了。第二種就是其它標記(屬性、方法、參數),那麼會爲它創建一個代理對象,並無並且將真真正實例化bean放在了TargetSource
中的getTarget
方法中。- 最後,回到標題,
@Lazy
註解爲什麼會失效?它並沒有失效,一直都是生效着的,之所以認爲它失效了,是沒有用對它,沒有理解它!