Салам! Бүгүн биз сени JNDI менен тааныштырабыз. Келгиле, бул эмне, эмне үчүн керек, ал кантип иштейт, аны менен кантип иштей аларыбызды билели. Анан биз Spring Boot бирдигинин сынагын жазабыз, анын ичинде биз дал ушул JNDI менен ойнойбуз.
Киришүү. Ат коюу жана каталог кызматтары
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 жана файл тутумдарынан тышкары, башка көптөгөн кызматтар бар, мисалы:- Lightweight Directory Access Protocol (LDAP) ;
- CORBA атоо кызматы ;
- Network Information Service (NIS) ;
- Жана башкалар.
JNDI
JNDI, же Java аталышы жана каталог интерфейси, ат коюу жана каталог кызматтарына кирүү үчүн Java API болуп саналат. JNDI – бул Java программасы үчүн ар кандай ат коюу жана каталог кызматтары менен иштешүү үчүн бирдиктүү механизмди камсыз кылган API. Капчыктын астында JNDI менен ар кандай кызматтын ортосундагы интеграция Кызмат Провайдеринин Interface (SPI) аркылуу ишке ашат. SPI ар кандай аталыштарды жана каталог кызматтарын ачык туташтырууга мүмкүндүк берет, бул Java тиркемесинин туташкан кызматтарга кирүү үчүн JNDI API колдонуусуна мүмкүндүк берет. Төмөндөгү сүрөт JNDI архитектурасын көрсөтөт:Булак: Oracle Java Tutorials
JNDI. Жөнөкөй сөздөр менен мааниси
Негизги суроо: бизге JNDI эмне үчүн керек? JNDI биз Java an objectисин Java codeунан an objectтердин кээ бир "Каттоосунан" ушул an objectке байланышкан an objectтин аты менен ала алышыбыз үчүн керек. Кайталанган сөздөрдүн көптүгү бизди адаштырбашы үчүн жогорудагы сөздү тезистерге бөлүп карайлы:- Акыр-аягы, биз Java an objectисин алышыбыз керек.
- Биз бул an objectти кандайдыр бир реестрден алабыз.
- Бул реестрде бир топ an objectилер бар.
- Бул реестрдеги ар бир an objectтин уникалдуу аталышы бар.
- Объектти реестрден алуу үчүн, биз өтүнүчүбүзгө ат коюшубуз керек. «Баланча ат менен эмнең бар болсо мага бер» дегендей.
- Биз реестрден an objectтерди аты боюнча гана окубастан, ошондой эле бул реестрдеги an objectтерди белгилүү бир аталыштар менен сактай алабыз (кандайдыр бир жол менен алар ошол жерде болот).
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).
- javax.naming;
- javax.naming.directory;
- javax.naming.ldap;
- javax.naming.event;
- javax.naming.spi.
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)
Object
- an objectти аталыштан ажыратуу боюнча тескери операция төмөнкү ыкмаларды колдонуу менен ишке ашырылат unbind
:
void unbind(Name name)
void unbind(String name)
InitialContext
InitialContext
JNDI дарагынын тамыр элементин билдирген класс жана Context
. Белгилүү бир түйүнгө салыштырмалуу JNDI дарагынын ичиндеги an objectтерди аты боюнча издөө керек. Мындай түйүн катары дарактын тамыр түйүнү кызмат кыла алат InitialContext
. JNDI үчүн типтүү колдонуу учуру болуп саналат:
- алуу
InitialContext
. InitialContext
JNDI дарагынан 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ти анын аты боюнча алуу (издөө);
- Объект нөл эмес экенин текшерип көрөлү.
- JDBC API;
- H2 DDatabase.
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=
GO TO FULL VERSION