JavaRush /Java блогу /Random-KY /Java ичинде JNDI колдонуу
Анзор Кармов
Деңгээл
Санкт-Петербург

Java ичинде JNDI колдонуу

Группада жарыяланган
Салам! Бүгүн биз сени JNDI менен тааныштырабыз. Келгиле, бул эмне, эмне үчүн керек, ал кантип иштейт, аны менен кантип иштей аларыбызды билели. Анан биз Spring Boot бирдигинин сынагын жазабыз, анын ичинде биз дал ушул JNDI менен ойнойбуз. Java'да JNDI колдонуу - 1

Киришүү. Ат коюу жана каталог кызматтары

JNDIге кирүүдөн мурун, ат коюу жана каталог кызматтары деген эмне экенин түшүнүп алалы. Мындай кызматтын эң айкын мисалы – бул каалаган компьютердеги, ноутбуктагы же смартфондогу файл системасы. Файл системасы файлдарды башкарат (кызыктуу). Мындай системалардагы файлдар дарак структурасында топтоштурулган. Ар бир файлдын уникалдуу толук аты бар, мисалы: C:\windows\notepad.exe. Көңүл буруңуз: файлдын толук аты – бул кандайдыр бир түпкү чекиттен (C диски) файлдын өзүнө (notepad.exe) чейинки жол. Мындай чынжырдагы аралык түйүндөр каталогдор (windows каталогу) болуп саналат. Каталогдордун ичиндеги файлдардын атрибуттары бар. Мисалы, "Жашыруун", "Окуу үчүн гана" ж.б. Файлдык система сыяктуу жөнөкөй нерсенин деталдуу сүрөттөлүшү ат коюу жана каталог кызматтарынын аныктамасын жакшыраак түшүнүүгө жардам берет. Ошентип, ат жана каталог кызматы - бул көптөгөн аталыштарды көптөгөн an objectтерге картага түшүрүүнү башкарган система. Биздин файл тутумубузда биз an objectтерди жашырган файл аттары менен иштешебиз — ар кандай форматтагы файлдар. Ат коюу жана каталог кызматында аталган an objectтер дарак структурасында уюштурулат. Жана каталог an objectилеринин атрибуттары бар. Аты жана каталог кызматынын дагы бир мисалы DNS (Домендик аталыштар системасы). Бул система адам окуй турган домендик аталыштар (мисалы, https://javarush.com/) жана машина окуй турган IP даректер (мисалы, 18.196.51.113) ортосундагы картаны башкарат. DNS жана файл тутумдарынан тышкары, башка көптөгөн кызматтар бар, мисалы:

JNDI

JNDI, же Java аталышы жана каталог интерфейси, ат коюу жана каталог кызматтарына кирүү үчүн Java API болуп саналат. JNDI – бул Java программасы үчүн ар кандай ат коюу жана каталог кызматтары менен иштешүү үчүн бирдиктүү механизмди камсыз кылган API. Капчыктын астында JNDI менен ар кандай кызматтын ортосундагы интеграция Кызмат Провайдеринин Interface (SPI) аркылуу ишке ашат. SPI ар кандай аталыштарды жана каталог кызматтарын ачык туташтырууга мүмкүндүк берет, бул Java тиркемесинин туташкан кызматтарга кирүү үчүн JNDI API колдонуусуна мүмкүндүк берет. Төмөндөгү сүрөт JNDI архитектурасын көрсөтөт: Javaда JNDI колдонуу - 2

Булак: Oracle Java Tutorials

JNDI. Жөнөкөй сөздөр менен мааниси

Негизги суроо: бизге JNDI эмне үчүн керек? JNDI биз Java an objectисин Java codeунан an objectтердин кээ бир "Каттоосунан" ушул an objectке байланышкан an objectтин аты менен ала алышыбыз үчүн керек. Кайталанган сөздөрдүн көптүгү бизди адаштырбашы үчүн жогорудагы сөздү тезистерге бөлүп карайлы:
  1. Акыр-аягы, биз Java an objectисин алышыбыз керек.
  2. Биз бул an objectти кандайдыр бир реестрден алабыз.
  3. Бул реестрде бир топ an objectилер бар.
  4. Бул реестрдеги ар бир an objectтин уникалдуу аталышы бар.
  5. Объектти реестрден алуу үчүн, биз өтүнүчүбүзгө ат коюшубуз керек. «Баланча ат менен эмнең бар болсо мага бер» дегендей.
  6. Биз реестрден an objectтерди аты боюнча гана окубастан, ошондой эле бул реестрдеги an objectтерди белгилүү бир аталыштар менен сактай алабыз (кандайдыр бир жол менен алар ошол жерде болот).
Ошентип, бизде реестр, же an object сактагыч же JNDI дарагы бар. Андан кийин, бир мисал колдонуп, JNDI маанисин түшүнүүгө аракет кылалы. Белгилей кетсек, JNDI көбүнчө Enterprise өнүктүрүүдө колдонулат. Жана мындай тиркемелер кандайдыр бир колдонмо serverинин ичинде иштейт. Бул server Java EE Колдонмо serverи же Tomcat сыяктуу сервлет контейнери же башка контейнер болушу мүмкүн. Объект реестринин өзү, башкача айтканда, JNDI дарагы, адатта, ушул тиркеме serverинин ичинде жайгашкан. Акыркысы дайыма эле зарыл эмес (жергorктүү жерде мындай дарак болушу мүмкүн), бирок бул эң типтүү. JNDI дарагын атайын адам (системанын администратору же DevOps адиси) башкара алат, ал an objectтерди аттары менен "реестрде сактайт". Биздин тиркеме жана JNDI дарагы бир контейнердин ичинде чогуу жайгашканда, биз мындай реестрде сакталган каалаган Java an objectисине оңой жете алабыз. Мындан тышкары, реестр жана биздин тиркеме ар кандай контейнерлерде, ал тургай, ар кандай физикалык машиналарда жайгашышы мүмкүн. JNDI ошондо да Java an objectилерине алыстан кирүүгө мүмкүндүк берет. Типтүү окуя. Java EE serverинин администратору реестрге маалымат базасына туташуу үчүн керектүү маалыматты сактаган an objectти жайгаштырат. Демек, маалымат базасы менен иштөө үчүн биз жөн гана JNDI дарагынан керектүү an objectти сурайбыз жана аны менен иштейбиз. Бул абдан ыңгайлуу. Ыңгайлуулук ошондой эле ишкананы өнүктүрүүдө ар кандай чөйрөлөр бар экендигинде. Өндүрүш serverлери бар жана тест serverлери бар (жана көбүнчө 1ден ашык тест serverлери бар). Андан кийин, JNDI ичиндеги ар бир serverге маалымат базасына туташуу үчүн an objectти жайгаштыруу жана бул an objectти биздин тиркемеде колдонуу менен, биздин тиркемени бир serverден (сыноо, чыгаруу) экинчисине жайылтууда эч нерсени өзгөртүүгө туура келбейт. Бардык жерде маалымат базасына кирүү мүмкүнчүлүгү болот. Мисал, албетте, бир аз жөнөкөйлөштүрүлгөн, бирок JNDI эмне үчүн керек экенин жакшыраак түшүнүүгө жардам берет деп үмүттөнөм. Андан кийин, биз Javaдагы JNDI менен жакындан таанышабыз, кээ бир кол салуу элементтери менен.

JNDI API

JNDI Java SE платформасында берилет. JNDI колдонуу үчүн JNDI класстарын, ошондой эле ат коюу жана каталог кызматтарына кирүү үчүн бир же бир нече кызмат көрсөтүүчүлөрдү импорттооңуз керек. JDK төмөнкү кызматтар үчүн кызмат көрсөтүүчүлөрдү камтыйт:
  • Lightweight Directory Access Protocol (LDAP);
  • Common Object Request Broker Architecture (CORBA);
  • Common Object Services (COS) аталышы кызматы;
  • Java Remote Method Invocation (RMI) реестри;
  • Домендик аталыш кызматы (DNS).
JNDI API codeу бир нече пакеттерге бөлүнөт:
  • javax.naming;
  • javax.naming.directory;
  • javax.naming.ldap;
  • javax.naming.event;
  • javax.naming.spi.
Биз JNDIге киришибизди эки интерфейс менен баштайбыз - Аты жана Контекст, алар негизги JNDI функцияларын камтыйт

Interface Name

Name интерфейси компоненттердин аталыштарын, ошондой эле JNDI атоо синтаксисин башкарууга мүмкүндүк берет. JNDIде бардык ат жана каталог операциялары контекстке салыштырмалуу аткарылат. Абсолюттук тамырлар жок. Ошондуктан, JNDI InitialContext аныктайт, ал ат коюу жана каталог операциялары үчүн баштапкы чекитти камсыз кылат. Баштапкы контекстке жеткенден кийин, аны an objectтерди жана башка контексттерди издөө үчүн колдонсо болот.
Name objectName = new CompositeName("java:comp/env/jdbc");
Жогорудагы codeдо биз кандайдыр бир an object жайгашкан аталышты аныктадык (ал жайгашкан эмес, бирок биз ага ишенебиз). Биздин түпкү максатыбыз – бул an objectке шилтеме алуу жана аны программабызда колдонуу. Ошентип, аты сызык менен бөлүнгөн бир нече бөлүктөн (же токендерден) турат. Мындай токендер контекст деп аталат. Биринчиси жөн гана контекст, андан кийинкилердин баары субконтекст (мындан ары субконтекст деп аталат). Эгер сиз аларды каталогдорго же каталогдорго окшош же кадимки папкаларга окшош деп ойлосоңуз, контексттерди түшүнүү оңой болот. Түпкү контекст түпкү папка болуп саналат. Субконтекст – бул подконтекст. Берилген аталыштын бардык компоненттерин (контекст жана субконтексттер) төмөнкү codeду иштетүү менен көрө алабыз:
Enumeration<String> elements = objectName.getAll();
while(elements.hasMoreElements()) {
  System.out.println(elements.nextElement());
}
чыгаруу төмөнкүдөй болот:

java:comp
env
jdbc
Чыгуу аталыштагы токендер бири-биринен сызык менен бөлүнгөнүн көрсөтүп турат (бирок, биз муну айтканбыз). Ар бир аталыш белгисинин өзүнүн индекси бар. Токенди индекстөө 0дөн башталат. Түп контекстинде индекс нөл, кийинки контекстте индекс 1, кийинкиде 2 ж.б. Биз анын индекси боюнча субконтекст атын ала алабыз:
System.out.println(objectName.get(1)); // -> env
Биз кошумча белгилерди кошо алабыз (аягында же индекстин белгилүү бир жеринде):
objectName.add("sub-context"); // Добавит sub-context в конец
objectName.add(0, "context"); // Добавит context в налачо
Методдордун толук тизмеси расмий documentациядан тапса болот .

Интерфейс контексти

Бул интерфейс контекстти инициализациялоо үчүн константалардын жыйындысын, ошондой эле контексттерди түзүү жана жок кылуу, an objectтерди атка байлоо жана an objectтерди издөө жана алуу үчүн методдордун жыйындысын камтыйт. Келгиле, бул интерфейстин жардамы менен аткарылуучу кээ бир операцияларды карап көрөлү. Эң кеңири таралган иш - бул an objectти аты боюнча издөө. Бул ыкмаларды колдонуу менен жүзөгө ашырылат:
  • Object lookup(String name)
  • Object lookup(Name name)
Объектти атка байлоо төмөнкү ыкмалар менен ишке ашырылат bind:
  • void bind(Name name, Object obj)
  • void bind(String name, Object obj)
Эки ыкма тең аталыштын аталышын an objectке байланыштырат.Байланыштыруу Object - an objectти аталыштан ажыратуу боюнча тескери операция төмөнкү ыкмаларды колдонуу менен ишке ашырылат unbind:
  • void unbind(Name name)
  • void unbind(String name)
Методдордун толук тизмеси расмий documentация сайтында жеткorктүү .

InitialContext

InitialContextJNDI дарагынын тамыр элементин билдирген класс жана Context. Белгилүү бир түйүнгө салыштырмалуу JNDI дарагынын ичиндеги an objectтерди аты боюнча издөө керек. Мындай түйүн катары дарактын тамыр түйүнү кызмат кыла алат InitialContext. JNDI үчүн типтүү колдонуу учуру болуп саналат:
  • алуу InitialContext.
  • InitialContextJNDI дарагынан an objectтерди аты боюнча алуу үчүн колдонуңуз .
Аны алуунун бир нече жолу бар InitialContext. Мунун баары Java программасы жайгашкан чөйрөгө көз каранды. Мисалы, Java программасы жана JNDI дарагы бир эле колдонмо serverинде иштеп жатса, аны InitialContextалуу абдан жөнөкөй:
InitialContext context = new InitialContext();
Эгер андай болбосо, контекстти алуу бир аз кыйыныраак болот. Кээде контекстти инициализациялоо үчүн чөйрө касиеттеринин тизмесин тапшыруу керек болот:
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
    "com.sun.jndi.fscontext.RefFSContextFactory");

Context ctx = new InitialContext(env);
Жогорудагы мисал контекстти инициализациялоонун мүмкүн болгон жолдорунун бирин көрсөтөт жана башка семантикалык жүктү көтөрбөйт. Кодго майда-чүйдөсүнө чейин киришүүнүн кереги жок.

SpringBoot бирдиги тестинин ичинде JNDI колдонуунун мисалы

Жогоруда биз JNDI ат коюу жана каталог кызматы менен иштеши үчүн SPI (кызмат көрсөтүүчү интерфейси) болушу керек деп айттык, анын жардамы менен Java жана ат коюу кызматынын ортосунда интеграция ишке ашырылат. Стандарттык JDK бир нече түрдүү SPI менен келет (биз аларды жогоруда санаганбыз), алардын ар бири демонстрациялык максаттар үчүн анча кызыкчылык туудурbyte. JNDI жана Java тиркемесин контейнердин ичинде көтөрүү бир аз кызыктуу. Бирок, бул макаланын автору жалкоо адам, ошондуктан JNDI кантип иштээрин көрсөтүү үчүн, ал эң аз каршылыктын жолун тандап алды: JNDIди SpringBoot тиркеме бирдигинин сынагынын ичинде иштетиңиз жана Spring Framework'дун кичинекей бузукусун колдонуп JNDI контекстине кириңиз. Ошентип, биздин план:
  • Келгиле, бош жазгы Бут долбоорун жазалы.
  • Келгиле, бул долбоордун ичинде бирдик сынагын түзөлү.
  • Тесттин ичинде биз JNDI менен иштөөнү көрсөтөбүз:
    • контекстке мүмкүнчүлүк алуу;
    • JNDIде кандайдыр бир ат менен кандайдыр бир an objectти байлоо (байлоо);
    • an objectти анын аты боюнча алуу (издөө);
    • Объект нөл эмес экенин текшерип көрөлү.
Келгиле, ирети менен баштайлы. File->New->Project... Javaда JNDI колдонуу - 3 Андан кийин, Spring Initializr пунктун тандаңыз : Javaда JNDI колдонуу - 4Долбоор жөнүндө метаберorштерди толтуруңуз: Java'да JNDI колдонуу - 5Андан кийин керектүү Spring Framework компоненттерин тандаңыз. Биз кээ бир DataSource an objectтерин байлайбыз, андыктан маалымат базасы менен иштөө үчүн компоненттер керек:
  • JDBC API;
  • H2 DDatabase.
Javaда JNDI колдонуу - 6Келгиле, файл тутумунда жайгашкан жерди аныктайлы: Javaда JNDI колдонуу - 7Жана долбоор түзүлдү. Чынында, биз үчүн автоматтык түрдө бир бирдик тести түзүлдү, аны биз демонстрациялоо максатында колдонобуз. Төмөндө долбоордун түзүмү жана бизге керектүү тест: Javaда JNDI колдонуу - 8contextLoads тестинин ичинде code жазууну баштайлы. Жогоруда талкууланган Жазгы кичинекей хакерлик класс болуп саналат SimpleNamingContextBuilder. Бул класс JNDI бирдигинин тесттерин же өз алдынча тиркемелерди оңой көтөрүү үчүн иштелип чыккан. Контекстти алуу үчүн codeду жазалы:
final SimpleNamingContextBuilder simpleNamingContextBuilder
       = new SimpleNamingContextBuilder();
simpleNamingContextBuilder.activate();

final InitialContext context = new InitialContext();
Коддун биринчи эки саптары кийинчерээк JNDI контекстин оңой баштоого мүмкүндүк берет. Аларсыз, InitialContextинстанцияны түзүүдө өзгөчө учур ташталат: javax.naming.NoInitialContextException. Жоопкерчorктен баш тартуу. Класс SimpleNamingContextBuilder- эскирген класс. Жана бул мисал сиз JNDI менен кантип иштей аларыңызды көрсөтүүгө арналган. Булар JNDI ичиндеги бирдик тесттерин колдонуунун эң жакшы тажрыйбалары эмес. Бул контекстти куруу жана JNDIден an objectтерди байлоо жана алуу үчүн балдак деп айтууга болот. Контекстти алгандан кийин, биз андан an objectтерди чыгара алабыз же контексттен an objectтерди издей алабыз. Азырынча JNDIде эч кандай an object жок, андыктан ал жерге бир нерсе коюу логикалык болмок. Мисалы, DriverManagerDataSource:
context.bind("java:comp/env/jdbc/datasource", new DriverManagerDataSource("jdbc:h2:mem:mydb"));
DriverManagerDataSourceБул сапта класс an objectисин ат менен байланыштырдык java:comp/env/jdbc/datasource. Андан кийин an objectти контексттен аты менен ала алабыз. Биз жөн эле койгон an objectти алуудан башка аргабыз жок, анткени контекстте башка an objectтер жок =(
final DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/datasource");
Эми биздин DataSource байланышы бар экенин текшерип көрөлү (байланыш, туташуу же туташуу бул маалымат базасы менен иштөө үчүн иштелип чыккан Java классы):
assert ds.getConnection() != null;
System.out.println(ds.getConnection());
Эгерде биз бардыгын туура кылган болсок, натыйжа төмөнкүдөй болот:

conn1: url=jdbc:h2:mem:mydb user=
Коддун кээ бир саптары өзгөчөлүктөргө алып келиши мүмкүн деп айтууга болот. Төмөнкү саптар ыргытылат javax.naming.NamingException:
  • simpleNamingContextBuilder.activate()
  • new InitialContext()
  • context.bind(...)
  • context.lookup(...)
Ал эми класс менен иштөөдө DataSourceаны ыргытса болот java.sql.SQLException. Ушуга байланыштуу, блоктун ичиндеги codeду аткаруу керек try-catch, же сыноо бирдигинин кол тамгасында ал өзгөчөлүктү таштай алат деп көрсөтүү керек. Бул жерде тест классынын толук codeу болуп саналат:
@SpringBootTest
class JndiExampleApplicationTests {

    @Test
    void contextLoads() {
        try {
            final SimpleNamingContextBuilder simpleNamingContextBuilder
                    = new SimpleNamingContextBuilder();
            simpleNamingContextBuilder.activate();

            final InitialContext context = new InitialContext();

            context.bind("java:comp/env/jdbc/datasource", new DriverManagerDataSource("jdbc:h2:mem:mydb"));

            final DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/datasource");

            assert ds.getConnection() != null;
            System.out.println(ds.getConnection());

        } catch (SQLException | NamingException e) {
            e.printStackTrace();
        }
    }
}
Сыноону жүргүзгөндөн кийин, сиз төмөнкү журналдарды көрө аласыз:

o.s.m.jndi.SimpleNamingContextBuilder    : Activating simple JNDI environment
o.s.mock.jndi.SimpleNamingContext        : Static JNDI binding: [java:comp/env/jdbc/datasource] = [org.springframework.jdbc.datasource.DriverManagerDataSource@4925f4f5]
conn1: url=jdbc:h2:mem:mydb user=

Корутунду

Бүгүн биз JNDIди карадык. Биз ат коюу жана каталог кызматтары эмне экенин жана JNDI бул Java программасынын ар кандай кызматтары менен бирдей иштешүүгө мүмкүндүк берген Java API экенин билдик. Тактап айтканда, JNDI жардамы менен биз JNDI дарагына белгилүү бир ат менен an objectтерди жазып, ошол эле an objectтерди аты менен ала алабыз. Бонустук тапшырма катары, сиз JNDI кантип иштээрин мисал келтирсеңиз болот. Контекстке башка an objectилерди байлап, андан кийин бул an objectти аты менен окуңуз.
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION