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

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

前言

有时候我们会在属性注入的时候添加@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

标签:Java,Spring,@Lazy
0
投稿

猜你喜欢

  • Java实现的校验银行卡功能示例

    2022-01-28 19:33:53
  • C#实现的文件压缩和解压缩类

    2022-11-30 16:08:08
  • JDK源码之PriorityQueue解析

    2022-05-15 17:17:15
  • Android控件之SlidingDrawer(滑动式抽屉)详解与实例分享

    2023-07-27 09:26:18
  • SpringBoot实现配置文件的替换

    2023-11-21 22:27:16
  • C#判断一个矩阵是否为对称矩阵及反称矩阵的方法

    2023-04-04 10:55:23
  • java JUC信号量Semaphore原理及使用介绍

    2023-01-02 02:08:57
  • JAVA中String介绍及常见面试题小结

    2021-11-19 03:38:20
  • SpringMVC使用MultipartResolver实现文件上传

    2023-02-19 08:34:38
  • C#多线程系列之任务基础(一)

    2022-12-09 04:48:22
  • Java SpringBoot高级用法详解

    2021-07-03 18:27:09
  • 分析Android Choreographer源码

    2022-09-04 07:25:12
  • springboot如何重定向外部网页

    2022-11-12 05:19:19
  • Android编程实现的短信编辑器功能示例

    2022-09-08 12:58:44
  • Java 集合的Contains和Remove方法

    2022-06-17 05:29:10
  • C# ref and out的使用小结

    2021-12-19 22:35:40
  • Android中Edittext设置输入条件

    2023-03-11 23:05:08
  • 如何利用Jenkins + TFS为.Net Core实现持续集成/部署详解

    2022-11-12 06:14:32
  • rocketmq client 日志的问题处理方式

    2023-07-15 08:10:32
  • 聊聊Java并发中的Synchronized

    2022-07-26 03:19:24
  • asp之家 软件编程 m.aspxhome.com