微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

spring @Lazy延迟注入的逻辑实现

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

目录

一、一个简单的小例子

二、源码解读

1. 注入

2. 使用逻辑

总结

前言

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

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

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

这个方法就不用认真看了,主要功能就是从spring容器中找到MyService。

在之前讲@Autowired原理和@Resource注入原理的时候解释过了,不清楚的可以看专栏里其他文章

拿出来之后会发现,咱们拿到的target对象还是一个cglib增加的对象

那么当执行方法逻辑时

由于target是cglib对象,会再次进入到cglibAopProxy#DynamicAdvisedInterceptor#intercept方法

此时拿到的target对象类型就不同了

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

总结

所以可以发现其实@Lazy只不过是给spring的代理对象proxy再进行了一次proxy,只不过没有在注入的时候,就获取到对象,而是借用了方法invoke时通过proxy的intercept方法getTarget,然后进行方法调用,延迟了对象的注入。之后每次调用的时候都需要从spring容器获取到原生的proxy对象。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐