Java|Java Spring @Lazy延迟注入源码案例详解
前言
有时候我们会在属性注入的时候添加@Lazy注解实现延迟注入,今天咱们通过阅读源码来分析下原因
一、一个简单的小例子
代码如下:
@Servicepublic 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() {@Overridepublic Class> getTargetClass() {return descriptor.getDependencyType(); }@Overridepublic boolean isStatic() {return false; }@Overridepublic Object getTarget() {Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null); /**something valid**/return target; }@Overridepublic 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对象。
文章图片
【Java|Java Spring @Lazy延迟注入源码案例详解】到此这篇关于Java Spring @Lazy延迟注入源码案例详解的文章就介绍到这了,更多相关Java Spring @Lazy延迟注入源码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- Activiti(一)SpringBoot2集成Activiti6
- 事件代理
- SpringBoot调用公共模块的自定义注解失效的解决
- Java|Java OpenCV图像处理之SIFT角点检测详解
- 解决SpringBoot引用别的模块无法注入的问题
- java中如何实现重建二叉树
- 数组常用方法一
- 【Hadoop踩雷】Mac下安装Hadoop3以及Java版本问题
- 2018-07-09|2018-07-09 Spring 的DBCP,c3p0