JavaRush /Java блогу /Random-KY /Hello Worldден Spring Web MVCге чейин жана сервлеттердин ...
Viacheslav
Деңгээл

Hello Worldден Spring Web MVCге чейин жана сервлеттердин ага кандай тиешеси бар

Группада жарыяланган
Hello Worldден Spring Web MVCге чейин жана сервлеттердин ага кандай тиешеси бар - 1

Киришүү

Белгилүү болгондой, Java программасынын ийгorги так тармакка туташуу үчүн аракет кылган программалык камсыздоонун эволюциясынын аркасында келди. Ошондуктан, биз кадимки " Hello World " консолдук тиркемесин негиз кылып алабыз жана консолдук тиркемеден тармактык тиркеме болуу үчүн эмне керек экенин түшүнөбүз. Ошентип, адегенде Java долбоорун түзүшүңүз керек. Программисттер жалкоо адамдар. Тарыхка чейинки мезгилде, кээ бирлери мамонтторго аңчылык кылып жүргөндө, башкалары отуруп, Java китепканаларынын жана каталогдорунун ар түрдүү структураларында адашып калбоого аракет кылышкан. Иштеп чыгуучу тиркемени түзүү процессин көзөмөлдөй алышы үчүн, ал жөн гана "Мен 2-versionнын китепканасын каалайм" деп жаза алышы үчүн, алар атайын куралдарды ойлоп табышты - системаларды куруу. Эң атактуу экөө Maven жана Gradle болуп саналат . Бул макала үчүн биз Gradle колдонобуз. Эгерде мурда каталог структурасын өзүбүз түзүшүбүз керек болсо, азыр Gradle, Gradle Init Plugin колдонуп, бир командада каталог түзүмү жана негизги Негизги классы бар Java долбоорун түзүүгө мүмкүндүк берет: gradle init --type java-application Бул команда үчүн инициализацияны (init) аткарат. бизге Hello World консолу менен Java тиркемеси (java-тиркеме). Аяктагандан кийин, каталогдо файл пайда болот - build.gradle . Бул биздин куруу скриптибиз - башкача айтканда, бул үчүн кандай аракеттерди жасоо керектиги сүрөттөлгөн тиркемени түзүү үчүн белгилүү скрипт. Келгиле, аны ачып, ага сызыкты кошолу: jar.baseName = 'webproject' Gradle сизге долбоордо ар кандай аракеттерди аткарууга мүмкүндүк берет жана бул аракеттер тапшырмалар деп аталат . Команданы (тапшырманы) аткаруу менен /build/libsgradle build каталогунда JAR файлы түзүлөт . Жана, сиз ойлогондой, анын аты азыр webproject.jar болот . Бирок биз аткарсак , ката алабыз: . Себеби, java тиркемеси үчүн манифестти тиркөө керек – бул тиркеме менен кантип иштөө, аны кандай кабыл алуу керектигинин сүрөттөлүшү. Ошондо java тиркемесин аткара турган JVM программага кирүү чекити кайсы класс экенин жана башка маалыматтарды (мисалы, класс жолу) билет. Эгерде биз куруу скриптинин мазмунун жакшыраак карап көрсөк, плагиндердин туташып жатканын көрөбүз. Мисалы: Эгер биз Gradle Java Plugin барагына барсак , манифестти конфигурациялай аларыбызды көрө алабыз: java -jar ./build/libs/webproject.jarno main manifest attributeapply plugin: 'java'
jar {
    manifest {
        attributes 'Main-Class': 'App'
    }
}
Негизги класс, программага кирүү чекити биз үчүн Gradle Init Plugin тарабынан түзүлгөн. Ал тургай, mainClassName параметринде көрсөтүлгөн. Бирок бул бизге туура келбеди, анткени... бул жөндөө башка плагинге, Gradle Колдонмо плагинине тиешелүү . Ошентип, бизде экранда Hello World көрсөтүүчү Java тиркемеси бар. Бул Java колдонмосу JAR (Java Archive) ичинде топтолгон. Бул жөнөкөй, консолго негизделген, заманбап эмес. Аны кантип веб-тиркемеге айландыруу керек?
От Hello World до Spring Web MVC и при чём тут сервлеты - 2

Сервлет API

Java тармагы менен иштей алышы үчүн, байыркы убакта Servlet API деп аталган спецификация пайда болгон . Дал ушул спецификация кардар менен serverдин өз ара аракеттенүүсүн, кардардан билдирүү алууну (мисалы, браузер) жана жооп жөнөтүүнү (мисалы, барактын тексти менен) сүрөттөйт. Албетте, андан бери көп нерсе өзгөрдү, бирок Java тиркемесинин веб-тиркеме болушу үчүн Servlet API колдонулат. Негизсиз божомолдор болбош үчүн, келгиле, ошол спецификацияны тандап алалы: JSR-000340 JavaTM Servlet 3.1 . Биринчиден, бизди " 1-бөлүм: Обзор " кызыктырат. Ал биз түшүнүшүбүз керек болгон негизги түшүнүктөрдү сүрөттөйт. Биринчиден, сервлет деген эмне? " 1.1 Сервлет деген эмне? " бөлүмүндө Сервлет контейнер тарабынан башкарылуучу жана динамикалык мазмунду жаратуучу Java компоненти экени айтылат . Башка Java компоненттери сыяктуу эле, сервлет byte-codeго компиляцияланган жана Java технологиясын колдонуу менен веб-serverге жүктөлө турган Java классы. Сервлеттер Сервлет Контейнери ишке ашырган суроо-жооп парадигмасынын алкагында веб-кардар (мисалы, браузер) менен иштешүүсү маанилүү. Көрсө, Сервлеттер кандайдыр бир Сервлет Контейнеринде жашайт экен. Бул эмне? « 1.2 Сервлет контейнери деген эмне? » бөлүмүндө Сервлет контейнери суроо-талаптар жана жооптор жөнөтүлө турган тармактык кызматтарды камсыз кылган веб-serverдин же тиркеме serverинин кандайдыр бир бөлүгү деп айтылат . Бул Сервлет Контейнери сервлеттердин жашоо циклин башкарат. Бардык Сервлет Контейнерлер HTTP протоколун минималдуу колдоого алышы керек, бирок башкаларды колдошу мүмкүн. Мисалы, HTTPS. Сервлет Контейнери сервлеттер аткарылган чөйрөгө коопсуздукка байланыштуу кандайдыр бир чектөөлөрдү киргизе алышы да маанилүү. Ошондой эле " 10.6 Web Application Archive File " га ылайык веб тиркеме WAR (Web Archive) файлында топтолушу керек. Башкача айтканда, азыр биз башка нерсе үчүн биздин банканы жана тиркеме плагиндерин алып салышыбыз керек. Жана бул Gradle WAR плагини . Жана jar.baseName ордуна war.baseName белгилеңиз Анткени Биз мындан ары jar плагинди колдонбогондуктан, манифесттин жөндөөлөрүн да алып салдык. JARды ишке киргизгенибизде, Java Virtual Machine (JVM) биздин тиркеме менен кантип иштөө керектигин манифест аркылуу айтып беришибиз керек болчу. Анткени JVM аны иштетип жаткан. Веб-тиркеме, сыягы, кандайдыр бир веб-server тарабынан аткарылат. Көрсө, ал кандайдыр бир жол менен ага биздин веб-тиркеме менен кантип иштөө керектигин айтышы керекпи? Анан ооба экен. Веб-тиркемелердин өзүнүн атайын манифести бар. Бул Deployment Descriptor деп аталат . Бүтүндөй бөлүм ага арналган: “ 14. Жайгаштыруу Дескриптору ”. Маанилүү бөлүм бар: " 10-бөлүм:". Бул Servlet API көз карашы боюнча веб-тиркеме деген эмне экендиги жөнүндө сөз болот. Мисалы, " 10.5 Каталог структурасы " бөлүмүндө Жайгаштыруу Дескриптору кайда болушу керектиги көрсөтүлгөн: /WEB-INF/web.xml. WEB-INFти кайда жайгаштыруу керек? Gradle WAR плагинде айтылгандай, ал жаңы макетти кошот : src/main/webapp.Ошондуктан, ушундай каталогду түзөлү, ичинде биз WEB-INF каталогун, ал эми ичинде web.xml файлын түзөбүз.Каталогдун болушу маанилүү. META-INF эмес, WEB-INF деп аталат! Аны " 14.5.1 Негизги мисал " XML мисалынан көчүрүп алалы :
От Hello World до Spring Web MVC и при чём тут сервлеты - 3
Көрүнүп тургандай, конфигурациялоо үчүн XML documentи колдонулат. XML documentи жарактуу (жарактуу) деп табылышы үчүн кандайдыр бир "схема" менен шайкеш келиши керек. Сиз муну XML documentинин интерфейсинин бир түрү деп ойлосоңуз болот. Схема XML documentинде кандай элементтер болушу мүмкүн экенин, кандай типтеги маалыматтар элементти, тартипти, талапты жана башка аспектилерди аныктай аларын көрсөтөт. Документтен көчүрүлгөн мисал 2.5 versionсын көрсөтөт, бирок биз 3.1 versionсын колдонгубуз келет. Албетте, versionлар өзгөргөн сайын спецификация өзгөрүп, жаңы функциялар кошулду. Ошондуктан, сиз 2.5 versionсында колдонулган схемадан башка схеманы колдонушуңуз керек (web-app_2_5.xsd). 3.1 versionсы үчүн кандай схеманы колдонушум керек? Документация бизге бул жагынан жардам берет, " 14.3 Жайгаштыруу Дескриптору " бөлүмүндө Башкача айтканда , схемага шилтемени көрсөтүлгөн xsd менен бардык жерде алмаштыруу керек, аны 3.1ге specification is available at http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd өзгөртүүнү унутпашыбыз керек , ошондой эле бардык жерде аттар мейкиндигин өзгөртүү керек ( version="2.5"xmlns жана xsi:schemaLocation). Алар биз кайсы аттар мейкиндигинде иштей турганыбызды көрсөтөт (жөнөкөй сөз менен айтканда, кандай элементтин аталыштарын колдоно алабыз). Эгер сиз схема файлын ачсаңыз, targetNamespace биз көрсөтүүгө тийиш болгон аттар мейкиндигин камтыйт:
От Hello World до Spring Web MVC и при чём тут сервлеты - 4
Эсибизде болгондой, Jar файлынын манифестинде биз кайсы классты колдонгубуз келгенин жазганбыз. Бул жерде эмне кылуу керек? Бул жерде биз веб кардардан суроо-талапты алганыбызда кайсы сервлет классын колдонгубуз келгенин такташыбыз керек. Сүрөттөмөнү " 14.4 Жайгаштыруу Диаграммасы " бөлүмүндө окуса болот . Ал мындай болот:
От Hello World до Spring Web MVC и при чём тут сервлеты - 5
Бул жерде баары жөнөкөй. Серверлет жарыяланып, андан кийин ал белгилүү бир калыпка түшүрүлөт. Бул учурда, /app боюнча. Үлгү аткарылганда, сервлет ыкмасы аткарылат. Сулуулук үчүн, App классы xml конфигурациясын оңдоону унутпай, пакетке которулушу керек. Бирок бул баары эмес. Колдонмо сервлет болушу керек. Сервлет болуу деген эмнени билдирет? Бул HttpServletтен мурас алышыбыз керек дегенди билдирет . Мисалды " 8.1.1 @WebServlet " бөлүмүндө көрүүгө болот . Ага ылайык, биздин App классыбыз төмөнкүдөй болот:
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class App extends HttpServlet {
    public String getGreeting() {
        return "Hello world.";
    }

	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		response.setContentType("text/html");
		try {
			response.getWriter().println(getGreeting());
		} catch (IOException e) {
			throw new IllegalStateException(e);
		}
	}
}
Бирок биздин долбоор азырынча даяр эмес. Анткени биз азыр Servlet API 3.1 versionсынан көз карандыбыз. Бул биздин куруу скриптинде биз Servlet API'ге көз карандылыкты көрсөтүшүбүз керек дегенди билдирет. JVM codeго жазганыңыз туура экенин жана аны кантип колдонуу керектигин бorши керек. Эсибизде тургандай, спецификация бул баары кантип иштеши керектигин сүрөттөгөн интерфейстер. Ал эми ишке ашыруулар веб-server тарапта. Ошондуктан, Servlet APIсиз Maven Centralда керектүү китепкананы табуу болот: javax.servlet-api . Жана көз карандылык блогуна жазуу кошуңуз . Maven репозиторийинде, сиз көргөндөй, ал берилген деп айтылат. Көз карандылыкты колдонуудан мурун, сиз масштабды көрсөтүшүңүз керек. Gradle'де "берилген" деп аталган чөйрөсү жок, бирок анын " компиляция гана " чөйрөсү бар. Ошондуктан, биз көрсөтөбүз: providedCompile 'javax.servlet:javax.servlet-api:3.1.0' Уф, баары жакшы окшойт? Gradle Build биздин долбоорду WAR файлына түзөт. Анан аны менен эмне кылышыбыз керек? Биринчиден, бизге Web Server керек. Google'да биз " web server Java тизмеси " деп жазабыз жана веб-serverлердин тизмесин көрөбүз. Бул тизмеден тандап алалы, мисалы, TomCat . Apache Tomcat веб-сайтына өтүңүз , акыркы versionсын (учурда 9 versionсы) zip архиви катары жүктөп алыңыз (эгерде Windows үчүн). Аны кандайдыр бир каталогго ачыңыз. Уррай, бизде web server бар. Бин подкаталогундагы веб-server каталогунан биз буйрук сабынан каталинаны аткарабыз жана жеткorктүү варианттарды көрөбүз. кылалы: catalina start. Ар бир веб-serverде веб-server көзөмөлдөгөн каталог бар. Эгерде ал жерде веб-тиркеме файлы пайда болсо, веб-server аны орното баштайт. Бул орнотуу жайылтуу же жайылтуу деп аталат . Ооба ооба, ошондуктан " жайгаштыруу дескриптору ". Башкача айтканда, тиркемени кантип туура жайгаштыруу керек. Tomcatте бул каталог webapps болуп саналат . Келгиле, ошол жерде gradle build аркылуу жасаган согушту көчүрүп алалы. Андан кийин, журналда биз төмөнкүдөй нерсени көрөбүз: Deployment of web application archive [tomcat\webapps\webproject.war] has finished in [время] ms Дагы жакшыраак түшүнүү үчүн, tomcat каталогунда биз файлды оңдоп \conf\tomcat-users.xml, төмөнкү саптарды кошобуз:
От Hello World до Spring Web MVC и при чём тут сервлеты - 6
Эми serverди кайра иштетебиз (catalina stop, catalina start) жана дарекке барабыз.Бул http://127.0.0.1:8080/manager жерде биз бардык тиркемелердин жолдорун көрөбүз. Биздин веб-долбоорубузга /вебдолбоор жол берилген болушу мүмкүн. Бул кандай жол? " 10.1 Веб-serverлердин ичиндеги веб-тиркемелер " бөлүмүндөгү спецификацияда веб-тиркеме тиркеме ичиндеги кандайдыр бир жол менен байланышканын айтат (бул учурда, /webproject). Бул жол аркылуу бардык суроо-талаптар ошол эле ServletContext менен байланыштырылат. Бул жол contextRoot деп да аталат . Ал эми " 10.2 СервлетКонтекстке мамилеси " ылайык , сервлет контейнери веб тиркеме менен ServletContext менен бири-бирин байланыштырат. Башкача айтканда, ар бир веб тиркемесинин өзүнүн ServletContext бар. ServletContext деген эмне ? Спецификацияда айтылгандай, ServletContext бул сервлеттерге алар иштеп жаткан " колдонмонун көрүнүшүн" камсыз кылган an object . Servlet Контекст Servlet API спецификациясынын 4-бөлүмүндө кеңири сүрөттөлгөн. Таң калыштуусу, 3.1 versionсындагы Servlet API мындан ары web.xml болушун талап кылbyte. Мисалы, annotationларды колдонуу менен сервлетти аныктай аласыз:
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/app2")
public class App2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        response.getWriter().println("app2");
    }
}
Ошондой эле тема боюнча сунушталат: " Java EE интервью - JEE Servlet API (суроолор жана жооптор) ". Ошентип, бизде Сервлет бар - ал веб-кардарга кандай жооп берүү үчүн жооптуу. Бизде колдонуучудан суроо-талаптарды кабыл алган, сервлеттин жолу менен кирген жолго дал келген жана дал келүү табылса, Сервлетти аткара турган ServletContainer бар. Жакшы. Бул дүйнөнүн сүрөтүндө Жаз кайсы орунду ээлейт ?

Spring Web MVC

Жакшы, бизде веб тиркеме бар. Эми биз Жазды туташтырышыбыз керек. Муну кантип кыла алабыз? Биринчиден, сиз жазды долбооруңузга кантип туура туташтыруу керектигин аныкташыңыз керек. Көрсө, буга чейин муну Жазгы платформа долбоорунун documentтерине ылайык жасоого мүмкүн болчу , бирок азыр “ Платформа 2019-жылдын 9-апрелинде колдоого алынган мөөнөтүн аяктайт ”, башкача айтканда, бул максатка ылайыктуу эмес. колдонуңуз, анткени ал жакында колдоого алынбай калат. Чыгуунун жалгыз жолу - " Платформанын колдонуучулары Spring Boot'тун көз карандылыкты башкарууну колдонууну баштоого үндөшөт ". Ошондуктан, келгиле, Жазгы жүктөө documentтерине өтөбүз . Биз Spring Bootтун өзүн эмес, Жазгы жүктөөдөн көз карандылыкты башкарууну гана колдонушубузду түшүндүрүп берейин. Башкача айтканда, Spring Boot долбоору китепканалардын кайсы versionларын колдонуу керектиги жөнүндө бorм бере алат (анын ичинде Spring MVC). Ал жерден биз 3.2 табабыз . Spring Boot'тун көз карандылыкты башкарууну өзүнчө колдонуу . Документке ылайык, куруу сценарийине төмөнкүлөрдү кошуңуз:
plugins {
    id 'org.springframework.boot' version '2.0.4.RELEASE' apply false
}
apply plugin: 'io.spring.dependency-management'
Жана
dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}
Көрүнүп тургандай, биз көрсөткөн apply false, б.а. Биз Spring Boot'тун өзүн колдонбойбуз, бирок ошол жерден көз карандылыкты башкарууну колдонобуз. Бул көз карандылыкты башкаруу BOM деп да аталат - " Материалдар Билл ". Эми биз Spring Web MVC долбоорунун өзүн туташтырууга даярбыз. Spring Web MVC Spring Framework долбоорунун бир бөлүгү жана биз " Web Servlet " бөлүмүнө кызыкдарбыз . Куруу скриптине көз карандылыкты кошолу: compile 'org.springframework:spring-webmvc'. Көрүнүп тургандай, биз компиляциянын масштабын койдук, анткени веб-server бизге Жазды бербейт. Биздин долбоор Жаз китепканасын өзүнө киргизүүгө мажбур. Андан кийин, биз үчүн " 1.2. DispatcherServlet " бөлүмүн окуу маанилүү , анда Spring MVC " Фронт контроллер " үлгүсүнүн айланасында курулган деп айтылат , бул жерде конфигурацияны жана башка компоненттерге өкүлчүлүктү камсыз кылган кандайдыр бир борбордук сервлет бар. . Диспетчерди диспетчер деп которууга болот. Ошентип, биринчи кезекте, web.xml ичинде биз жарыялайбыз:
От Hello World до Spring Web MVC и при чём тут сервлеты - 7
Көрүнүп тургандай, бул чындыгында Servlet API спецификациясында аныкталган кадимки угуучу. Тагыраак айтканда, бул ServletContextListener, башкача айтканда, ал биздин веб тиркеме үчүн Сервлет Контекстти инициализациялоо үчүн иштетилген. Андан кийин, жазгы орнотуулары менен анын атайын xml конфигурациясы кайда жайгашканын айтып турган жөндөөнү көрсөтүшүңүз керек:
От Hello World до Spring Web MVC и при чём тут сервлеты - 8
Көрүнүп тургандай, бул жөн гана Сервлет Контекст деңгээлинде сакталган кадимки жөндөө, бирок Колдонмо Контекстти инициализациялоодо Жаз тарабынан колдонула турган жөндөө. Эми бардык сервлеттердин ордуна башка бардык суроо-талаптарды таратуучу бир диспетчерди жарыялоо керек.
От Hello World до Spring Web MVC и при чём тут сервлеты - 9
Жана бул жерде эч кандай сыйкыр жок. Эгерде биз карап көрсөк, бул HttpServlet, бул жерде Жаз көп нерселерди жасайт, бул алHow. Белгилүү бир URL шаблонун сервлет менен салыштыруу (карталоо) гана калды:
От Hello World до Spring Web MVC и при чём тут сервлеты - 10
Баары мурункудай эле. Эми биздин веб-server көрсөтө турган нерсени түзөлү. Мисалы, WEB-INF ичинде баракчалардын подкаталогун түзөлү, анда hello.jsp файлы болот. Мазмун эң примитивдүү болушу мүмкүн. Мисалы, html тегдердин ичинде " Hello World " тексти менен h1 теги бар . applicationContext.xmlЖана биз мурда белгилеген файлды түзүүнү унутпаңыз . Жазгы documentациядан бир мисалды алалы: " 1.10.3. Автоматтык түрдө класстарды аныктоо жана буурчак аныктамаларын каттоо ".
От Hello World до Spring Web MVC и при чём тут сервлеты - 11
Анткени биз автоматтык аныктоону ушундай жол менен иштетебиз, эми биз 2 класс түзө алабыз (алар Жазгы атайын annotationларды колдонуудан улам Жазгы буурчак болуп эсептелет), аларды Жаз эми өзү түзүп, алардын жардамы менен биздин тиркемени ыңгайлаштырат:
  1. Веб конфигурациясы, мисалы Java стorнин конфигурациясы:

    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.jsp("/WEB-INF/pages/", ".jsp");
        }
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
    }

    Бул мисал Spring Framework documentациясында сүрөттөлгөн: " 1.11. MVC Config ".

    Бул жерде биз jsp баракчалары кайда жайгашканын аныктоого жардам берген ViewResolverди каттайбыз. Экинчи ыкма " Демейки сервлет " иштетилгенин камсыздайт.

    Бул тууралуу кененирээк бул жерден окуй аласыз: " default-servlet-handler эмнеге муктаж жана колдонулушу керек ".

  2. HelloController контроллери белгилүү JSPге суроо-талаптардын картасын сүрөттөө үчүн

    @Controller
    public class HelloController {
        @GetMapping("/hello")
        public String handle(Model model) {
            return "hello";
        }
    }

    Бул жерде биз " 1.4. Аннотацияланган контроллерлор " бөлүмүндөгү documentацияда сүрөттөлгөн @Controller annotationсын колдондук .

Эми, биздин тиркеме орнотулганда, биз суроо-талапты жөнөткөндө /webproject/hello(бул жерде /webproject контексттик тамыр), биринчи кезекте DispatcherServlet иштетилет. Ал негизги диспетчер катары биздин /* учурдагы суроо-талапка дал келгенибизди аныктайт, бул DispatcherServlet бир нерсе кылышы керек дегенди билдирет. Андан кийин ал тапкан бардык карталарды карап чыгат. Ал /hello менен картага түшүрүлгөн тутка ыкмасы менен HelloController бар экенин көрөт жана аны аткарат. Бул ыкма "салам" текстин кайтарат. Бул текст ViewResolver тарабынан кабыл алынат, ал serverге кардарга көрсөтүлүшү керек болгон jsp файлдарын кайдан издөө керектигин айтып берет. Ошентип, кардар акыры ошол абдан кымбат баракты алат.

Корутунду

Макаладан «контекст» деген сөз коркунучтуу эмес экени айкын болот деп ишенем. Бул спецификациялар абдан пайдалуу болуп чыкты. Ал эми documentация биздин душманыбыз эмес, досубуз. Жаздын эмнеге негизделгени, ал кандайча туташып турганы жана Servlet API менен кандай байланышы бар экени түшүнүктүү болот деп үмүттөнөм.
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION