您现在的位置是:首页 >学无止境 >循环依赖完结篇-为什么我的spring还报循环依赖网站首页学无止境

循环依赖完结篇-为什么我的spring还报循环依赖

好z 2024-06-17 11:25:15
简介循环依赖完结篇-为什么我的spring还报循环依赖

系统参数只是解决了单例的注入

第二节我们说到,spring通过三级缓存解决了循环依赖,代码是这么写的

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		if (logger.isTraceEnabled()) {
			logger.trace("Eagerly caching bean '" + beanName +
					"' to allow for resolving potential circular references");
		}
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}

从中我们发现了一个很致命的问题,它只允许单例bean生成工厂,然后把工厂放入缓存。

实际上prototype类型的bean我们是不管理的,它根本不会放入缓存,每次调用getBean,都重新实例化,注入,初始化。

以下代码节选自getBean开始

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
	final String beanName = transformedBeanName(name);
	Object bean;

	// Eagerly check singleton cache for manually registered singletons.
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		if (logger.isTraceEnabled()) {
			if (isSingletonCurrentlyInCreation(beanName)) {
				logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
						"' that is not fully initialized yet - a consequence of a circular reference");
			}
			else {
				logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
			}
		}
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}

	else {
		// Fail if we're already creating this bean instance:
		// We're assumably within a circular reference.
		if (isPrototypeCurrentlyInCreation(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}

根据之前所说,它不会存入缓存,那就更加不可能从getSingleton中取出数据了,而是直接进入else分支,else分支第一句话就会判断是否已经存在当前beanName。而beanName早就在上次创建这种bean的时候注入进去了。在beforePrototypeCreation,完成了这个beanName的注入。
所以我们得出结论,多例循环直接报错。

else if (mbd.isPrototype()) {
	// It's a prototype -> create a new instance.
	Object prototypeInstance = null;
	try {
		beforePrototypeCreation(beanName);
		prototypeInstance = createBean(beanName, mbd, args);
	}
	finally {
		afterPrototypeCreation(beanName);
	}
	bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

我该怎么办

方法一

@postProcessor这个注解生效于完成依赖注入,初始化的postProcessBeforeInitialization阶段,这时候已经完成了依赖注入,所以巧妙避开了循环依赖

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
	// 会寻找到@postConstructor
	LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
	try {
		metadata.invokeInitMethods(bean, beanName);
	}
	catch (InvocationTargetException ex) {
		throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
	}
	catch (Throwable ex) {
		throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
	}
	return bean;
}

方法二

@Lazy这个注解
在一开始容器对于所有bean进行创建的时候,它会被过滤掉,以下就是创建过程,对于bd.isLazyInit()的bean,不会往下走

public void preInstantiateSingletons() throws BeansException {
	if (logger.isTraceEnabled()) {
		logger.trace("Pre-instantiating singletons in " + this);
	}
	
	// Iterate over a copy to allow for init methods which in turn register new bean definitions.
	// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
	
	// Trigger initialization of all non-lazy singleton beans...
	for (String beanName : beanNames) {
		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
		if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
			if (isFactoryBean(beanName)) {
				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
				if (bean instanceof FactoryBean) {
					final FactoryBean<?> factory = (FactoryBean<?>) bean;
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
										((SmartFactoryBean<?>) factory)::isEagerInit,
								getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
			}
			else {
				getBean(beanName);
			}
		}
	}

而在其它bean注入@Lazy的bean的时候,会将你的对象包装成动态代理对象,在调用这个对象方法的时候,就会被拦截,这时候才会真正开始注入该对象
以下是

@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");
	}
	return target;
}

最后展示一下CGLIB是怎样操作的,JDK也差不多

以下是CGLIB代理对象创建流程
Callback[] callbacks = getCallbacks(rootClass);中,我们创建了未来的拦截面

public Object getProxy(@Nullable ClassLoader classLoader) {


	try {
		Class<?> rootClass = this.advised.getTargetClass();


		Class<?> proxySuperClass = rootClass;
		if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
			proxySuperClass = rootClass.getSuperclass();
			Class<?>[] additionalInterfaces = rootClass.getInterfaces();
			for (Class<?> additionalInterface : additionalInterfaces) {
				this.advised.addInterface(additionalInterface);
			}
		}

		// Configure CGLIB Enhancer...
		Enhancer enhancer = createEnhancer();
		if (classLoader != null) {
			enhancer.setClassLoader(classLoader);
			if (classLoader instanceof SmartClassLoader &&
					((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
				enhancer.setUseCache(false);
			}
		}
		enhancer.setSuperclass(proxySuperClass);
		enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
		enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
		enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

		Callback[] callbacks = getCallbacks(rootClass);
		Class<?>[] types = new Class<?>[callbacks.length];
		for (int x = 0; x < types.length; x++) {
			types[x] = callbacks[x].getClass();
		}
		// fixedInterceptorMap only populated at this point, after getCallbacks call above
		enhancer.setCallbackFilter(new ProxyCallbackFilter(
				this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
		enhancer.setCallbackTypes(types);

		// Generate the proxy class and create a proxy instance.
		return createProxyClassAndInstance(enhancer, callbacks);
	}
}

以下是Callback[] callbacks = getCallbacks(rootClass);的部分流程,不难看出,这个DynamicAdvisedInterceptor是一定跑不掉的,而我们@Lazy重写的getTarget方法就会在其中发挥作用

boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();

// Choose an "aop" interceptor (used for AOP calls).
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

// Choose a "straight to target" interceptor. (used for calls that are
// unadvised but can return this). May be required to expose the proxy.
Callback targetInterceptor;
if (exposeProxy) {
	targetInterceptor = (isStatic ?
			new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
			new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
}
else {
	targetInterceptor = (isStatic ?
			new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
			new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
}

// Choose a "direct to target" dispatcher (used for
// unadvised calls to static targets that cannot return this).
Callback targetDispatcher = (isStatic ?
		new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());

Callback[] mainCallbacks = new Callback[] {
		aopInterceptor,  // for normal advice
		targetInterceptor,  // invoke target without considering advice, if optimized
		new SerializableNoOp(),  // no override for methods mapped to this
		targetDispatcher, this.advisedDispatcher,
		new EqualsInterceptor(this.advised),
		new HashCodeInterceptor(this.advised)
};

这是DynamicAdvisedInterceptor的intercept方法,
target = targetSource.getTarget();中,我们巧妙地在一般直接获取被代理对象的getTarget方法中,添加了doResolveDependency方法,从而完成了我们懒加载的目的

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
	Object oldProxy = null;
	boolean setProxyContext = false;
	Object target = null;
	TargetSource targetSource = this.advised.getTargetSource();
	try {
		if (this.advised.exposeProxy) {
			// Make invocation available if necessary.
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}
		// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
		target = targetSource.getTarget();
		Class<?> targetClass = (target != null ? target.getClass() : null);
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
		Object retVal;
		// Check whether we only have one InvokerInterceptor: that is,
		// no real advice, but just reflective invocation of the target.
		if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
			// We can skip creating a MethodInvocation: just invoke the target directly.
			// Note that the final invoker must be an InvokerInterceptor, so we know
			// it does nothing but a reflective operation on the target, and no hot
			// swapping or fancy proxying.
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			retVal = methodProxy.invoke(target, argsToUse);
		}
		else {
			// We need to create a method invocation...
			retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
		}
		retVal = processReturnType(proxy, target, method, retVal);
		return retVal;
	}
	finally {
		if (target != null && !targetSource.isStatic()) {
			targetSource.releaseTarget(target);
		}
		if (setProxyContext) {
			// Restore old proxy.
			AopContext.setCurrentProxy(oldProxy);
		}
	}
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。