Explicação detalhada de 3K palavras de como resolver completamente a dependência circular do Spring por meio do cache de três níveis

O conteúdo a seguir é baseado no Spring6.0.4.

Na verdade, essa é uma pergunta frequente em entrevistas. O irmão Song sempre quis falar sobre esse assunto com você em detalhes. Existem muitos artigos sobre esse assunto na Internet, mas sempre sinto que ainda é um pouco difícil de explicar esta questão claramente.Hoje Deixe-me tentar e ver se consigo resolver este problema com meus amigos.

Pretendo escrever alguns artigos sobre este tópico.Hoje, tentarei não falar sobre o código-fonte e apenas discutir com meus amigos como resolver dependências circulares do ponto de vista das ideias de design.

1. Dependências circulares

1.1 O que é uma dependência circular

Primeiro, o que é uma dependência circular? Na verdade, isso é fácil de entender, ou seja, dois beans dependem um do outro, semelhante ao seguinte:

 
 

Java

copiar código

@Service public class AService { @Autowired BService bService; } @Service public class BService { @Autowired AService aService; }

AService e BService dependem um do outro:

Isso deve ser bem compreendido.

1.2 Tipos de dependências circulares

De um modo geral, existem três formas diferentes de dependências circulares, e a seção 1.1 acima é uma delas.

As outras duas são as três dependências, conforme a figura abaixo:

Esse tipo de dependência circular geralmente fica profundamente oculto e não é fácil de detectar.

Há também a autossuficiência, conforme demonstrado abaixo:

De um modo geral, se houver dependências circulares em nosso código, isso significa que pode haver problemas no processo de design do nosso código e devemos tentar evitar a ocorrência de dependências circulares. No entanto, uma vez que ocorre uma dependência circular, o Spring irá lidar com isso por padrão.Claro, isso não significa que o código de dependência circular está bem. De fato, na versão mais recente do Spring, a dependência circular está habilitada adicionalmente.Se não houver nenhuma configuração adicional, um erro será relatado diretamente se a dependência circular ocorrer.

Além disso, o Spring não pode lidar com todas as dependências circulares, o Song Ge analisará com você mais tarde.

2. Ideias de solução de dependência circular

2.1 Soluções

Então, como resolver a dependência circular? Na verdade, é muito simples, basta adicionar um cache ao China Plus, vejamos a imagem a seguir:

Nós introduzimos um pool de cache aqui.

Quando precisarmos criar uma instância de AService, primeiro criaremos um AService original por meio de reflexão Java.Esse AService original pode ser entendido simplesmente como um AService que acabou de ser novo (na verdade acabou de ser criado por meio de reflexão) e não definiu nenhum atributo. , primeiro armazenamos este AService em um buffer pool.

Em seguida, precisamos definir valores para as propriedades de AService e, ao mesmo tempo, precisamos lidar com as dependências de AService. Neste momento, descobrimos que AService depende de BService, então criamos um objeto BService. Quando criamos o BService, descobrimos que o BService depende do AService, então, neste momento, primeiro retire o AService do buffer pool e use-o primeiro e, em seguida, continue o processo subsequente de criação do BService até que o BService seja criado e, em seguida, atribua-o ao AService , e então AService e BService são criados.

Alguns amigos podem dizer que o AService obtido pelo BService do buffer pool é um produto semi-acabado, não o verdadeiro AService final, mas amigos, devemos saber que nosso Java é passado por referência (também pode ser considerado como passado por valor , mas esse valor é o endereço de memória), o que o BService conseguiu naquele momento foi a referência de AService, para ser franco, era apenas um endereço de memória, e o AService foi encontrado de acordo com esse endereço, portanto, se o AService for criado depois, o AService obtido pelo BService está completo O AService tem.

Então o pool de cache mencionado acima tem um nome especial no contêiner Spring, que é chamado earlySingletonObjects. Sem passar pelo ciclo de vida completo, as propriedades do Bean podem não ter sido definidas e as dependências exigidas pelo Bean ainda não foram injetado. Os outros dois níveis de cache são:

  • singletonObjects: Este é o cache de primeiro nível. O cache de primeiro nível armazena todos os Beans que passaram por um ciclo de vida completo, ou seja, um Bean experimentou tudo, desde a criação, atribuição de atributos e execução de vários processadores. Armazenado em singletonObjects , quando precisarmos obter um Bean, iremos primeiro ao cache de primeiro nível para encontrá-lo e, quando não houver ninguém no cache de primeiro nível, consideraremos ir ao cache de segundo nível.
  • singletonFactory: Este é o cache de terceiro nível. No cache de primeiro nível e no cache de segundo nível, a chave do cache é beanName e o valor do cache é um objeto Bean, mas no cache de terceiro nível, o valor do cache é uma expressão Lambda, por meio do qual o destino pode ser criado Um objeto proxy para o objeto.

Alguns amigos podem achar estranho que, de acordo com a introdução acima, o cache de primeiro nível e o cache de segundo nível sejam suficientes para resolver a dependência circular, por que existe um cache de terceiro nível? Então você tem que considerar a situação do AOP!

2.2 O que fazer se houver POA

O que o irmão Song apresentou a você acima é a criação de beans comuns, então não há realmente nenhum problema. Mas há outro recurso muito importante no Spring, que é o AOP.

Posto isto, tenho de falar com os meus amigos sobre o processo de criação da AOP na Primavera.

Normalmente, primeiro obtemos uma instância de Bean por meio de reflexão e, em seguida, preenchemos os atributos para o Bean. Depois que os atributos são preenchidos, a próxima etapa é executar vários BeanPostProcessors. O artigo Qual é a diferença entre BeanFactoryPostProcessor e BeanPostProcessor? ) , se há um método neste bean que precisa ser proxy, então o sistema configurará automaticamente o pós-processador correspondente. Song Ge dá um exemplo simples, assumindo que eu tenho o seguinte serviço:

 
 

Java

copiar código

@Service public class UserService { @Async public void hello() { System.out.println("hello>>>"+Thread.currentThread().getName()); } }

Em seguida, o sistema fornecerá automaticamente um AsyncAnnotationBeanPostProcessorprocessador chamado , nesse processador, o sistema gerará um objeto UserService proxy e usará esse objeto para substituir o UserService original.

Então amigos, o que vocês precisam descobrir é que o UserService original e o recém-gerado proxy UserService são dois objetos diferentes, ocupando dois endereços de memória diferentes! ! !

Voltemos à imagem abaixo:

Se AService for gerar um objeto proxy no final, então o AService original é realmente armazenado no pool de cache, porque ainda não atingiu a etapa de processamento do AOP (primeiro você deve atribuir valores a cada atributo e em seguida, processamento AOP), isso Como resultado, o AService obtido por BService do pool de cache é o AService original. Depois que o BService é criado, a atribuição de atributo de AService é concluída. Então, no processo de criação subsequente de AService, AService torne-se um objeto proxy, não O AService no pool de cache é interrompido e, eventualmente, o AService do qual o BService depende não é o mesmo que o AService que finalmente foi criado.

Para resolver esse problema, o Spring apresenta um singletonFactory de cache de três níveis.

O mecanismo de trabalho de singletonFactory é o seguinte (supondo que AService seja um objeto proxy):

Quando criamos um AService, após o AService original ser criado por meio de reflexão, primeiro julgue se o Bean atual existe no cache de nível 1 atual, se não, então:

  1. Primeiro adicione um registro ao cache de terceiro nível. A chave do registro é o beanName do Bean atual e o valor é uma expressão Lambda ObjectFactory. Ao executar este Lambda, um objeto proxy pode ser gerado para o AService atual.
  2. Então, se o AService Bean atual existir no cache de segundo nível, remova-o.

Agora continue a atribuir valores aos atributos de AService. Acontece que AService precisa de BService e, em seguida, cria BService. Ao criar BService, descobre-se que BService precisa de AService, então primeiro vá para o cache de primeiro nível para descobrir se existe AService. Em caso afirmativo, basta usá-lo, se não, vá para o cache de segundo nível para descobrir se existe AService, se houver, use-o, se não, vá para o cache de terceiro nível para encontrar o ObjectFactory, e depois executar o método getObject aqui, este método está em processo de execução, ele irá julgar se um objeto proxy precisa ser gerado, se necessário gerar um objeto proxy e retornar, caso não precise gerar um objeto proxy, apenas retorne o objeto original. Por fim, armazene o objeto obtido no cache de segundo nível para o próximo uso e exclua os dados correspondentes no cache de terceiro nível. Desta forma, o BService do qual AService depende é criado.

Em seguida, continue a melhorar AService e execute vários pós-processadores. No momento, alguns pós-processadores desejam gerar objetos proxy para AService e descobrem que AService já é um objeto proxy, portanto, não há necessidade de gerá-lo e diretamente use o existente O objeto proxy pode ser usado em vez de AService.

Até agora, AService e BService estão concluídos.

Em essência, singletonFactory avança o processo AOP.

3. Resumo

Em geral, o Spring resolve dependências circulares apreendendo dois pontos-chave:

  • Exposição antecipada : Quando o objeto recém-criado não tiver nenhum valor atribuído, ele será exposto e colocado no cache para referência antecipada por outros beans (cache secundário).
  • AOP antecipadamente : quando A depende de B, verifique se ocorreu uma dependência circular (a maneira de verificar é marcar o A sendo criado, e então B precisa de A, e quando B cria A, descobre que A está sendo criado, o que significa que ocorreu uma dependência circular), se ocorrer uma dependência circular, o processamento AOP será executado antecipadamente e será usado após o processamento (cache de três níveis).

AbstractAutoProxyCreatorOriginalmente, o processo do AOP é processar o AOP ( ) por vários pós-processadores após os atributos receberem valores,

No entanto, deve-se notar que o cache de três níveis não pode resolver todas as dependências circulares. O irmão Song continuará a conversar com você em detalhes posteriormente neste artigo.

Bem, o artigo de hoje requer uma certa base de código-fonte Spring para entender

Acho que você gosta

Origin blog.csdn.net/wdj_yyds/article/details/131767195
Recomendado
Clasificación