Springbootソース分析のSpringサイクル依存の開示
11261 ワード
要約:
経験のあるプログラマーであれば、開発中に必ずこのような現象に遭遇したことがあります.これを言ったばかりか、ある子供たちは大いに驚いて色を失うかもしれません.
Spring循環依存フローチャート
Springサイクル依存発生原因は、エージェントの特性を持つBeanPostProcessor を使用している.典型的には事務注釈@Transaction、非同期注釈@Ayncなどの があります.
ソースの分析循環依存性が発生した場合、 がある.キャッシュ工場 を追加する.
AbstractAdvisoorAutoProxyCreatorとAync AnnotationBeanPostProcessorを比較します.
循環依存をどう解決しますか?ローディング順序を変更する は関連する @Lazy
allowRawInjection Despite Wrapping属性を利用して判断を強制的に変更します.
経験のあるプログラマーであれば、開発中に必ずこのような現象に遭遇したことがあります.これを言ったばかりか、ある子供たちは大いに驚いて色を失うかもしれません.
Spring
は循環依存問題を解決したのではないですか?どうしてまた循環依存が発生したのですか?次に、Spring
循環依存の最も本質的な原因を明らかにしましょう.Spring循環依存フローチャート
Springサイクル依存発生原因
ソースの分析
protected Object doCreateBean( ... ){
...
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
...
// populateBean , A , B~~
// B Bean( ), , A , A
// B a , Bean A getEarlyBeanReference() A ~~
// A getEarlyBeanReference() , , A , ,so B A ** **
// :@Async getEarlyBeanReference() , postProcessAfterInitialization
// @Async , ~~~( ~)
// : A B B ( B , )
// B A Bean A ( ) : exposedObject
populateBean(beanName, mbd, instanceWrapper);
// @Async Bean ~~~ :AsyncAnnotationBeanPostProcessor
// exposedObject
exposedObject = initializeBean(beanName, exposedObject, mbd);
...
// ~~~
if (earlySingletonExposure) {
// A B , A , earlySingletonReference A
// ( : A , earlySingletonReference =null return )
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// exposedObject @Aysnc , bean else
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// allowRawInjectionDespiteWrapping Bean Bean , ( )
// false , true , 。 ~~~
// dependentBeanMap Bean Bean Map~~~~
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// Bean A B,so ["b"]
String[] dependentBeans = getDependentBeans(beanName);
Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
// ~ B
// “b” removeSingletonIfCreatedForTypeCheckOnly false alreadyCreated B ~~~
// b , a B a A , ( )~~~
// so actualDependentBeans , A ~~~
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
// , ~~~
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
...
}
問題の簡略化Object earlySingletonReference = getSingleton(beanName, false);
は必ず値addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
は、インスタンスオブジェクトにSmartInstantiationAwareBeanPostProcessor
AbstractAutoProxyCreator
はSmartInstantiationAwareBeanPostProcessor
のサブクラスです.覚えておきます.SmartInstantiationAwareBeanPostProcessor
のサブクラスは大切です.exposedObject = initializeBean(beanName, exposedObject, mbd);
は、BeanPostProcessor
の後付け処理を行い、BeanPostProcessor
です.Spring
のサイクル依存性は、その3レベルのキャッシュによって容易に解決されたが、この2つの場所のバックヤード処理は、サイクル依存の問題をもたらしている.AbstractAdvisoorAutoProxyCreatorとAync AnnotationBeanPostProcessorを比較します.
SmartInstantiationAwareBeanPostProcessor
のサブクラスは、両方の後付け処理を実行しますので、前後は同じオブジェクト参照です.循環依存問題が発生しません.非同期コメントはできません.なぜですか?上の分析を自分で見て、よく見てください.循環依存をどう解決しますか?
@Lazy
注釈allowRawInjectionDespiteWrapping
は、true
(判断された文を利用して)に設定されているBeanPostProcessor
が設計した注釈を使わないでください.@Lazy
の一般的な意味は怠惰負荷であり、BeanDefinition.setLazyInit()
にしか作用しないということです.ここでは、遅延処理(代理処理)の能力を追加しました. // @since 4.0 , @Lazy AutowireCandidateResolver
public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {
// ,
// lazy proxy , @Autowired @Lazy = true
@Override
@Nullable
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
// isLazy=true , null
// @Lazy , ( @Lazy value false)
return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
}
// ,@Lazy (value true)
// @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;
}
// , ~~~
protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
"BeanFactory needs to be a DefaultListableBeanFactory");
// , DefaultListableBeanFactory.doResolveDependency() ~~~
final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
//TargetSource , AOP ,
// HotSwappableTargetSource、SingletonTargetSource、LazyInitTargetSource、
//SimpleBeanTargetSource、ThreadLocalTargetSource、PrototypeTargetSource
// , ~~~ getTarget , ( ~~~)
TargetSource ts = new TargetSource() {
@Override
public Class> getTargetClass() {
return descriptor.getDependencyType();
}
@Override
public boolean isStatic() {
return false;
}
// getTarget , , doResolveDependency
// , ~~~ @Lazy~~~
// , http、 ,
@Override
public Object getTarget() {
Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
if (target == null) {
Class> type = getTargetClass();
// ( null)
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;
}
@Override
public void releaseTarget(Object target) {
}
};
// ProxyFactory ts
// TargetSource, TargetSource
ProxyFactory pf = new ProxyFactory();
pf.setTargetSource(ts);
Class> dependencyType = descriptor.getDependencyType();
// private AInterface a; true
// ( , set ????)
if (dependencyType.isInterface()) {
pf.addInterface(dependencyType);
}
return pf.getProxy(beanFactory.getBeanClassLoader());
}
}
インジェクションが完了した@Lazy
の注釈が表示されている場合、最終的にはここで一時的に生成されたプロキシオブジェクトだけが注入され、本当に目的方法を実行した時にのみコンテナに入れられます.allowRawInjection Despite Wrapping属性を利用して判断を強制的に変更します.
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowRawInjectionDespiteWrapping(true);
}
}
このようにすると、容器の中にプロキシオブジェクトがあり、他の例に露出するのは元の引用です.循環依存内のbean
だけに影響を与えているので、影響範囲は全体的なものではないので、より良い方法が見つからない場合には、このような方法も悪くない.