Ставьте лайк и смотрите снова, формируйте привычку и ищите в WeChat [Третий принц Ао Бин] Подпишитесь на этого программиста, который любит писать о чувствах.
эта статьяGitHub github.com/JavaFamilyВключено, и есть полные тестовые площадки, материалы и мой цикл статей для интервью с производителями первой линии.
предисловие
В предыдущей статье Dubbo Ао Бин рассказал всем об общей структуре, а также упомянул, что успех Dubbo неотделим от нее.Принять дизайн микроядра + расширение SPI, чтобы стороны доступа с особыми потребностями могли настраивать расширения и выполнять индивидуальную вторичную разработку.
Хорошая расширяемость особенно важна для фреймворка, который, как следует из его названия,Создайте ядро, предоставляя пользователям простое и удобное использование, но также требующееудовлетворить их индивидуальные потребности.
Dubbo полагается на механизм SPI для реализации функции плагина.Почти все функциональные компоненты реализованы на основе SPI, а многие точки расширения, которые можно использовать напрямую, предоставляются по умолчанию.Реализована открытая для расширения архитектура с функционально-ориентированным разделением..
Что такое СПИ
Прежде всего, мы должны знать, что такое SPI.
SPI (интерфейс поставщика услуг) в основном используется во фреймворке, наиболее распространенный и тот, который мы используем при доступе к базе данных.java.sql.Driver
интерфейс.
Если подумать, на рынке существуют различные базы данных, и лежащие в основе протоколы разных баз данных очень разные, поэтому вам нужно сначалаНастроить интерфейс, чтобы ограничить эти базы данных, чтобы пользователи языка Java могли легко и единообразно программировать интерфейс, ориентированный на вызов базы данных.
Производители баз данных должны разрабатывать свои соответствующие реализации в соответствии с интерфейсом, поэтому возникает вопрос, какую реализацию следует использовать, когда она действительно используется? Где найти класс реализации?
В этот раз мне пригодится механизм Java SPI, я не знаю, какой класс реализации использовать, и если класс реализации не может быть найден, мы говорим, что все закончилось.
Все согласны написать конфигурацию класса реализации в одном месте, а потом пойти туда, где ее проверить, разве вы не знаете?
Это то, что делает Java SPI, и по соглашению создается каталог META-INF/services/ в пути к классам.файл, названный в честь интерфейса службы,ПотомВ файл записывается полное имя конкретного класса реализации, предоставленного этим пакетом jar..
Таким образом, когда мы обращаемся к пакету jar, мы можем найти каталог META-INF/services/ пакета jar, найти файл по имени интерфейса, а затем прочитать содержимое файла для загрузки и создания экземпляра пакета. класс реализации.
Например, давайте посмотрим, как это делает MySQL.
Давайте посмотрим на содержимое файла.
Это то, что делает MySQL.Чтобы дать всем более глубокое понимание, я просто напишу пример.
Пример Java SPI
Затем я создал файл с полным именем интерфейса в каталоге META-INF/services/ со следующим содержимым:
com.demo.spi.NuanNanAobing
com.demo.spi.ShuaiAobing
Результат после запуска следующий
Анализ исходного кода Java SPI
В предыдущей статье я также упомянул, что Dubbo реализует не SPI в Java, а пользовательский SPI, что должно быть неудобством или недостатком Java SPI.
Таким образом, C поможет вам глубоко понять Java SPI, чтобы вы могли понять, что не хорошо, а затем сравнить его с Dubbo SPI, чтобы сделать его преимущества более ясными.
Не пугайтесь, когда увидите исходный код, C уже сделал комментарии для всех, и логика не сложная.Необходимо усилить исходный код.为了让大家更好的理解,丙在源码分析完了之后还会画个图,帮大家再理一下思路。
Как вы можете видеть из моего примера вышеServiceLoader.load()
По сути, это запись Java SPI, давайте посмотрим, что это за операция.
Подытожу одним предложением: проще говоря, сначала найдите ClassLoader, привязанный к текущему потоку, если нет, используйте SystemClassLoader, затем очистите кэш, а затем создайте LazyIterator.
Теперь основное внимание уделяется LazyIterator.Из приведенного выше кода видно, что мы вызываем hasNext() для выполнения цикла экземпляра и получаем экземпляр через next(). И LazyIterator на самом деле является классом реализации Iterator. Давайте посмотрим, что он делает.
Независимо от входа в ветку if или else, основное внимание уделяется коду, который я создал, а затем пришло время ввести важный момент!
Видно, что этот метод на самом деле заключается в том, чтобы найти файл, соответствующий интерфейсу, в оговоренном месте, затем загрузить файл и проанализировать содержимое в файле.
Давайте посмотрим на Nextservice () снова.
Таким образом, нужно загрузить класс через полное имя, заполненное в файле, создать его экземпляр и поместить его в кеш, чтобы вернуть экземпляр.
Анализ исходного кода всего Java SPI завершен, это очень просто? Это согласовать каталог, перейти в этот каталог, чтобы найти файл в соответствии с именем интерфейса, проанализировать файл, чтобы получить полное имя класса реализации, а затем циклически загрузить класс реализации и создать его экземпляр.
Я буду использовать другую картинку, чтобы провести вас через это снова.
Подумайте, что плохого в Java SPI
Я полагаю, что вы можете сразу увидеть, что Java SPI проходит через файл конфигурации SPI при поиске классов реализации расширения иСоздание экземпляров всех классов реализации, предполагая, что процесс инициализации класса реализации потребляет ресурсы и занимает много времени, но он не используется в вашем коде, что приводит к пустой трате ресурсов.
Таким образом, Java SPI не может загружать классы реализации по запросу.
Dubbo SPI
Поэтому Dubbo реализовал SPI самостоятельно.Давайте подумаем о загрузке по требованию.Во-первых, вы должны дать имя.Перейдите к файлу, чтобы найти соответствующее полное имя класса реализации через имя, а затем загрузите и создайте его экземпляр.
Так устроен Dubbo.Файл конфигурации хранит пары ключ-значение.Я вырезал конфигурацию Cluster.
иПомимо загрузки классов реализации по запросу, Dubbo SPI добавляет функции IOC и AOP, а также механизм адаптивного расширения.
Давайте сначала рассмотрим соглашение Dubbo о каталогах файлов конфигурации.В отличие от Java SPI, Dubbo делится на три категории каталогов.
-
Каталог META-INF/services/: файлы конфигурации SPI в этом каталоге предназначены для совместимости с Java SPI.
-
Meta-Inf / Dubbo / Directory: этот каталог хранит пользовательские файлы конфигурации SPI.
-
Каталог META-INF/dubbo/internal/: В этом каталоге хранятся файлы конфигурации SPI, используемые внутри Dubbo.
Простой пример Dubbo SPI
Использование очень просто, я покажу пример на официальном сайте.
Сначала создайте файл с полным именем интерфейса в каталоге META-INF/dubbo со следующим содержимым:
optimusPrime = org.apache.spi.OptimusPrime
bumblebee = org.apache.spi.Bumblebee
Затем отметьте аннотацию @SPI на интерфейсе, чтобы указать, что он использует механизм SPI, как показано на следующем рисунке (я просто взял диаграмму кластера в качестве примера, которая отличается от интерфейса, определенного в этом примере кода).
Затем указанный класс реализации можно загрузить с помощью следующего примера кода.
Посмотрим на результаты операции.
Анализ исходного кода Дуббо
Версия исходного кода для этого анализа — 2.6.5.
Я полагаю, что благодаря приведенному выше описанию у вас уже есть определенное представление о Dubbo SPI.Далее давайте посмотрим на его реализацию.
Из приведенного выше примера кода мы знаем, что ExtensionLoader, по-видимому, находится в центре внимания, он похож на существование ServiceLoader в Java SPI.
Мы видим, что общий процесс заключается в том, чтобы сначала найти ExtensionLoader через класс интерфейса, а затем использовать ExtensionLoader.getExtension(name) для получения экземпляра класса реализации с указанным именем.
Давайте сначала посмотрим, что делает getExtensionLoader().
Это очень просто, сделать какие-то суждения, а потом узнать из кэша, существует ли уже этот тип ExtensionLoader, если нет, создать новый и вставить его в кэш. Наконец, верните ExtensionLoader, соответствующий классу интерфейса.
Давайте еще раз взглянем на метод getExtension().Из этого явления мы можем узнать, что этот метод предназначен для поиска экземпляра класса реализации по имени из ExtensionLoader, соответствующего классу.
Как видите, ключевым моментом является createExtension() Давайте посмотрим, что делает этот метод.
Общая логика очень понятна, сначала найдите класс реализации, определите, есть ли экземпляр в кеше, если нет, создайте экземпляр путем отражения, а затем выполните инъекцию зависимости метода set. Если класс-оболочка найден, оберните другой слой.
Пока что я сначала нарисую картинку, и она всем будет понятна, это еще очень просто.
Итак, вопрос в том, как найти getExtensionClasses()? Как внедряется injectExtension() (на самом деле, я уже говорил о внедрении метода set)? Зачем нужен класс-оболочка?
getExtensionClasses
Этот метод тоже собирается идти в кеш, если кеш пустой, то вызываемloadExtensionClasses
, Давайте посмотрим на этот метод.
иloadDirectory
Он основан на имени класса и указанном каталоге, сначала найдите файл, чтобы получить все ресурсы, затем загрузите классы один за другим, а затем передайтеloadClass
Выполните операцию кэширования.
Видно, что loadClass загрузил класс раньше, а loadClass просто выполняет другое кэширование в зависимости от ситуации над классом. СоответственноAdaptive
,WrapperClass
и общий класс, общий класс будетActivate
Записал. На этом весь процесс SPI для обычных классов завершен.
Далее, давайте посмотрим на использование нескольких вещей, которые не являются обычными типами.
Адаптивные аннотации — адаптивные расширения
Прежде чем приступить к анализу аннотаций, нам нужно знать механизм адаптивного расширения Dubbo.
Давайте сначала рассмотрим сценарий.Сначала мы загружаем расширение SPI согласно конфигурации, но я не хочу, чтобы расширение загружалось при запуске.Я хочу динамически выбирать соответствующее расширение по параметрам в момент запроса .
Как это сделать?
Dubbo реализует адаптивное масштабирование через прокси-механизм., просто для создания прокси-класса для интерфейса, который вы хотите расширить, вы можете скомпилировать код прокси-класса, созданный с помощью JDK или javassist, а затем создать экземпляр с помощью отражения.
Реализация в этом случае будет знать требуемый класс расширения в соответствии с параметрами запроса исходного метода, а затем передатьExtensionLoader.getExtensionLoader(type.class).getExtension(从参数得来的name)
, чтобы получить реальный экземпляр для вызова.
Сделал пример с официального сайта, давайте посмотрим.
Теперь у каждого должно быть определенное представление об адаптивном расширении, давайте посмотрим на исходный код и как это сделать.
Эта аннотация является аннотацией, связанным с адаптивным расширением. Он может изменять классы и методы. Когда класс изменен, прокси-класс не будет сгенерирован, потому что этот класс является модифицированным прокси-классом. Когда метод модифицирован, прокси-класс будет генерируется.
Адаптивная аннотация на классе
нравитсяExtensionFactory
Существует три класса реализации, один из которых помечен аннотацией Adaptive.
Когда ExtensionLoader создан, он получит ExtensionFactory указанного класса расширения через getAdaptiveExtension.
давайте еще раз посмотримAdaptiveExtensionFactory
реализация.
Вы можете видеть, что все классы реализации сначала кэшируются, а затем соответствующее расширение находится путем обхода при его получении.
Давайте далее проанализируем, что происходит на волне getAdaptiveExtension.
До сих пор он фактически был проанализирован с помощью вышеуказанногоgetExtensionClasses
LoadClass в адаптивном специальном кэше повторяется.
Адаптивные аннотации к методам
По способу аннотация должна динамически сплавать код, а затем динамически генерировать класс. Давайте возьмем протокол в качестве примера.
Протокол не имеет класса реализации с аннотацией Adaptive , но два метода интерфейса имеют аннотацию Adaptive , а два метода — нет.
Так что логика должна бытьcreateAdaptiveExtensionClass
,
Я не буду вдаваться в подробности того, как в нем генерировать код, если интересно, можете сами посмотреть, я проанализирую готовый продукт, и все.
Я украшу его для всеобщего обозрения.
Можно видеть, что пакет будет сгенерирован, и оператор импорта также будет сгенерирован. Имя класса является интерфейсом плюс $ Adaptive, и этот интерфейс реализован. Если вызывается вызов метода без адаптивной аннотации, ошибка будет быть брошенным прямо.
Давайте посмотрим на метод пометки заметок, я взял пример экспорта.
Как я уже говорил, в соответствии с параметрами запроса, то есть URL-адресом, получите конкретное имя класса реализации, которое будет вызываться, а затем вызовитеgetExtension
Получать.
Весь процесс адаптивного расширения выглядит следующим образом.
WrapperClass - AOP
Класс-оболочка связан с тем, что расширенный интерфейс может иметь несколько расширенных классов реализации, иЭти классы реализации расширения будут иметь идентичную или общую логику., Если каждый класс реализации напишет код один раз, он будет повторяться, и его непросто поддерживать.
Поэтому был создан класс-оболочка, и Dubbo автоматически обернет его за вас.Поскольку конструктор расширенного класса имеет только один параметр, и это тип расширенного интерфейса, он будет определен как класс-оболочка, а затем записывается и используется для обертывания других объектов класса реализации.
Простой и гениальный, это АОП.
injectExtension - IOC
Глядя на код напрямую, это очень просто, достаточно найти метод set, найти зависимый объект по параметрам и внедрить его.
Это МОК.
Активировать аннотацию
Я кратко расскажу об этой аннотации.В качестве примера возьмем Filter.Filter имеет много классов реализации.В некоторых сценариях требуется несколько классов реализации, а в некоторых сценариях требуется несколько других, и для обозначения этого используется аннотация Activate. .
Он имеет три атрибута, группа указывает, на каком конце находится украшение, будь то поставщик или потребитель, значение указывает, что оно будет активировано только тогда, когда оно появляется в параметре URL, а порядок указывает порядок классов реализации.
Суммировать
Сначала поставьте полную картину вышеописанного процесса.
Тогда давайте снова подведем итоги.Сегодня C сначала поможет вам понять, что такое SPI, написать простой пример и проанализировать исходный код Java SPI.
Зная, что Java SPI загружает и создает экземпляры всех классов реализации одновременно.
Dubbo SPI реализует сам SPI, который может создавать экземпляр указанного класса реализации по имени, а также реализует IOC, AOP и адаптивное расширение SPI.
В целом это не сложно, и не будет сильно запутанно.После прочтения статьи, если вы пройдете статью еще раз, вы приобретете еще больше.
Я Ао Бин, чем больше ты знаешь, тем больше ты не знаешь, увидимся в следующий раз!
талантнаш【Три подряд】Это самая большая движущая сила для создания Ao Bing.Если в этом блоге есть какие-либо ошибки и предложения, пожалуйста, оставьте сообщение!
Статья постоянно обновляется, вы можете искать в WeChat "Третий принц Ао Бин"Прочтите это в первый раз, ответьте [материал] Подготовленные мной материалы интервью и шаблоны резюме крупных заводов первой линии, эта статьяGitHub github.com/JavaFamilyОн был включен, и есть полные тестовые сайты для интервью с крупными заводами.Добро пожаловать в Star.