spring @Lazy延遲注入的邏輯實現(xiàn)
前言
有時候我們會在屬性注入的時候添加@Lazy注解實現(xiàn)延遲注入,今天咱們通過閱讀源碼來分析下原因
一、一個簡單的小例子
代碼如下:
@Service
public class NormalService1 {
@Autowired
@Lazy
private MyService myService;
public void doSomething() {
myService.getName();
}
}
作用是為了進行延遲加載,在NormalService1進行屬性注入的時候,如果MyService還沒有生成bean也不用擔心,會注入一個代理,但是在實際運行的時候,會獲取Spring容器中實際的MyService,在某些情況下,因為spring生命周期的原因,這個注解有大用。
二、源碼解讀
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,懶加載,此時spring會根據(jù)具體類型搞個cglib代理類
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
很明顯要執(zhí)行g(shù)etLazyResolutionProxyIfNecessary方法,如果加了@Lazy注解,最終會執(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() {
@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);
/**
something valid
**/
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,然后再生成了一個代理(CGLIB或者JDK),然后作為MyService對象注入給了NormalService1。那么所謂的執(zhí)行的過程中才進行獲取真正的MyService對象是什么意思呢?
2. 使用邏輯
本文示例代碼使用的是CGLIB代理,其實是類似的,因為注入的MyService是個CGLIB代理對象,那么在執(zhí)行方法的時候,就會調(diào)用CglibAopProxy#DynamicAdvisedInterceptor#intercept方法

那么此處其實調(diào)用的就是上面的
Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
這個方法就不用認真看了,主要功能就是從Spring容器中找到MyService。
在之前講@Autowired原理和@Resource注入原理的時候解釋過了,不清楚的可以看專欄里其他文章。
拿出來之后會發(fā)現(xiàn),咱們拿到的target對象還是一個CGLIB增加的對象

那么當執(zhí)行方法邏輯時

由于target是CGLIB對象,會再次進入到CglibAopProxy#DynamicAdvisedInterceptor#intercept方法。
此時拿到的target對象類型就不同了

是我們代理之前的target對象,此時再次進行invoke的時候,就會進行動態(tài)代理的一般邏輯,先查找該方法匹配的所有advice,然后依次調(diào)用,最終調(diào)用target本身對于方法的執(zhí)行。
總結(jié)
所以可以發(fā)現(xiàn)其實@Lazy只不過是給spring的代理對象proxy再進行了一次proxy,只不過沒有在注入的時候,就獲取到對象,而是借用了方法invoke時通過proxy的intercept方法getTarget,然后進行方法調(diào)用,延遲了對象的注入。之后每次調(diào)用的時候都需要從Spring容器中獲取到原生的proxy對象。

到此這篇關(guān)于spring @Lazy延遲注入的邏輯實現(xiàn)的文章就介紹到這了,更多相關(guān)spring @Lazy延遲注入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis-Plus3.2.0 MetaObjectHandler 無法進行公共字段全局填充
這篇文章主要介紹了Mybatis-Plus3.2.0 MetaObjectHandler 無法進行公共字段全局填充,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-11-11
Mybatis-Plus?sum聚合函數(shù)及按日期查詢并求和的方式詳解
這篇文章主要介紹了Mybatis-Plus sum聚合函數(shù)及按日期查詢并求和,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-06-06
解決SpringBoot使用@Value獲取不到y(tǒng)aml中配置值的問題
在最近的開發(fā)中遇到一個問題,使用@Value獲取yml文件中配置的屬性時始終獲取不到值,所以本文給大家詳細介紹了SpringBoot使用@Value獲取不到y(tǒng)aml中值的問題分析及解決方法,需要的朋友可以參考下2024-01-01
Java SpringBoot微服務(wù)框架驗證碼報錯問題解決方案
這篇文章主要介紹了Java SpringBoot微服務(wù)框架驗證碼報錯問題解決方案,包括dockerfile容器操作和完整dockerfile,本文給大家介紹的非常詳細,需要的朋友可以參考下2024-08-08
java.net.UnknownHostException異常的一般原因及解決步驟
關(guān)于java.net.UnknownHostException大家也許都比較熟悉,這篇文章主要給大家介紹了關(guān)于java.net.UnknownHostException異常的一般原因及解決步驟,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-02-02
Mapper層繼承BaseMapper<T>需要引入的pom依賴方式
這篇文章主要介紹了Mapper層繼承BaseMapper<T>需要引入的pom依賴方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
java計算給定字符串中出現(xiàn)次數(shù)最多的字母和該字母出現(xiàn)次數(shù)的方法
這篇文章主要介紹了java計算給定字符串中出現(xiàn)次數(shù)最多的字母和該字母出現(xiàn)次數(shù)的方法,涉及java字符串的遍歷、轉(zhuǎn)換及運算相關(guān)操作技巧,需要的朋友可以參考下2017-02-02

