@Lazy原理/源码解析,如何解决Spring不能解决的循环依赖

导读:本篇文章讲解 @Lazy原理/源码解析,如何解决Spring不能解决的循环依赖,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

@Lazy作用

作用

加在Bean上,指示是否要延迟初始化bean。
如加在@Autowired注入的属性上,可以用来解决Spring无法解决的循环依赖

解决问题举例

例如两个Bean循环依赖,并且使用了@Async等注解,在系统启动 bean生成过程中抛出循环依赖异常,可在其中一个bean注入时加上@Lazy

@Lazy使用

@Service
public class AServiceImpl implements AService {
    @Autowired
    private BService bService;
    
	@Async
	public void aMethod1(){
		...
	}
	@Async
	public void aMethod2(){
		bService.bMethod();
	}
}
@Service
public class BServiceImpl implements BService {
    @Autowired
    @Lazy
    private AService aService;
    
    public void bMethod(){
		aService.aMethod1();
	}
}

@Lazy原理

位置

org.springframework.context.annotation.Lazy

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lazy {

	/**
	 * Whether lazy initialization should occur.
	 */
	boolean value() default true;

}

被Spring源码使用处ContextAnnotationAutowireCandidateResolver

如下,其中ContextAnnotationAutowireCandidateResolver类
在这里插入图片描述
该类实现了两个接口,AutowireCandidateResolver是自动装配的策略接口,Aware->BeaFactoryAware接口说明此类参与了Spring启动流程并产生了影响
在这里插入图片描述

参与循环依赖解决getLazyResolutionProxyIfNecessary

			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);
			if (result == null) {
				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
			}
			return result;

在resolveDependency时,doResolveDependency之前,Spring会使用getLazyResolutionProxyIfNecessary尝试获取LazyResolutionProxy

	@Override
	@Nullable
	public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
		return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
	}

这段代码会在Spring生成bean的时候用到,打断点看一下调用栈,研究过SpringBoot启动流程、Spring Bean生成流程的同学比较容易理解,看到很多熟悉的方法,大致流程是项目启动-刷新应用上下文-获取bean-创建bean,然后在解决依赖的时候调用到getLazyResolutionProxyIfNecessary
在这里插入图片描述

这段代码也很好理解,先判断当前bean是否使用了@lazy

	protected boolean isLazy(DependencyDescriptor descriptor) {
		for (Annotation ann : descriptor.getAnnotations()) {
			Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
			if (lazy != null && lazy.value()) {
				return true;
			}
		}
		MethodParameter methodParam = descriptor.getMethodParameter();
		if (methodParam != null) {
			Method method = methodParam.getMethod();
			if (method == null || void.class == method.getReturnType()) {
				Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);
				if (lazy != null && lazy.value()) {
					return true;
				}
			}
		}
		return false;
	}

buildLazyResolutionProxy创建 “虚假的”代理

是的话执行buildLazyResolutionProxy,创建AService的lazy注解专用代理,此代理非最终注入BService的代理,当实际使用到AService时,会走getTarget()获取正确的代理对象

	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的匿名内部类和对象
		TargetSource ts = new TargetSource() {
			@Override
			public Class<?> getTargetClass() {
				return descriptor.getDependencyType();
			}
			@Override
			public boolean isStatic() {
				return false;
			}
			//此方法为关键方法,在实际使用到此bean的时候才去获取完整的Spring代理对象
			@Override
			public Object getTarget() {
				Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
				if (target == null) {
					Class<?> type = getTargetClass();
					if (Map.class == type) {
						return Collections.emptyMap();
					}
					else if (List.class == type) {
						return Collections.emptyList();
					}
					else if (Set.class == type || Collection.class == type) {
						return Collections.emptySet();
					}
					throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
							"Optional dependency not present for lazy injection point");
				}
				//运行中AService真正使用到BService时使用的完整代理
				return target;
			}
			@Override
			public void releaseTarget(Object target) {
			}
		};
		//创建lazy代理对象
		ProxyFactory pf = new ProxyFactory();
		//将ts设置到代理中
		pf.setTargetSource(ts);
		Class<?> dependencyType = descriptor.getDependencyType();
		if (dependencyType.isInterface()) {
			pf.addInterface(dependencyType);
		}
		//将AService的lazy代理返回给BService暂用
		return pf.getProxy(beanFactory.getBeanClassLoader());
	}

从getLazyResolutionProxyIfNecessary上一级方法中看出,返回Lazy代理时不会走doResolveDependency,也就是说,如果不加@Lazy,会进入解决循环依赖的
doResolveDependency方法中,如果Spring无法解决,就会抛出异常

			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);
			if (result == null) {
				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
			}
			return result;

总结

通过源码分析可以看出,@Lazy通过生成“假”代理对象的方式,阻止参与循环依赖的bean解决依赖。直接阻止了项目启动时有可能发生的循环依赖错误,而随后真正使用@Autowired注入的bean时,获取Spring容器中完整的代理bean

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

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/93728.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!