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

网友投稿 620 2022-12-21

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

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

目录前言一、一个简单的小例子二、源码解读1. 注入2. 使用逻辑总结

前言

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

一、一个简单的小例子

代码如下:

@Service

public class NormalService1 {

@Autowired

@Lazy

private MyService myService;

public void doSomething() {

myService.getName();

}

}

YwMHBycAC

作用是为了进行延迟加载,在NormalService1进行属性注入的时候,如果MyService还没有生成bean也不用担心,会注入一个代理,但是在实际运行的时候,会获取Spring容器中实际的MyService,在某些情况下,因为spring生命周期的原因,这个注解有大用。

二、源码解读

1. 注入

代码如下(DefaultLishttp://tableBeanFactory#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对象。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:微信小程序开发使用vue(微信小程序开发使用MySQL)
下一篇:扬州车载物联网卡报价(扬州车载物联网卡报价查询)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~