Prefácio
Após o artigo anterior, descriptografia da primavera - a análise XML e o registro de feijão, vamos continuar analisando o código -fonte. Sem mais delongas, vamos dar uma olhada na introdução detalhada juntos.
Descriptografia
Existem duas categorias principais de declarações na configuração XML da Spring. Um é o padrão, como <bean id="person"/> , e o outro é o personalizado, como <tx:annotation-driven /> . As duas tags têm métodos de análise muito diferentes. O método ParsebeandEfinitions é usado para distinguir os métodos parse usados por diferentes tags. Obtenha o espaço para nome através node.getNamespaceURI() , determine se é um espaço de nome padrão ou um espaço de nome personalizado e compare -o com o espaço de nome fixo http://www.springframework.org/schema/beans na primavera. Se for consistente, use parseDefaultElement(ele, delegate); Caso contrário, é delegate.parseCustomElement(ele);
Parsing de tags padrão
O ParsedefaultElement fez um processamento diferente para 4 tags diferentes, alias, feijão e feijão. Entre eles, a análise das tags de feijão é a mais complexa e importante, por isso iniciaremos uma análise aprofundada do feijão. Se pudermos entender o processo de análise dessa tag, a análise de outras tags será naturalmente resolvida. No artigo anterior, apenas o descrevemos brevemente. Neste artigo, discutiremos isso em detalhes em torno do módulo de análise.
classe pública DefaultBeandEfinitionDocumentReader implementa o BEANDefinitionDocumentReader {private void parsedefaultElement (elemento ele, beandefinitionParserDelegate delegate) {// importar tag Parsing if (delegate.nodenameEquals (eleMeReReCeReMeDeReReReMerBeBeBeBeBeRensing; } // Alias tag Parsing else if (delegate.nodenameequals (ele, alias_element)) {ProcessaliasRegistration (ELE); } // Resolução de tags de feijão else if (delegate.nodenameequals (ele, bean_element)) {processBeandEfinition (ele, delegado); } // Importar resolução de tags mais if (delegate.nodenameequals (ele, itens_beans_element)) {// Beans Tag Resolution Method DoregisterBeandEfinitions (ELE); }}} Primeiro, vamos analisar processBeanDefinition(ele, delegate) na classe
Protegido processo processador de processos (elemento ele, beandefinitionParserDelegate Delegate) {// Delegar o método ParsebeandEfinitionElement do BeandEfinitionDelegate Classe para elemento Parsing BandEfinitionHolder BdHolder = DeLegate.PareanDeelement if (bdholder! = null) {// Quando o bdholder retornado não estiver vazio, se houver um atributo personalizado no nó filho do rótulo padrão, você precisará analisar o rótulo personalizado novamente bdholder = delegate.DecorateBeandEDefinitionEfrequired (ele, bdholder); Tente {// Após a conclusão da análise, o BDHolder analisado precisa ser registrado. A operação de registro é delegada ao método RegisterBeandEfinition de BeandEfinitionReaderutils.registerBeanDefinition (Bdholder, getReReRerContext (). GetRegistry ()); } catch (beandEfinitionStoreException ex) {getReReRerContext (). Error ("Falha ao registrar a definição do feijão no nome '" + bdholder.getbeanName () + "'", ele, ex); } // Finalmente, um evento de resposta é emitido para notificar o ouvinte relevante de que o feijão foi carregado getReaderContext (). FireComponentRecistered (New BeanComponentDefinition (BDHolder)); }}Neste código:
Vamos analisar em detalhes como a mola analisa cada tag e nó
Análise de tags de feijão
classe pública BEANDEFINIÇÃOPARSERDELEGATE {@NULLALLABLE Public BeandEfinitionHolder parsebeanCefinitionElement (elemento ele, @Nullable BeandEfinition // Obtenha o atributo de nome da string de tag de bean nameattr = ele.getAttribute (name_attribute); List <string> aliases = new ArrayList <> (); if (stringutils.haslength (nameattr)) {// passa o valor do atributo de nome e divida -o em um número de string (ou seja, se vários nomes estiverem configurados no arquivo de configuração, processe aqui) string [] namearr = stringUtils.tokenizeToStringArray (nameattr, multi_value_ation = aliases.addall (Arrays.asList (namearr)); } String beanname = id; // Se o ID estiver vazio, use o atributo de primeiro nome configurado como o id if (! StringUtils.hastext (beanname) &&! Aliases.isEmpty ()) {beanname = aliases.remove (0); if (logger.isdebugenabled ()) {Logger.debug ("no XML 'id' especificado - usando '" + beanname + "' como nome de feijão e" + aliases + "como aliases"); }} if (contendoBean == null) {// Verifique a singularidade do nome do feijão e do aliases // O núcleo interno é usar a coleção de nomes usados para salvar todo o nome usado e aliasesuniques (nome de beanname, aliases, ele); } // Analisar ainda todas as outras propriedades no objeto genérico de objeto abstratoBeaNDeFinition beandEfinition = parsebeanndEfinitionElement (ELE, Beanname, contidoBean); if (beandefinition! = null) {// Se o feijão não especificar o nome do feijão, use a regra padrão para gerar o nome do feijão para este feijão if (! this.readerContext.getReGistry (), true); } else {beanname = this.readerContext.gereateBeanName (beandEfinition); // Registre um pseudônimo para o nome da classe de feijão simples, se possível, // se o gerador retornar o nome da classe mais um sufixo. // Isso é esperado para a compatibilidade da primavera 1.2/2.0. String beansClassName = beandefinition.getbeanClassName (); if (beansclassName! = null && beanname.startswith (beansclassName) && beanname.length ()> beanclassName.length () &&! this.readerContext.getRegistry (). isbeannameinuse (beansname)) {alias.dd (). }} if (logger.isdebugenabled ()) {Logger.debug ("nem XML 'id' nem 'nome' especificado -" + "usando o nome de feijão gerado [" + beanname + "]"); }} catch (Exceção ex) {error (ex.getMessage (), ele); retornar nulo; }} String [] aliasesarray = stringUtils.toStringArray (aliases); // encapsula as informações no objeto BEANDefinition Return New BeandEfinitionHolder (BeandEfinition, Beanname, Aliasessarray); } retornar nulo; }} Esse método processa principalmente atributos relacionados, como ID, nome, alias, etc., gera o nome do feijão e completa o método de análise da função de sobrecarga na função de sobrecarga parseBeanDefinitionElement(ele, beanName, containingBean) .
Em seguida, concentre -se no parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean)
Veja como ele completa a operação de análise de tags
Nó de feijão e análise de atributos
@NULLLABLEPUBLIC AbstractBeandEfinition parsebeanndefinitionElement (elemento ele, string beanname, @nullable beandefinition // Obtenha o atributo de classe do feijão string string className = null; if (ele.hasattribute (class_attribute)) {className = ele.getAttribute (class_attribute) .trim (); } // Obtenha o atributo pai da tag string parent = null; if (ele.hasattribute (parent_attribute)) {parent = ele.getAttribute (parent_attribute); } tente {// crie abstrateBeandEfinition para hospedar atributos abstrateBeanDefinition bd = createbeandEfinition (className, pai); // Obtenha vários atributos de etiqueta de feijão parsebeanndefinitionAttributes (ELE, Beanname, contendoBean, BD); // parse descrição tag bd.setDescription (domutils.getChildElementValueByTagName (ELE, Descrição_Element)); // parse meta tag parsemetaElements (ELE, BD); // parse lookup-method tag parselookupOverrideSubElements (ele, bd.getMethodOverrides ()); // parse substituiu-method tag parsereplacedmethodsubelements (ele, bd.getMethodOverrides ()); // parse substituiu-method tag parsereplacedmethodsubelements (ele, bd.getMethodOverrides ()); // parse construtor-arg tag parseconstructorargElements (ELE, BD); // Parse Tag ParsePropertyElements (ELE, BD); // Parse qualificador tag parsequalifierelements (ele, BD); bd.setResource (this.readerContext.getResource ()); Bd.SetSource (ExtractSource (ELE)); retornar BD; } catch (classNotFoundException ex) {Error ("Classe Bean [" + className + "] não encontrado", ele, ex); } catch (noclassDeffoundError err) {Error ("classe que a classe Bean [" + className + "] depende de não encontrado", ele, err); } catch (ex throwable ex) {Error ("Falha inesperada durante a análise da definição do feijão", ele, ex); } finalmente {this.parsestate.pop (); } retornar nulo;}Analisar outros atributos e elementos (existem muitos elementos e atributos, portanto, essa é uma enorme carga de trabalho) e os empacote em uma energia genérica. Depois de analisar esses atributos e elementos, se você detectar que o feijão não possui um nome de beanname especificado, usa as regras padrão para gerar um nome de bean para o feijão.
// beandefinitionParserDelegate.javaProtected abstrateBeanDefinition createbeandEfinition (@NullLable String ClassName, @NullLable String parentName) lança classeNotFoundException {return beandEfinitionReaderUtUT.CreateBeRendEnTition (ParentName, ClassName, ClassName, th ThatErtErt.ClateReReReNTENTENTENTENAME (parentname {ReturnMeReReReReReReReReReRTEnTeRenTeRenToNeT. BeandefinitionReaderutils {public static abstractBeandEfinition CreateBeandEfinition (@NullLable String parentname, @NullLable String ClassName, @NullLable ClassLoader Classloader) lança ClassNotFoundException {GenericbeandEfinition BD = New GenericBeanndEnCinition; // piselename pode estar vazio bd.setParentName (nome -mãe); // Se o Classloader não estiver vazio // use o carregador de classe passado para carregar o objeto de classe com a mesma máquina virtual. Caso contrário, apenas classloader é gravado se (className! = Null) {if (classLoader! = Null) {bd.setBeanClass (classutils.forname (className, classloader)); } else {bd.setBeanClassName (className); }} retornar bd; }}O BeandEfinition é uma representação interna de <Bean> em um contêiner, e o BeandEfinition e o <Bean> são individuais. Ao mesmo tempo, o BeandEfinition será registrado no BeaNDefinitionRegistry, que é como o banco de dados de memória de informações de configuração da primavera.
Até agora, createBeanDefinition(className, parent); foi concluído e também obtivemos a abstração de definição usada para hospedar os atributos. Vamos dar uma olhada em como parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); parsebeanndefinitionAttributes (ele, beanname, contendoBean, BD); analisar vários atributos de tags em feijão.
classe pública BEANDefinitionParserDelegate {public abstractBeandEfinition parsebeandEfinitionAttributes (elemento ele, string beanname, @nullable beandefinition contém através de que a parte do Código Demorativa Mundial. Se houver, bd.set (atributo); retornar BD; }} `` `A análise completa da tag `Bean` terminou. O elemento analisando sob a tag `bean` é semelhante. Se você estiver interessado, pode rastrear o código-fonte e ver os métodos de análise como 'Qualificador, Lookup-Method` (*não complicado que `bean`*). O conteúdo de tag personalizado é mais detalhado no próximo capítulo.
Finalmente, encapsular as informações obtidas na instância `beandefinitionholder`
`` java // beandefinitionParserDelegate.java@nullablepublic beandefinitionholder parsebeanndefinitionElement (elemento ele, @nullable beandefinition
Registre uma parseada BeandEfinition
Depois de analisar o arquivo de configuração, obtivemos todas as propriedades do feijão, e o próximo passo é registrar o feijão
classe pública beandEfinitionReaderutils {public static void RegisterBeaNDeFinition (BeandEfinitionHolderHolder, BeaNDefinitionRegistry Registry) lança BeandEfinitionStoreException {// Use Beanname como um identificador exclusivo string beanname = definither.getBeanName (); // Registre o código central do Bean Registry.RegisterBeaNDeFinition (Beanname, Definilder.getBeandEfinition ()); // Registre todos os aliases para o Bean String [] Aliases = defintHolder.getaliases (); if (aliases! = null) {for (alias de string: aliases) {Registry.registeralias (beanname, alias); }}}}O código acima conclui principalmente duas funções: uma é registrar o BeaNDefinition usando o Beanname, e o outro é concluir o registro de alias.
Feanname Register BeandEfinition
classe pública DefaultListableBeanFactory {@Override public void RegisterBeandBefinition (string beanname, beandefinition beandefinition) lança BeandEfinitionStoreException {Assert.hastext (beanname, "o nome do feijão não deve estar vazio; Assert.NotNull (BeandEfinition, "BeandEfinition não deve ser nulo"); if (beanDefinition instanceof AbstractBeanDefinition) { try { // The last verification before registration, the verification here is different from the XML file verification // It is mainly for the methodOverrides verification in the AbstractBeanDefinition property // Check whether methodOverrides coexist with the factory method or the methodOverrides does not exist at all ((AbstractBeanDefinition) beandefinition) .Validate (); } catch (beandEfinitionValidationException ex) {tiro o novo BeandEfinitionStoreException (BeaNDefinition.GetResourceDescription (), Beanname, "Validação da definição do feijão falhou", ex); }} BeandEfinition OldBeandEfinition; // Obtenha a beandefinition no cache OldBeandEfinition = this.BeandEfinitionMap.get (Beanname); if (oldBeanDefinition != null) { // If there is a cache, determine whether overwriting is allowed if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': Já existe [" + OldBeandEfinition +"] ligado. "); } else if (OldBeAndEfinition.getRole () <beandefinition.getRole ()) {// EG foi Role_application, agora substituindo Role_support ou Role_infrastructure se (this.logger.iswarnenabled () {this.an '' 'Besning ("Over.Logger.Iswarnenabled ()) {this.OnLogger.warn (" ingressing) Definição do feijão gerado pela estrutura: substituindo [" + OldBeandEfinition +"] por [" + beandEfinition +"] "); }} else if (! beandefinition.equals (OldBeandEfinition)) {if (this.logger.isinfoenabled ()) {this.logger.info ("definição de bean de bean" + bEanname + "'com uma definição diferente: repondo [" + antigodefinition "" "" "Com replicar [" + antigo, }} else {if (this.logger.isdebugenabled ()) {this.logger.debug ("Substituindo definição de feijão para bean '" + beanname + "' com uma definição equivalente: substituindo [" + OldBeandEfinition + "] com [" + BeandEfinição + ";); }} // Se a substituição for permitida, salve o BeandEfinition no beandEfinitionMap this.BeanDefinitionMap.put (Beanname, BeandEfinition); } else { // Determine whether bean creation has started if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { // Save beanDefinition into beanDefinitionMap this.beanDefinitionMap.put(beanName, beanDefinition); // Atualizar lista de beanname registrada <string> atualizadaDefinitions = new ArrayList <> (this.beandEfinitionNames.size () + 1); updateDefinitions.addall (this.beandEfinitionNames); atualizadaDefinitions.add (beanname); this.BeaNDefinitionNames = atualizados; if (this.ManualSingLetonames.Contains (Beanname)) {Set <String> UPDATEDSINGLETONS = new LinkedHashSet <> (this.ManualSingLonames); UPDATEDSINGLETONS.REMOVE (BEANNAME); this.ManualSingLonames = UpdateSingLetons; }}} else {// ainda não comecei a criar feijões. this.BeandEfinitionNames.add (Beanname); this.ManualSingLonames.Remove (Beanname); } this.frozenBeandEfinitionNames = null; } if (OldBeandEfinition! = NULL || containssingleton (beanname)) {// Redefina o cache correspondente ao beanname RESETBeandEfinition (Beanname); }}}Registre um alias
Depois de registrar o BeandEfinition, o próximo passo é registrar o alias. A relação correspondente entre alias registrada e nome do feijão é armazenada no aliasmap. Você descobrirá que o método Registeralias é implementado no SimpleAliasReRegistry
classe pública SimleAliasRegistry { / ** mapa do alias ao nome canônico* / mapa final privado <string, string> aliasmap = new ConcurrentHashMap <> (16); public void Registeralias (nome da string, alias de string) {Assert.hastext (nome, "'Nome' não deve estar vazio"); Assert.hastext (pseudônimo, "'alias' não deve estar vazio"); if (alias.equals (nome)) {// Se o nome do beann for o mesmo que o alias, o alias não será registrado e excluirá o alias correspondente this.aliasmap.remove (alias); } else {string registredName = this.aliasmap.get (alias); if (RegisterDName! = null) {if (registredName.equals (nome)) {// Se o alias foi registrado e o nome apontado for o mesmo que o nome atual, nenhum processamento será feito; } // Se o alias não permitir a substituição, será lançada uma exceção se (! AllothAliasOverriding ()) {lançar a nova ilegalStateException ("não puder registrar o alias '" + Alias + "' para nome '" + nome + "': ele já está registrado para nome '" + registreDName + "'."); }} // O loop de verificação aponta para dependências como a-> b b-> c c-> a, ocorre um erro de checkforaliascircle (nome, alias); this.aliasmap.put (alias, nome); }}} Verifique a dependência circular do alias através do método de checkForAliasCircle() . Quando A -> B existir, se A -> C -> B aparecer novamente, será lançada uma exceção:
Void protegido CheckForAliasCircle (nome da string, alias de string) {if (hasalias (alias, nome)) {tire nova ilegalStateException ("não pode registrar alias '" + alias + "' para nome '" + nome + "': referência circular - '" + name + "' é um direto ou indireto de 'para'". }} public boolean hasalias (nome da string, alias de string) {for (map.entry <string, string> entrada: this.aliasmap.entrySet ()) {string registredName = entradas.getValue (); if (registredName.equals (nome)) {string registredalias = entradas.getKey (); return (registredalia.equals (alias) || hasalias (registredalias, alias)); }} retornar false;}Nesse ponto, o registro de alias foi concluído e as seguintes tarefas foram concluídas.
Enviar notificação
Notifique o ouvinte para ser analisado e registrado
//DefaultBeanDefinitionDocumentReader.javaprotected void ProcessBeandEfinition (elemento ele, BeandEfinitionParserDelegate Delegate) {// Enviar evento de registro. getReaderContext (). FireComponentRecistered (New BeaanComponentDefinition (Bdholder));}O método do FirecMONGONGISTERED é usado para notificar o ouvinte para analisar e registrar o trabalho. A implementação aqui é apenas para extensão. Quando o desenvolvedor do programa precisa ouvir o evento BeandEfinition registrado, ele pode registrar o ouvinte e escrever a lógica de processamento no ouvinte. Atualmente, a primavera não lida com este evento neste evento
O ReadContext é gerado ligando para o CreaterAreaderContext no Classe XMLBeandEfinitionReader e depois chamando fireComponentRegistered()
Análise de tag alias
A Spring fornece configuração de alias para <alias name="person" alias="p"/> . A resolução de tags é feita no método ProcessaliasRegistration (Element ELE).
classe pública DefaultBeanDefinitionDocumentReader {Protected void ProcessaliasRegistration (elemento ele) {// Obtenha o nome da tag Alisa Nome da propriedade String Nome = ele.getAttribute (name_attribute); // Obter alisa tag alisa tag alias atributo string alias = ele.getAttribute (alias_attribute); booleano válido = true; if (! stringUtils.hastext (nome)) {getReRerContext (). Error ("Nome não deve estar vazio", ele); válido = false; } if (! stringUtils.hastext (alias)) {getReaderContext (). Error ("Alias não deve estar vazio", ele); válido = false; } if (válido) {try {// Register alias getReaderContext (). getRegistry (). Registeralias (nome, alias); } catch (Exceção ex) {getReaderContext (). Error ("Falha ao registrar o alias '" + Alias + "' para Bean com nome '" + Nome + "'", ele, ex); } // Depois de registrar o pseudônimo, diga ao ouvinte para fazer o processamento correspondente getRereadContext (). FirealiasRetisted (nome, alias, extractSource (ELE)); }}}Primeiro, o atributo de tag alias é extraído e verificado. Depois que a verificação é passada, o registro de alias é realizado. Alias Registro e registro de alias na análise de tags de feijão foram realizados. Não vou repetir aqui.
Importar análise de tags
classe pública DefaultBeanDefinitionDocumentReader {Protected void importBeaNDeFinitionResource (elemento ele) {// Obtenha o atributo de recurso da troca de string de importação = ele.getAttribute (Resource_attribute); // Se não existir, nenhum processamento será feito se (! StringUtils.hastext (localização)) {getReaderContext (). Error ("Localização do recurso não deve estar vazio", ele); retornar; } // Analisando o formato do atributo de espaço reservado como "$ {user.dir}" location = getReReRerContext (). Getenvironment (). Resolverequedledoundholders (localização); SET <Source> realResources = new LinkedHashSet <> (4); // Determine se o recurso é um caminho absoluto ou um caminho relativo boolean absolutelocation = false; tente {absolutelocation = ResourcePatternutils.isurl (localização) || Resourceutils.Touri (Localização) .isabsolute (); } Catch (UrisyntoxException Ex) {// não pode se converter em um URI, considerando o local relativo //, a menos que seja o bem conhecido prefixo da mola "ClassPath*:"} // se for um caminho absoluto, o arquivo de configuração correspondente será carregado diretamente de acordo com o endereço se (absolutelocation) {try {Int importCount. realresources); if (logger.isdebugenabled ()) {Logger.debug ("importado" + importCount + "Definições de feijão da URL Location [" + localização + "]"); }} catch (beandEfinitionStoreException ex) {getReReRerContext (). Error ("Falha ao importar definições de feijão da localização da URL [" + localização + "]", ele, ex); }} else {try {int importCount; // carrega o recurso de acordo com o recurso Relativo RelativerSource = getReReRerContext (). GetResource (). CreaterLative (localização); if (RelativerSource.Exists ()) {importCount = getReReRerContext (). getReader (). loadBeandEfinitions (relatVeReSource); realresources.add (relativeResource); } else {string basElocation = getReReContext (). getResource (). geturl (). tostring (); importCount = getReReContext (). getReader (). LoadBeanDefinitions (stringUtils.applyRelativePath (BasELocation, Location), RealResources); } if (logger.isdebugenabled ()) {Logger.debug ("importado" + importCount + "definições de feijão da localização relativa [" + localização + "]); }} catch (ioexception ex) {getReaderContext (). Error ("Falha ao resolver o local atual do recurso", ele, ex); } catch (beandEfinitionStoreException ex) {getReaderContext (). Error ("Falha ao importar definições de feijão da localização relativa [" + localização + "]", ele, ex); }} // Após a análise, o processamento de ativação do ouvinte é executado. Recurso [] actSARRAY = realResources.ToArray (novo recurso [realResources.size ()]); getReaderContext (). FireImportProceded (Localização, ActSarray, ExtractSource (ELE)); }} Depois de concluir o processamento da tag de importação, a primeira coisa é obter o caminho representado pelo atributo <import resource="beans.xml"/> recursos e analisar o espaço reservado para atributo no caminho como ${user.dir} e determinar se o local é um caminho absoluto ou um caminho relativo. Se for um caminho absoluto, o processo de análise do feijão é chamado recursivamente (loadBeanDefinitions(location, actualResources);) para executar outra análise. Se for um caminho relativo, calcule o caminho absoluto e analise -o. Por fim, notifique o ouvinte e a análise está concluída.
Resumir
Depois de alguns outono desconhecido, inverno, primavera e verão, tudo seguirá a direção que você deseja ...
Ok, o acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.
Dizer algo
Código de texto completo: https://gitee.com/battcn/battcn-spring-source/tree/master/chapter1 (download local)