软件编程
位置:首页>> 软件编程>> java编程>> Java Spring @Lazy延迟注入源码案例详解

Java Spring @Lazy延迟注入源码案例详解

作者:liangsheng_g  发布时间:2023-06-24 05:21:07 

标签:Java,Spring,@Lazy

前言

有时候我们会在属性注入的时候添加@Lazy注解实现延迟注入,今天咱们通过阅读源码来分析下原因

一、一个简单的小例子

代码如下:


@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会根据具体类型搞个cglib代理类
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}

很明显要执行getLazyResolutionProxyIfNecessary方法,如果加了@Lazy注解,最终会执行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。那么所谓的执行的过程中才进行获取真正的MyService对象是什么意思呢?

2. 使用逻辑

本文示例代码使用的是CGLIB代理,其实是类似的,因为注入的MyService是个CGLIB代理对象,那么在执行方法的时候,就会调用CglibAopProxy#DynamicAdvisedInterceptor#intercept方法

Java Spring @Lazy延迟注入源码案例详解

那么此处其实调用的就是上面的


Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);

这个方法就不用认真看了,主要功能就是从Spring容器中找到MyService。
在之前讲@Autowired原理和@Resource注入原理的时候解释过了,不清楚的可以看专栏里其他文章。
拿出来之后会发现,咱们拿到的target对象还是一个CGLIB代理的对象

Java Spring @Lazy延迟注入源码案例详解

那么当执行方法逻辑时

Java Spring @Lazy延迟注入源码案例详解

由于target是CGLIB对象,会再次进入到CglibAopProxy#DynamicAdvisedInterceptor#intercept方法。
此时拿到的target对象类型就不同了

Java Spring @Lazy延迟注入源码案例详解

是我们代理之前的target对象,此时再次进行invoke的时候,就会进行 * 的一般逻辑,先查找该方法匹配的所有advice,然后依次调用,最终调用target本身对于方法的执行。

来源:https://blog.csdn.net/liangsheng_g/article/details/119353666

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com