O que é dependência circular?
Como o nome indica, a dependência circular significa que A depende de B e B depende de A. A relação de dependência entre os dois forma um círculo, que geralmente é causado por codificação incorreta. Spring só pode resolver o problema da dependência circular do atributo, mas não pode resolver o problema da dependência circular do construtor, porque esse problema não tem solução.
Em seguida, primeiro escrevemos um Demo para demonstrar como o Spring lida com o problema de dependências circulares de atributos.
Falar é fácil. Me mostre o código
A primeira etapa: definir uma classe ComponentA, que possui uma propriedade privada componentB.
package com.tech.ioc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author 君战
* **/
@Component
public class ComponentA {
@Autowired
private ComponentB componentB;
public void say(){
componentB.say();
}
}
Etapa 2: Defina uma classe ComponentB, que depende do ComponentA. E defina um método para facilitar a impressão de dados.
package com.tech.ioc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author 君战
* **/
@Component
public class ComponentB {
@Autowired
private ComponentA componentA;
public void say(){
System.out.println("componentA field " + componentA);
System.out.println(this.getClass().getName() + " -----> say()");
}
}
A terceira etapa: concentre-se, escreva uma classe-SimpleContainer, imitando a manipulação subjacente do Spring de dependências circulares. Se você entender esse código, será muito simples examinar a lógica do Spring para lidar com dependências circulares.
package com.tech.ioc;
import java.beans.Introspector;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 演示Spring中循环依赖是如何处理的,只是个简版,真实的Spring依赖处理远比这个复杂。
* 但大体思路都相同。另外这个Demo很多情况都未考虑,例如线程安全问题,仅供参考。
* @author 君战
*
* **/
public class SimpleContainer {
/***
* 用于存放完全初始化好的Bean,Bean处于可状态
* 这个Map定义和Spring中一级缓存命名一致
* */
private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
/***
* 用于存放刚创建出来的Bean,其属性还没有处理,因此存放在该缓存中的Bean还不可用。
* 这个Map定义和Spring中三级缓存命名一致
* */
private final Map<String, Object> singletonFactories = new HashMap<>(16);
public static void main(String[] args) {
SimpleContainer container = new SimpleContainer();
ComponentA componentA = container.getBean(ComponentA.class);
componentA.say();
}
public <T> T getBean(Class<T> beanClass) {
String beanName = this.getBeanName(beanClass);
// 首先根据beanName从缓存中获取Bean实例
Object bean = this.getSingleton(beanName);
if (bean == null) {
// 如果未获取到Bean实例,则创建Bean实例
return createBean(beanClass, beanName);
}
return (T) bean;
}
/***
* 从一级缓存和二级缓存中根据beanName来获取Bean实例,可能为空
* */
private Object getSingleton(String beanName) {
// 首先尝试从一级缓存中获取
Object instance = singletonObjects.get(beanName);
if (instance == null) { // Spring 之所以能解决循环依赖问题,也是靠着这个singletonFactories
instance = singletonFactories.get(beanName);
}
return instance;
}
/***
* 创建指定Class的实例,返回完全状态的Bean(属性可用)
*
* */
private <T> T createBean(Class<T> beanClass, String beanName) {
try {
Constructor<T> constructor = beanClass.getDeclaredConstructor();
T instance = constructor.newInstance();
// 先将刚创建好的实例存放到三级缓存中,如果没有这一步,Spring 也无法解决三级缓存
singletonFactories.put(beanName, instance);
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
Class<?> fieldType = field.getType();
field.setAccessible(true);
// 精髓是这里又调用了getBean方法,例如正在处理ComponentA.componentB属性,
// 执行到这里时就会去实例化ComponentB。因为在getBean方法首先去查缓存,
// 而一级缓存和三级缓存中没有ComponentB实例数据,所以又会调用到当前方法,
// 而在处理ComponentB.componentA属性时,又去调用getBean方法去缓存中查找,
// 因为在前面我们将ComponentA实例放入到了三级缓存,因此可以找到。
// 所以ComponentB的实例化结束,方法出栈,返回到实例化ComponentA的方法栈中,
// 这时ComponentB已经初始化完成,因此ComponentA.componentB属性赋值成功!
field.set(instance, this.getBean(fieldType));
}
// 最后再将初始化好的Bean设置到一级缓存中。
singletonObjects.put(beanName, instance);
return instance;
} catch (Exception e) {
e.printStackTrace();
}
throw new IllegalArgumentException();
}
/**
* 将类名小写作为beanName,Spring底层实现和这个差不多,也是使用javaBeans的
* {@linkplain Introspector#decapitalize(String)}
**/
private String getBeanName(Class<?> clazz) {
String clazzName = clazz.getName();
int index = clazzName.lastIndexOf(".");
String className = clazzName.substring(index);
return Introspector.decapitalize(className);
}
}
Se você leu e entendeu o código acima, faremos uma análise real do código-fonte do Spring para o processamento de dependências circulares.Eu acredito que será fácil de ler novamente.
Análise do código-fonte subjacente
A análise começa com o método doGetBean de AbstractBeanFactory. Pode-se ver que o transformBeanName é chamado primeiro neste método (na verdade para lidar com o problema BeanName), que é o mesmo que o método getBeanName que escrevemos, mas a consideração do Spring é muito mais complicada do que isso por causa do FactoryBean e do alias problemas.
// AbstractBeanFactory#doGetBean
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object bean;
// !!!重点是这里,首先从缓存中beanName来获取对应的Bean。
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 执行到这里说明缓存中存在指定beanName的Bean实例,getObjectForBeanInstance是用来处理获取到的Bean是FactoryBean问题
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
else {
try {
// 删除与本次分析无关代码....
// 如果是单例Bean,则通过调用createBean方法进行创建
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
return (T) bean;
}
O método getSingleton tem um método sobrecarregado. Aqui, o método getSingleton sobrecarregado é chamado. Observe que o valor do parâmetro booleano transmitido aqui é verdadeiro, porque esse valor determina se o Bean anterior pode ser exposto.
// DefaultSingletonBeanRegistry#getSingleton
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
// DefaultSingletonBeanRegistry#getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 首先从一级缓存中获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 如果一级缓存中未获取到,再从二级缓存中获取
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果未从二级缓存中获取到并且allowEarlyReference值为true(前面传的为true)
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
//Double Check
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 最后尝试去三级缓存中获取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 保存到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 从三级缓存中移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
Ok, depois de ler como o Spring obtém a instância do Bean do cache, veja como o método creatBean cria o Bean
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 删除与本次分析无关的代码...
try {// createBean方法底层是通过调用doCreateBean来完成Bean创建的。
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
} catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
// AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 创建Bean实例
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
// 如果允许当前Bean早期曝光。只要Bean是单例的并且allowCircularReferences 属性为true(默认为true)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 这里调用了addSingletonFactory方法将刚创建好的Bean保存到了三级缓存中。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 删除与本次分析无关的代码.....
Object exposedObject = bean;
try {// Bean属性填充
populateBean(beanName, mbd, instanceWrapper);
// 初始化Bean,熟知的Aware接口、InitializingBean接口.....都是在这里调用
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {
}
// 删除与本次分析无关的代码.....
return exposedObject;
}
Primeiro analise o método addSingletonFactory, porque neste método o Bean é armazenado no cache de terceiro nível.
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
// 如果一级缓存中不存在指定beanName的key
if (!this.singletonObjects.containsKey(beanName)) {
// 将刚创建好的Bean示例保存到三级缓存中
this.singletonFactories.put(beanName, singletonFactory);
// 从二级缓存中移除。
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
A injeção de dependência do processamento Bean é concluída pelo método populateBean, mas o link de execução inteiro é muito longo, então não vou discutir isso aqui. Vou apenas falar sobre como o contêiner IoC chama o método getBean passo a passo ao processar dependências A lógica que escrevemos para lidar com a injeção de campo está correta.
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// 删除与本次分析无关代码...
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
// 遍历所有已注册的BeanPostProcessor接口实现类,如果实现类是InstantiationAwareBeanPostProcessor接口类型的,调用其postProcessProperties方法。
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
// 删除与本次分析无关代码...
pvs = pvsToUse;
}
}
// 删除与本次分析无关代码...
}
}
No Spring, a anotação @Autowired é processada pela classe AutowiredAnnotationBeanPostProcessor e a anotação @Resource é processada pela classe CommonAnnotationBeanPostProcessor. Ambas as classes implementam a interface InstantiationAwareBeanPostProcessor, e ambas completam a injeção de dependência no método postProcessProperties substituído. Aqui, analisamos o processamento de anotações @Autowired.
// AutowiredAnnotationBeanPostProcessor#postProcessProperties
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 根据beanName以及bean的class去查找Bean的依赖元数据-InjectionMetadata
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {// 调用inject方法
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
No método de injeção de InjectionMetadata, obtenha todos os elementos dependentes (InjectedElement) que precisam ser processados pelo Bean atual, que é uma coleção, percorra a coleção e chame o método inject de cada elemento de injeção de dependência.
// InjectionMetadata#inject
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 获取当前Bean所有的依赖注入元素(可能是方法,也可能是字段)
Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
// 如果当前Bean的依赖注入项不为空,遍历该依赖注入元素
for (InjectedElement element : elementsToIterate) {
// 调用每一个依赖注入元素的inject方法。
element.inject(target, beanName, pvs);
}
}
}
Duas classes internas são definidas na classe AutowiredAnnotationBeanPostProcessor-AutowiredFieldElement e AutowiredMethodElement herdam de InjectedElement, que correspondem à injeção de campo e injeção de método respectivamente.
Pegue a injeção de campo comumente usada como exemplo. No método inject de AutowiredFieldElement, primeiro determine se o campo atual foi processado. Se ele foi processado, vá diretamente para o cache, caso contrário, chame o método resolveDependency de BeanFactory para processar a dependência .
// AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {// 如果当前字段已经被处理过,直接从缓存中获取
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
} else {
// 构建依赖描述符
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {// 调用BeanFactory的resolveDependency来解析依赖
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
} catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
// 删除与本次分析无关代码....
}
if (value != null) {
// 通过反射来对属性进行赋值
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
}
O método resolveDependency implementado em DefaultListableBeanFactory finalmente chama o método doResolveDependency para completar a função de resolução de dependência. No código-fonte do Spring, se houver algum método de do, esse método é a maneira real de trabalhar.
// DefaultListableBeanFactory#resolveDependency
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// .....
// 如果在字段(方法)上添加了@Lazy注解,那么在这里将不会真正的去解析依赖
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
// 如果添加@Lazy注解,那么则调用doResolveDependency方法来解析依赖
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
// DefaultListableBeanFactory#doResolveDependency
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
//.....
try {
// 根据名称以及类型查找合适的依赖
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {// 如果未找到相关依赖
if (isRequired(descriptor)) { // 如果该依赖是必须的(@Aautowired的required属性),直接抛出异常
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
// 如果查找到的依赖多于一个,例如某个接口存在多个实现类,并且多个实现类都注册到IoC容器中。
if (matchingBeans.size() > 1) {// 决定使用哪一个实现类,@Primary等方式都是在这里完成
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
} else {
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
} else {
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
// 如果查找到的依赖是某个类的Class(通常如此),而不是实例,
//调用描述符的方法来根据类型resolveCandidate方法来获取该类型的实例。
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
//...
}
No método resolveCandidate do descritor de dependência, a instância getBean é obtida chamando o método getBean do BeanFactory.
// DependencyDescriptor#resolveCandidate
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {
return beanFactory.getBean(beanName);
}
Na implementação do método getBean, ainda é feito chamando o método doGetBean. Isso também é basicamente o mesmo que o processamento de dependência que escrevemos, mas nós mesmos escrevemos é relativamente simples, e o Spring tem que considerar e processar cenários complexos, então o código é mais complicado, mas a ideia geral é a mesma.
// AbstractBeanFactory#getBean
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
O ponto chave é a demonstração que escrevemos para lidar com dependências circulares.Se você entender esse código e olhar para o tratamento de dependência circular do Spring, você vai achar que é muito simples.
Resumindo
Dependência circular refere-se à relação de referência mútua entre dois Beans. Por exemplo, A depende de B e B depende de A, mas Spring só pode resolver a dependência circular de atributos, não a dependência circular do construtor, e este cenário não pode ser resolvido .
A chave para a solução do Spring para dependências circulares é primeiro armazenar o Bean no cache de terceiro nível ao lidar com as dependências de atributos do Bean. Quando houver uma dependência circular, obtenha o Bean relevante do cache de terceiro nível e, em seguida, remova-o do cache de terceiro nível. Armazenado no cache secundário.
Entenda, na verdade, a dependência circular é tão simples quanto isso e, finalmente, clique aqui para receber o pacote de presente da arquitetura Java! ! !