Предисловие
После предыдущей статьи Весенняя дешифрование - XML -анализ и регистрация бобов, давайте продолжим анализировать исходный код. Без лишних слов давайте посмотрим на подробное введение вместе.
Дешифрование
Существует две основные категории объявлений в конфигурации XML Spring. Один из них-такая по умолчанию, например, <bean id="person"/> , а другой-это пользовательский, такой как <tx:annotation-driven /> . Две теги имеют очень разные методы анализа. Метод Parsebaindefinitions используется для различения методов анализа, используемых различными тегами. Получите пространство имен через метод node.getNamespaceURI() , определите, является ли это пространством имен по умолчанию или пользовательским пространством имен, и сравните его с фиксированным пространством имен http://www.springframework.org/schema/beans весной. Если это последовательно, используйте parseDefaultElement(ele, delegate); В противном случае это delegate.parseCustomElement(ele);
Расположение тегов по умолчанию
ParsedeFaultElement проделала различную обработку для 4 различных тегов, псевдоним, бобов и бобов. Среди них анализ меток бобов является наиболее сложным и важным, поэтому мы начнем углубленный анализ из бобов. Если мы сможем понять процесс анализа этого тега, анализ других тегов будет естественным образом решен. В предыдущей статье мы просто кратко опишем это. В этой статье мы подробно обсудим это вокруг модуля анализа.
открытый класс DefaultBeanDefinitionDocumentReader реализует BeanDefinitionDocumentReader {private void parsedefaultElement (элемент Ele, BeandefinitionParserDelegate делегат) {// Импорт tag parsing if (delegate.nodenameequals (ele, import_element)) {importbeandefinceRec } // Ссылка псевдонима } // Резолюция тега бобов else if (delegate.nodenameequals (ele, bean_element)) {processbeandefinition (ele, delegate); } // разрешение тега импорта else if (delegate.nodenameequals (ele, nested_beans_element)) {// Рекурсивный метод тега бобов Doregisterbeandefinitions (ele); }}} Во -первых, давайте проанализируем processBeanDefinition(ele, delegate) в классе
Защищенный void processbeandefinition (Element ele, BeandefinitionParserdelegate делегат) {// Делегировать метод ParsebaindefinitionElement в классе BeandefinitionDelegate для элемента разыгрывания Beandefinitionholder Bdholder = делегат.parsebeNtefinitionelement (ele); if (bdholder! = null) {// Когда возвращаемый Bdholder не является пустым, если есть пользовательский атрибут под дочерним узлом метки по умолчанию, вам необходимо просматривать пользовательскую метку снова bdholder = delegate.decoratebaindefinitionifrequired (Ele, bdholder); Попробуйте {// После завершения анализа, проанализированный Bdholder должен быть зарегистрирован. Регистрационная операция делегирована методом регистрации BeandefinitionReadRutils.geraIsterBeanDefinition (bdholder, getReaderContext (). GetRegistry ()); } catch (beandefinitionStoreException ex) {getReaderContext (). ERROR («Не удалось зарегистрировать определение бобов с именем» « + bdholder.getBeanName () +" '", ele, ex); } // Наконец, выдается событие ответа, чтобы уведомить соответствующего слушателя о том, что боб был загружен getReaderContext (). FireComponentErageed (New BeancomponentDefinition (bdholder)); }}В этом коде:
Давайте подробно проанализируем, как Spring Sackses Каждый тег и узел
Анализ тегов бобов
открытый класс BeandefinitionParserDelegate {@nullable public beandefinition holder parsebeandefinitionelement (element ele, @nullable beandefinition содержит bean) {// Получить атрибут идентификатора string bean tag string id = ele.getattribute (id_attribute); // Получить атрибут имени строки тега Bean nameattr = ele.getattribute (name_attribute); List <string> Aliases = new ArrayList <> (); if (stringutils.haslength (nameattr)) {// передавать значение атрибута имени и разделить его на строковый номер (то есть, если несколько имен настроены в файле конфигурации, обрабатывайте его здесь) string [] namearr = stringUtils.tokenizetoStringArray (nameattr, multi_value_attribute_delimiters); Aliases.addall (Arrays.aslist (namearr)); } String beanname = id; // Если идентификатор пуст, используйте атрибут настроенного имени в качестве идентификатора if (! StringUtils.hastext (beanname) &&! Aliases.isempty ()) {beanname = aliases.remove (0); if (logger.isdebugenabled ()) {logger.debug ("no xml 'id' указан - с использованием" + beanname + "'как имя боба и" + псевдонимы + "в виде псевдоним"); }} if (содержит bean == null) {// проверка уникальности Beanname и псевдонимов // Внутреннее ядро состоит в том, чтобы использовать коллекцию используемых именных имен, чтобы сохранить все использованные Beanname и псевдоним (Beanname, Aliases, Ele); } // Дополнительное разложение всех других свойств в объект GenericBeanDefinition AbstractBeanDefinition Beandefinition = parsebaindefinitionElement (ele, beanname, sondadebean); if (beandefinition! = null) {// Если фасоль не указывает Beanname, то используйте правило по умолчанию, чтобы сгенерировать Beanname для этого фасоля if (! stringutils.hastext (beanname)) {try {if (sognaingBean! this.readercontext.getregistry (), true); } else {beanname = this.readercontext.generatebeanname (beandefinition); // Зарегистрируйте псевдоним для имени класса Plain Bean, если все еще возможно, // Если генератор вернул имя класса плюс суффикс. // Это ожидается для пружины 1.2/2,0 обратной совместимости. String beanclassname = beandefinition.getbeanclassname (); if (beanclassname! = null && beanname.startswith (beanclassname) && beanname.length ()> beanclassname.length () &&! this.readercontext.getregistry (). IsbeanNameinuse (beanclassname)) {alias.addame (beanclassname); }} if (logger.isdebugenabled ()) {logger.debug ("ни xml 'id', ни« имя »указано -" + "с использованием сгенерированного имени Бин [" + beanname + "]"); }} catch (Exception ex) {error (ex.getMessage (), ele); вернуть ноль; }} String [] aliasesarray = stringutils.tostringarray (псевдоним); // инкапсулировать информацию в объект BeAndefinition Holder Return New Beandefinition Holder (Beandefinition, Beanname, Aliasesarray); } return null; }} Этот метод в основном обрабатывает связанные атрибуты, такие как идентификатор, имя, псевдоним и т. Д., генерирует Beanname и завершает анализ ядра тега в методе функции перегрузки parseBeanDefinitionElement(ele, beanName, containingBean) .
Затем сосредоточьтесь на parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean)
Посмотрите, как это завершает операцию анализа тега
Анализ бобового узла и атрибута
@NullablePublic AbstractBeanDefinition ParsebeanDefinitionElement (Element Ele, String Beanname, @nullable Beandefinition содержит bean) {this.parsestate.push (new Beanentry (Beanname)); // Получить атрибут класса строки тега Bean ClassName = null; if (ele.hasattribute (class_attribute)) {classname = ele.getattribute (class_attribute) .trim (); } // Получить родительский атрибут строки тега Bean Parent = null; if (ele.hasattribute (parent_attribute)) {parent = ele.getattribute (parent_attribute); } try {// Создать AbstractBeanDefinition для атрибутов хозяина AbstractBeanDefinition bd = createBeanDefinition (className, parent); // Получить различные атрибуты бобовых тегов parsebaindefinitionattributes (ele, beanname, sognaingbean, bd); // parse description tag bd.setdescription (domutils.getchildelementvaluebytagname (ele, description_element)); // parse meta Tag parsemetaelements (ele, bd); // parse lookup-method Tag parselookupoverridesubelements (ele, bd.getmethodoverrides ()); // parse заменил меток-метки Parserepledsemethodsubelements (ele, bd.getmethodoverrides ()); // parse заменил меток-метки Parserepledsemethodsubelements (ele, bd.getmethodoverrides ()); // parse constructor-arg Tag parseconstructorargelements (ele, bd); // Parse Property Tag ParsePropertyelements (Ele, BD); // parse Qualifier Tag PareScepecalifierelements (Ele, BD); bd.setresource (this.readercontext.getresource ()); Bd.setsource (ExtractSource (ELE)); вернуть BD; } catch (classnotfoundexception ex) {error ("class [" + classname + "] не найден", Ele, ex); } catch (noclassdeffounderror err) {error («класс, который класс бобов [" + classname + "] зависит от не найденного", Ele, Err); } catch (Throwable ex) {error («Неожиданный сбой во время анализа определения бобов», Ele, Ex); } наконец {this.parsestate.pop (); } return null;}Дополнительные анализы других атрибутов и элементов (есть много элементов и атрибутов, так что это огромная рабочая нагрузка) и упаковывают их в GenericBeandefinition. После анализа этих атрибутов и элементов, если вы обнаружите, что бобы не имеют указанного Beanname, вы используете правила по умолчанию для создания бобового имени для бобов.
// beandefinitionparserdelegate.javaprotected AbstractBeanDefinition CreateBeanDefinition (@Nullable String ClassName, @Nullable String ParentName) Throws ClassNotFoundExcept BeandefinitionReadRutils {public Static AbstractBeanDefinition CreateBeanDefinition (@nullable String parentName, @nullable String classname, @nullable classloader classloader) Throws ClassNotFoundException {GenericBeanDefinition BD = новый GenericBeanDefinition (); // parentname может быть пустым bd.setparentname (parentname); // Если ClassLoader не пуст //, используйте пропущенный ClassLoader для загрузки объекта класса с той же виртуальной машиной. В противном случае, только ClassLoader записывается if (classname! = Null) {if (classloader! = Null) {bd.setbeanclass (classutils.forname (classname, classloader)); } else {bd.setbeanclassname (classname); }} return bd; }}Beandefinition-это внутреннее представление <Bean> в контейнере, а Beandefinition и <Bean>-один на один. В то же время, Beandefinition будет зарегистрировано в BeandefinitionRegistry, которая похожа на базу данных информации о конфигурации пружины.
Пока что createBeanDefinition(className, parent); был закончен, и мы также получили AbstractBeanDefinition, используемое для размещения атрибутов. Давайте посмотрим на то, как parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); parsebaindefinitionattributes (ele, beanname, содержит bean, bd); Распокачайте различные атрибуты тегов в бобах.
Общедоступный класс BeandefinitionParserDelegate {public AbstractBeandefinition parsebaindefinitionattributes (element ele, String beanname, @nullable beandefinition Содержит, что AbstractBeandefinition BD) {// ... пропустить подробный код, эта часть кода в основном определяет, содержит ли он специфический атрибут. Если есть, bd.set (атрибут); вернуть BD; }} `` `Полный анализ тега «Бин» закончился. Элемент разбора под тегом «Бин» похож. Если вы заинтересованы, вы можете отслеживать исходный код и увидеть методы анализа, такие как `Qualifier, Lookup-Method` (*не сложный, чем` bean`*). Пользовательский контент тегов более подробно в следующей главе.
Наконец, инкапсулируйте полученную информацию в экземпляр «Бандридэфиниция».
`` java // beandefinitionparserdelegate.java@nullablepublic beandefinitionholder parsebeandefinitionelement (element ele, @nullable beandefinition содержит Beanname) {/ ... return New Beandefinitionholder (Beandefinition, Beanname, AliasRay);Зарегистрировать проанализированную боандерию
После анализа файла конфигурации мы получили все свойства боба, и следующий шаг - зарегистрировать бон
Public Class BeandefinitionReaderUtils {public static void Registrybeandefinition (Beandefinition Holder Holder, реестр BeandefinitionRegistry) THRES BEANDEFINITIONSTOREEXCEPTION {// Использование Beanname в качестве уникального идентификатора String BeanName = определение holder.getbeanName (); // зарегистрировать базовый кодекс реестра Beans.registerbeandefinition (beanname, definition holder.getbeandefinition ()); // зарегистрировать все псевдонимы для строки Bean [] aliases = definitionholder.getaliases (); if (псевдоним! = null) {for (string alias: псевдоним) {Registry.registeralias (Beanname, псевдоним); }}}}Приведенный выше код в основном завершает две функции: одна - зарегистрировать Beandefinition с использованием Beanname, а другая - завершить регистрацию псевдонима.
Beanname Register Beandefinition
открытый класс defaultbeablebeanfactory {@override public void Registerbeandefinition (String Beanname, Beandefinition Beandefinition) Throws BeandefinitionStoreException {assert.hastext (Beanname, «Имя боба не должно быть пусты»); Assert.notnull (Beandefinition, «Банддефиниция не должна быть нулевой»); if (Beaindefinition InstanceOf AbstractBeanDefinition) {try {// Последняя проверка перед регистрацией, проверка здесь отличается от проверки файла XML // оно в основном для проверки методов -эверридов в свойство AbstractBeandefinition // Проверьте, сосуществует методрид. Beandefinition) .validate (); } catch (beandefinitionvalidationException ex) {Throw New BeandefinitionStoreException (beandefinition.getresourcedescription (), Beanname, «Проверка определения бобов не удалась», Ex); }} Beandefinition OldBeandefinition; // Получить Beandefinition в кэш -oldbeandefinition = this.beandefinitionmap.get (beanname); if (OldBeanDefinition! = null) {// Если есть кэш, определите, разрешена ли перезапись if (! IsAlandBeanDefinitionOverriding ()) {Throut New BeandefinitionStoreException (Beandefinition.getResourcedespription (), BeanName, - не может регистрировать определение BENBENTEFINITION + «)». Уже есть [" + OldBeandefinition +"]. ");"); } else if (oldbeandefinition.getrole () <beandefinition.getrole ()) {// eg был Role_Application, теперь переопределяющий Role_support или Roge_infrastructure if (this.logger.iswarnenenabled ()) {this.logger.warn (this. Определение сгенерированного фреймворком: замена [" + OldBeandefinition +"] на [" + beandefinition +"] "); }} else if (! beandefinition.equals (oldbeandefinition)) {if (this.logger.isinfoenabled ()) {this.logger.info («Переходное определение бобов для бобов» + beanname + »с другим определением:" + Oldbeandefinition + "] с [ + beanfin; }} else {if (this.logger.isdebugenabled ()) {this.logger.debug («Переоценка определения бобов для бобов» « + beanname +" 'с эквивалентным определением: замена [ + Oldbeandefinition + "] с [" + beandefinition + "]"); }} // Если перезапись разрешен, сохраните Beandefinition в BeandefinitionMap this.beandefinitionmap.put (beanname, beandefinition); } else {// определить, началось ли создание бобов, если (HasbeanCreationStarted ()) {// не может модифицировать элементы сбора запуска времени (для стабильной итерации) синхронизированный (this.beandefinitionMap) {// Сохранить Beandefinition в Beandefitionmap this.beandefinitionmap.uppul (beanname, beanname, beanname, beanname, beanname, beanname, beanname, beanname, beanname, beanname, beanname, beanname, beanname- // Обновление зарегистрированного списка BeanName <string> updatedDefinitions = new ArrayList <> (this.beandefinitionnames.size () + 1); UpdatedDefinitions.addall (this.beandefinitionNames); UpdatedDefinitions.add (Beanname); this.beandefinitionNames = upplectedDefinitions; if (this.manualsinglennames.contains (beanname)) {set <string> updatedsingletons = new LinkedHashset <> (this.manualsingleNnames); Updatedsingletons.remove (Beanname); this.manualsinglennames = updatedsingletons; }}} else {// Я не начал создавать бобы, пока это.beandefinitionmap.put (beanname, beandefinition); this.beandefinitionnames.add (Beanname); this.manualsinglennames.remove (Beanname); } this.frozenbeandefinitionnames = null; } if (oldbeandefinition! = null || Содержитсинглтон (Beanname)) {// сбросить кэш, соответствующий Beanname Resetbeandefinition (beanname); }}}Зарегистрировать псевдоним
После регистрации Beandefinition следующим шагом является регистрация псевдонима. Соответствующая связь между зарегистрированным псевдонимом и Beanname хранится в псевдониме. Вы обнаружите, что метод регистрации реализован в SimplealiasRegistry
открытый класс SimpleAsregistry { / ** Карта от псевдонима до канонического имени* / private final Map <String, String> AliasMap = new ConcurrenthAshmap <> (16); public void registeralias (string name, string alias) {assert.hastext (name, "" name 'не должно быть пустым "); Assert.hastext (псевдоним, «псевдоним» не должен быть пустым »); if (alias.equals (name)) {// Если имена BeanName совпадает с псевдонимом, псевдоним не будет записан, и удалить соответствующий псевдоним this.aliasmap.remove (псевдоним); } else {string recavityedName = this.aliasMap.get (псевдоним); if (recavityedName! = null) {if (recarededname.equals (name)) {// Если псевдоним был зарегистрирован, а имя, на которое указано, то же самое, что и текущее имя, обработка не будет выполнена; } // Если псевдоним не разрешает перезаписание, будет выброшено исключение, если (! AllowaliasOverriding ()) {Through New OldalStateException («не может зарегистрировать псевдоним» « + псевдоним +" 'для имени' " + name +" ': оно уже зарегистрировано для имени " + recavitedDame +"'. "); }} // Цикл проверки указывает на зависимости, такие как a-> b b-> c c-> a, возникает ошибка Checkforaliascircle (имя, псевдоним); this.aliasmap.put (псевдоним, имя); }}} Проверьте круглую зависимость псевдонима с помощью метода checkForAliasCircle() . Когда существует -> b, если a -> c -> b появится снова, будет брошено исключение:
Защищенный void checkforaliascircle (string name, string псевдоним) {if (hasalias (псевдоним, имя)) {бросает новое нелегальное организацию («не может зарегистрировать псевдоним» « + псевдоним +" 'для имени' " + name +" ': rucular reference -' " + name +" ' - прямой или нездоровый псевдоним для " + alias +"' уже "); }} public boolean hasalias (string name, string alias) {for (map.entry <string, string> intry: this.aliasmap.entryset ()) {string recordyedname = entry.getValue (); if (recavityedName.equals (name)) {string recordyEdalias = intry.getKey (); return (recavityEdalia. }} вернуть false;}На этом этапе регистрация псевдонимы была завершена, и следующие задачи были выполнены.
Отправить уведомление
Уведомить слушателя быть проанализировано и зарегистрировано
//DefaultbeandefinitionDocumentReader.javapRoted void ProcessBeandefinition (Element Ele, BeandefinitionParserdelegate делегат) {// Отправить регистрационное событие. getReaderContext (). FireComponentErageeded (New BeanComponentDefinition (bdholder));}Метод, зарегистрированный Firecomponent, используется для уведомления слушателя о том, чтобы он анализировал и зарегистрировал работу. Реализация здесь только для расширения. Когда разработчику программы необходимо прослушать зарегистрированное событие Beandefinition, он может зарегистрировать слушателя и написать логику обработки в слушателя. В настоящее время Spring не справляется с этим событием в этом мероприятии
Readercontext генерируется путем вызова createreadercontext в классе XmlbeandefinitionReader, а затем вызывая fireComponentRegistered()
Анализ тегов псевдонима
Spring обеспечивает конфигурацию псевдонима для <alias name="person" alias="p"/> . Резолюция тега выполняется в методе ProcessaliasRegistration (Element ELE).
открытый класс defaultBeanDefinitionDocumentReader {Protected void ProcessaliasRegistration (element ele) {// Получить имя имени тега Alisa name = ele.getattribute (name_attribute); // Получить TAG ALISA TAG ALISA TAG ATRIBE ATTRIBUT ATTRIBUT ATRIBLE ALIAS = ELE.GetAttribute (ALIAS_ATTRIBUTE); логическое действительность = true; if (! stringUtils.hastext (name)) {getReaderContext (). error («Имя не должно быть пустым», ELE); alloy = false; } if (! stringUtils.hastext (псевдоним)) {getReaderContext (). error ("псевдоним не должен быть пустым", ele); alloy = false; } if (valive) {try {// регистр псевдоним getReaderContext (). getRegistry (). RegisterAlias (имя, псевдоним); } catch (Exception ex) {getReaderContext (). error ("не удалось зарегистрировать псевдоним" " + псевдоним +" 'для бобов с именем' " + name +" '", ele, ex); } // После регистрации псевдонима, сообщите слушателю выполнить соответствующую обработку getReaderContext (). FireAisReged (имя, псевдоним, ExtractSource (ele)); }}}Во -первых, атрибут тега псевдонима извлечен и проверен. После того, как проверка передается, регистрация псевдонимы проводится. Регистрация псевдонимы и регистрация псевдонимы в анализе тегов бобов были сделаны. Я не буду повторять это здесь.
Анализ импорта тегов
открытый класс DefaultBeanDefinitionDocumentReader {Protected void importBeanDefinitionResource (element ele) {// Получить атрибут ресурса строки Tag Tag = ele.getAttribute (resource_attribute); // Если его не существует, обработка не выполнена, если (! StringUtils.hastext (location)) {getReaderContext (). Error ("Местоположение ресурса не должно быть пустым", Ele); возвращаться; } // Формат атрибутов заполнителя, такой как "$ {user.dir}" location = getReaderContext (). GetEnvironment (). Resolvereectuideplaceholders (location); SET <Serrousce> FactionResources = new LinkedHashSet <> (4); // Определите, является ли ресурс абсолютным путем или относительным путем Boolean Absolutelocation = false; try {AbsoluteLocation = resourcePatterNutils.Isurl (местоположение) || Rescorceutils.touri (местоположение) .isabsolute (); } catch (urisyntaxException ex) {// не может конвертировать в URI, учитывая относительное местоположение //, если это не известный префикс пружины "classpath*:"} // Если это абсолютный путь, соответствующий файл конфигурации будет загружен непосредственно в соответствии с адресом, если (AbsoluteLocation) {try {int importCount = getReaderContext (). getReader (). if (logger.isdebugenabled ()) {logger.debug ("imported" + implyCount + "определения бобов из местоположения URL [" + location + "]"); }} catch (beandefinitionStoreException ex) {getReaderContext (). error («Не удалось импортировать определения бобов из местоположения URL [" + location + "]", ele, ex); }} else {try {int importCount; // Загрузите ресурс в соответствии с ресурсом относительного пути resourceVersource = getReaderContext (). GetResource (). CreaterElative (местоположение); if (relativeresource.exists ()) {importCount = getReaderContext (). getReader (). FactionResources.Add (RELATIVERSOURCE); } else {String baseLocation = getReaderContext (). getResource (). getUrl (). ToString (); ImportCount = getReaderCONTEXT (). getReader (). } if (logger.isdebugenabled ()) {logger.debug ("imported" + importCount + "определения бобов из относительного местоположения [" + location + "]"); }} catch (ioException ex) {getReaderContext (). error («Не удалось разрешить текущее местоположение ресурса», Ele, ex); } catch (beandefinitionStoreException ex) {getReaderContext (). ERROR («Не удалось импортировать определения бобов из относительного местоположения [" + location + "]", ele, ex); }} // После анализа выполняется обработка активации слушателя. Ресурс [] actresarray = actualResources.toarray (новый ресурс [actualResources.size ()]); getReaderContext (). FireImportPocasted (местоположение, Actresarray, ExtractSource (ELE)); }} После завершения обработки тега импорта первое, что нужно получить путь, представленный атрибутом ресурса <import resource="beans.xml"/> , а затем анализируйте заполнитель атрибута в пути, такой как ${user.dir} , а затем определить, является ли местоположение абсолютным путем или относительным путем. Если это абсолютный путь, процесс анализа бобов называется рекурсивно (loadBeanDefinitions(location, actualResources);) для проведения другого анализа. Если это относительный путь, вычислите абсолютный путь и проанализируйте его. Наконец, уведомить слушателя, и анализ завершен.
Суммировать
После нескольких неизвестных осенью, зимы, весны и лета все будет следовать за тем, что вы хотите ...
Хорошо, вышеупомянутое содержимое этой статьи. Я надеюсь, что содержание этой статьи имеет определенную справочную ценность для каждого обучения или работы. Если у вас есть какие -либо вопросы, вы можете оставить сообщение для общения. Спасибо за поддержку Wulin.com.
Скажите что-то
Полный текстовый код: https://gitee.com/battcn/battcn-spring-source/tree/master/chapter1 (локальная загрузка)