==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍
@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
注解为什么会失效?它并没有失效,一直都是生效着的,之所以认为它失效了,是没有用对它,没有理解它!