JavaRush /Java Blogu /Random-AZ /Salam Dünyadan Spring Web MVC-yə qədər və servletlərin bu...
Viacheslav
Səviyyə

Salam Dünyadan Spring Web MVC-yə qədər və servletlərin bununla nə əlaqəsi var

Qrupda dərc edilmişdir
Salam Dünyadan Spring Web MVC-yə qədər və servletlərin bununla nə əlaqəsi var - 1

Giriş

Bildiyimiz kimi, Java-nın uğuru məhz şəbəkəyə qoşulmağa çalışan proqram təminatının təkamülü sayəsində əldə edilmişdir. Buna görə də, biz adi “ Salam Dünya ” konsol tətbiqini əsas götürəcəyik və onun konsol proqramından şəbəkə proqramı olmaq üçün nəyə ehtiyacı olduğunu anlayacağıq. Beləliklə, əvvəlcə Java layihəsi yaratmalısınız. Proqramçılar tənbəl insanlardır. Tarixdən əvvəlki dövrlərdə, bəziləri mamont ovlayanda, digərləri oturub Java kitabxanalarının və kataloq strukturlarının müxtəlifliyində çaşqınlıq yaratmamağa çalışırdılar. Tərtibatçı proqram yaratma prosesinə nəzarət edə bilsin ki, o, sadəcə olaraq “Mən filan versiya 2-nin kitabxanası istəyirəm” yaza bilsin, onlar xüsusi alətlər - sistemlər qurmaqla çıxış etdilər. Ən məşhur ikisi MavenQradldır . Bu məqalə üçün biz Gradle istifadə edəcəyik. Əgər əvvəllər kataloq strukturunu özümüz yaratmalı olsaydıq, indi Gradle, Gradle Init Plugin-dən istifadə edərək, bir komandada kataloq strukturu və əsas Main sinfi olan Java layihəsi yaratmağa imkan verir: gradle init --type java-application Bu əmr üçün başlanğıc (init) həyata keçirir. bizə Hello World konsolu ilə Java proqramı (java tətbiqi). Tamamlandıqdan sonra qovluqda bir fayl görünəcək - build.gradle . Bu, bizim qurma skriptimizdir - yəni bunun üçün hansı hərəkətlərin edilməli olduğunu təsvir edən bir proqram yaratmaq üçün müəyyən bir skript. Gəlin onu açıb ona sətir əlavə edək: jar.baseName = 'webproject' Gradle bir layihədə müxtəlif hərəkətləri yerinə yetirməyə imkan verir və bu hərəkətlərə tapşırıqlar deyilir . Əmri (tapşırığı) yerinə yetirməklə /build/libsgradle build qovluğunda JAR faylı yaradılacaq . Və təxmin etdiyiniz kimi, onun adı indi webproject.jar olacaq . Amma yerinə yetirsək , xəta alacağıq: . Bunun səbəbi, java proqramı üçün müəyyən bir manifest əlavə etmək lazımdır - bu, proqramla necə işləmək, onu necə qavramaq təsviridir. Daha sonra java proqramını icra edəcək JVM proqrama giriş nöqtəsinin hansı sinif olduğunu və digər məlumatları (məsələn, classpath) biləcək. Quraşdırma skriptinin məzmununa daha yaxından nəzər salsaq, plaginlərin qoşulduğunu görəcəyik. Məsələn: Gradle Java Plugin səhifəsinə keçsək , manifesti konfiqurasiya edə biləcəyimizi görə bilərik: java -jar ./build/libs/webproject.jarno main manifest attributeapply plugin: 'java'
jar {
    manifest {
        attributes 'Main-Class': 'App'
    }
}
Əsas sinif, proqrama giriş nöqtəsi bizim üçün Gradle Init Plugin tərəfindən yaradılmışdır. Və hətta mainClassName parametrində göstərilmişdir. Amma bu bizə yaraşmadı, çünki... bu parametr başqa plaginə, Gradle Tətbiq Pluginə aiddir . Beləliklə, ekranda Hello World-i göstərən Java proqramımız var. Bu Java proqramı JAR-da (Java Arxiv) qablaşdırılıb. Sadə, konsol əsaslıdır, müasir deyil. Onu veb tətbiqinə necə çevirmək olar?
От Hello World до Spring Web MVC и при чём тут сервлеты - 2

Servlet API

Java-nın şəbəkə ilə işləyə bilməsi üçün qədim zamanlarda Servlet API adlı spesifikasiya ortaya çıxdı . Məhz bu spesifikasiya müştəri-server qarşılıqlı əlaqəsini, müştəridən mesajın alınmasını (məsələn, brauzer) və cavabın göndərilməsini (məsələn, səhifənin mətni ilə) təsvir edir. Təbii ki, o vaxtdan bəri çox şey dəyişdi, amma məsələ ondadır ki, Java proqramının veb tətbiqinə çevrilməsi üçün Servlet API istifadə olunur. Əsassız fərziyyələrə yol verməmək üçün gəlin həmin spesifikasiyanı götürək: JSR-000340 JavaTM Servlet 3.1 . İlk növbədə, bizi " Fəsil 1: İcmal " maraqlandırır . Anlamalı olduğumuz əsas anlayışları təsvir edir. Birincisi, servlet nədir? " 1.1 Servlet nədir? " bölməsində deyilir ki, Servlet konteyner tərəfindən idarə olunan və dinamik məzmun yaradan Java komponentidir. Digər Java komponentləri kimi, servlet də bayt koduna yığılmış və Java texnologiyasından istifadə edərək veb serverə yüklənə bilən Java sinfidir. Servlet Konteyneri tərəfindən həyata keçirilən sorğu/cavab paradiqması çərçivəsində servletlərin veb müştəri (məsələn, brauzer) ilə qarşılıqlı əlaqədə olması vacibdir. Servletlərin bir növ Servlet Konteynerində yaşadığı ortaya çıxır. Bu nədir? " 1.2 Servlet Konteyneri nədir? " bölməsində deyilir ki, Servlet Konteyneri sorğuların göndərildiyi və cavabların göndərildiyi şəbəkə xidmətlərini təmin edən veb server və ya proqram serverinin bir hissəsidir. Bu Servlet Konteyneri servletlərin həyat dövrünü idarə edir. Bütün Servlet Konteynerləri minimum HTTP protokolunu dəstəkləməlidir, lakin digərlərini dəstəkləyə bilər. Məsələn, HTTPS. Servlet Konteynerinin servletlərin icra olunduğu mühitə təhlükəsizliklə bağlı hər hansı məhdudiyyətlər qoya bilməsi də vacibdir. Həmçinin vacibdir ki, “ 10.6 Veb Tətbiq Arxivi Faylı ”na uyğun olaraq veb proqram WAR (Veb Arxivi) faylında paketlənməlidir. Yəni, indi başqa bir şey üçün jar və proqram plaginlərimizi çıxarmalıyıq. Və bu Gradle WAR plaginidir . Və jar.baseName əvəzinə war.baseName yazın Çünki Artıq jar plaginindən istifadə etmədiyimiz üçün manifest parametrlərini də sildik. JAR-ı işə saldığımız zaman Java Virtual Maşını (JVM) proqramımızla necə işləmək barədə manifest vasitəsilə izah edilməli idi. Çünki JVM onu idarə edirdi. Veb tətbiqi, görünür, bir növ veb server tərəfindən icra olunur. Belə çıxır ki, o, birtəhər ona veb proqramımızla necə işləməyi söyləməlidir? Və belə çıxır ki, bəli. Veb proqramların öz xüsusi manifestləri var. Bu Deployment Descriptor adlanır . Bütün bölmə ona həsr olunub: “ 14. Deployment Descriptor ”. Əhəmiyyətli bir bölmə var: " Fəsil 10:". Servlet API nöqteyi-nəzərindən veb tətbiqinin nə olmasından bəhs edir. Məsələn, " 10.5 Directory Structure " fəslində Yerləşdirmə Deskriptorunun harada olması göstərilib: /WEB-INF/web.xml. WEB-INF-i harada yerləşdirmək lazımdır? Gradle WAR plaginində deyildiyi kimi o, yeni layout əlavə edir : src/main/webapp.Ona görə də belə bir kataloq yaradaq, daxildə biz WEB-INF kataloqu, daxilində isə web.xml faylı yaradaq.Kataloqun olması vacibdir. META-INF deyil, WEB-INF adlanır! Gəlin onu " 14.5.1 Əsas Nümunə " XML nümunəsindən kopyalayaq :
От Hello World до Spring Web MVC и при чём тут сервлеты - 3
Gördüyümüz kimi konfiqurasiya üçün XML sənədindən istifadə olunur. XML sənədi etibarlı (Valid) sayılmaq üçün bəzi "sxem"ə uyğun olmalıdır. Bunu XML sənədi üçün bir növ interfeys kimi düşünə bilərsiniz. Sxem XML sənədində hansı elementlərin ola biləcəyini, hansı növ məlumatın elementi, sifarişi, tələbi və digər aspektləri müəyyən edə biləcəyini müəyyən edir. Sənədlərdən kopyalanan nümunə 2.5 versiyasını göstərir, lakin biz 3.1 versiyasını istifadə etmək istəyirik. Təbii ki, versiyalar dəyişdikcə spesifikasiya dəyişdi və yeni xüsusiyyətlər əlavə edildi. Buna görə də, 2.5 versiyası (web-app_2_5.xsd) üçün istifadə olunan sxemdən başqa bir sxemdən istifadə etməlisiniz. 3.1 versiyası üçün hansı sxemdən istifadə etməliyəm? Sənədlər bu işdə bizə kömək edəcək, “ 14.3 Yerləşdirmə Deskriptoru ”, yəni specification is available at http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd biz sxemə olan keçidi hər yerdə göstərilən xsd ilə əvəz etməliyik, onu version="2.5"3.1-ə dəyişdirməyi unutmamalıyıq, həmçinin hər yerdə ad sahəsini dəyişdirməliyik ( xmlns və xsi:schemaLocation-da). Onlar hansı ad məkanında işləyəcəyimizi göstərir (sadə dillə desək, hansı element adlarından istifadə edə bilərik). Sxem faylını açsanız, targetNamespace bizim təyin etməmiz lazım olan eyni ad sahəsini ehtiva edəcək:
От Hello World до Spring Web MVC и при чём тут сервлеты - 4
Xatırladığımız kimi, Manifest of Jar faylında hansı sinifdən istifadə etmək istədiyimizi yazmışdıq. Burada nə etməli? Burada veb müştəridən sorğu aldıqda hansı servlet sinfindən istifadə etmək istədiyimizi müəyyən etməliyik. Təsviri " 14.4 Yerləşdirmə Deskriptoru Diaqramı " bölməsində oxumaq olar . Bu belə görünəcək:
От Hello World до Spring Web MVC и при чём тут сервлеты - 5
Burada hər şey sadədir. Serverlet elan edilir və sonra müəyyən bir şablona uyğunlaşdırılır. Bu halda, /app-da. Şablon icra edildikdə, servlet metodu icra ediləcək. Gözəllik üçün, xml konfiqurasiyasını düzəltməyi unutmadan, Tətbiq sinfi paketə köçürülməlidir. Ancaq bu hamısı deyil. Tətbiq servlet olmalıdır. Servlet olmaq nə deməkdir? Bu o deməkdir ki, biz HttpServlet -dən miras almalıyıq . Nümunəni " 8.1.1 @WebServlet " bölməsində görmək olar . Buna görə, Tətbiq sinifimiz belə görünəcək:
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);
		}
	}
}
Amma layihəmiz hələ hazır deyil. Çünki biz indi Servlet API 3.1 versiyasından asılıyıq. Bu o deməkdir ki, qurma skriptimizdə Servlet API-dən asılılığı göstərməliyik. JVM bilməlidir ki, kodda yazdıqlarınız düzgündür və ondan necə istifadə olunur. Xatırladığımız kimi, spesifikasiya əslində hamısının necə işləməsini təsvir edən interfeyslərdir. Və tətbiqlər veb server tərəfində yerləşir. Buna görə də, Servlet API olmadan Maven Central-da tələb olunan kitabxananı tap olacaq: javax.servlet-api . Və asılılıqlar blokuna giriş əlavə edin . Maven deposunda, gördüyünüz kimi, təmin edilmişdir deyir. Asılılıqdan istifadə etməzdən əvvəl əhatə dairəsini təyin etməlisiniz. Gradle-in "təmin edilmiş" adlı əhatə dairəsi yoxdur, lakin onun " yalnız tərtib etmək " əhatə dairəsi var. Buna görə də bildirəcəyik: providedCompile 'javax.servlet:javax.servlet-api:3.1.0' Uh, hər şey qaydasındadır? Gradle Build bizim layihəmizi WAR faylına çevirəcək. Və bundan sonra onunla nə etməliyik? Birincisi, bizə Web Server lazımdır. Google-da biz “ web server java list ” yazırıq və veb serverlərin siyahısını görürük. Gəlin bu siyahıdan seçək, məsələn, TomCat . Apache Tomcat veb saytına daxil olun , ən son versiyanı (hazırda 9-cu versiya) zip arxivi kimi yükləyin (əgər Windows üçün). Onu hansısa qovluğa çıxarın. Hörmətli, bizim veb serverimiz var. Bin alt qovluğundakı veb server kataloqundan biz əmr satırından catalina icra edirik və mövcud variantları görürük. edək: catalina start. Hər bir veb-serverin nəzarət etdiyi bir kataloqu var. Əgər orada veb proqram faylı görünürsə, veb server onu quraşdırmağa başlayır. Bu quraşdırma yerləşdirmə və ya yerləşdirmə adlanır . Bəli bəli, buna görə də " yerləşdirmə deskriptoru ". Yəni tətbiqi necə düzgün yerləşdirmək olar. Tomcat-da bu kataloq webapps- dır . Gəlin orada gradle build istifadə edərək etdiyimiz müharibəni kopyalayaq. Bundan sonra, jurnalda belə bir şey görəcəyik: Deployment of web application archive [tomcat\webapps\webproject.war] has finished in [время] ms Daha yaxşı başa düşmək üçün Tomcat qovluğunda \conf\tomcat-users.xmlaşağıdakı sətirləri əlavə edərək faylı redaktə edəcəyik:
От Hello World до Spring Web MVC и при чём тут сервлеты - 6
İndi serveri yenidən işə salırıq (catalina stop, catalina start) və ünvana gedirik.Burada http://127.0.0.1:8080/manager bütün proqramların yollarını görəcəyik. Veb layihəmizə çox güman ki, yol/webproject verilmişdir. Bu yol nədir? " 10.1 Veb-serverlər daxilində veb proqramlar " fəslindəki spesifikasiya veb proqramın proqram daxilində hansısa yol ilə əlaqəli olduğunu bildirir (bu halda, /webproject). Bu yoldan keçən bütün sorğular eyni ServletContext ilə əlaqələndiriləcək. Bu yola contextRoot da deyilir . Və " 10.2 ServletContext ilə Münasibət " ə əsasən , servlet konteyneri veb tətbiqi və ServletContext ilə bir-bir əlaqələndirir. Yəni hər bir veb tətbiqinin öz ServletContext var. ServletContext nədir ? Spesifikasiyada göstərildiyi kimi, ServletContext servletləri işlədikləri “ tətbiq görünüşü” ilə təmin edən obyektdir . Servlet Konteksti Servlet API spesifikasiyasının 4-cü Fəslində daha ətraflı təsvir edilmişdir. Təəccüblüdür ki, 3.1 versiyada olan Servlet API artıq web.xml-in mövcud olmasını tələb etmir. Məsələn, annotasiyalardan istifadə edərək servlet müəyyən edə bilərsiniz:
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");
    }
}
Həmçinin mövzuda tövsiyə olunur: " Java EE Müsahibə - JEE Servlet API (Suallar və Cavablar) ". Beləliklə, bir Servletimiz var - veb müştəriyə hansı cavabın veriləcəyinə cavabdehdir. İstifadəçidən sorğuları qəbul edən, servlet yolu ilə əldə edilmiş yola uyğun gələn və uyğunluq aşkar edilərsə, Servleti icra edən ServletContainerimiz var. Yaxşı. Bahar dünyanın bu mənzərəsində hansı yeri tutur ?

Spring Web MVC

Əla, bizim veb proqramımız var. İndi Baharı bağlamalıyıq. Bunu necə edə bilərik? Əvvəlcə Baharı layihənizə düzgün şəkildə necə bağlayacağınızı başa düşməlisiniz. Belə çıxır ki, əvvəllər bunu Bahar platforması layihəsinin sənədlərinə uyğun olaraq etmək mümkün idi , lakin indi “ Platforma dəstəklənən ömrünü 9 aprel 2019-cu ildə başa vuracaq ”, yəni bunu etmək məqsədəuyğun deyil. istifadə edin, çünki tezliklə daha dəstəklənməyəcək. Yeganə çıxış yolu " Platformanın istifadəçiləri Spring Boot-un asılılıq idarəçiliyindən istifadə etməyə təşviq olunur ". Buna görə də, Spring Boot sənədlərinə keçək . Aydınlaşdırım ki, biz Spring Boot-un özündən deyil, yalnız Spring Boot-dan Dependency Management-dən istifadə edirik. Yəni, Spring Boot layihəsi kitabxanaların hansı versiyalarının (o cümlədən, Spring MVC) istifadə edilməsi haqqında məlumat verə bilər. Orada 3.2 tapacağıq . Spring Boot-un asılılıq idarəçiliyindən ayrı-ayrılıqda istifadə . Sənədlərə əsasən, tikinti skriptinə aşağıdakıları əlavə edin:
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
    }
}
Gördüyünüz kimi, biz qeyd etdik apply false, yəni. Biz Spring Boot-un özündən istifadə etmirik, lakin oradan asılılıq idarəetməsindən istifadə edirik. Bu asılılıq idarəçiliyi həm də BOM adlanır - " Materiallar Hesabatı ". İndi biz Spring Web MVC layihəsinin özünü birləşdirməyə hazırıq. Spring Web MVC Spring Framework layihəsinin bir hissəsidir və biz " Web Servlet " bölməsi ilə maraqlanırıq . Quraşdırma skriptinə asılılığı əlavə edək: compile 'org.springframework:spring-webmvc'. Gördüyümüz kimi, əhatə dairəsini tərtib etdik, çünki veb server bizə Bahar təqdim etmir. Layihəmiz Bahar kitabxanasını öz içinə salmağa məcburdur. Sonra, " 1.2. DispatcherServlet " bölməsini oxumaq bizim üçün vacibdir , burada deyilir ki, Spring MVC " Ön nəzarətçi " nümunəsi ətrafında qurulur , burada konfiqurasiya və digər komponentlərə nümayəndə heyətini təmin edən bir növ mərkəzi servlet var. . Dispetçer dispetçer kimi tərcümə edilə bilər. Beləliklə, ilk növbədə web.xml-də elan edirik:
От Hello World до Spring Web MVC и при чём тут сервлеты - 7
Gördüyümüz kimi, bu, əslində Servlet API spesifikasiyasında müəyyən edilmiş müntəzəm Dinləyicidir. Daha dəqiq desək, bu, ServletContextListener-dir, yəni veb tətbiqimiz üçün Servlet Kontekstini işə salmaq üçün işə salınıb. Sonra, Spring-ə parametrləri olan xüsusi xml konfiqurasiyasının harada yerləşdiyini bildirəcək bir parametr təyin etməlisiniz:
От Hello World до Spring Web MVC и при чём тут сервлеты - 8
Gördüyünüz kimi, bu, Servlet Kontekst səviyyəsində saxlanılan, lakin Tətbiq Kontekstini işə salarkən Spring tərəfindən istifadə ediləcək adi bir parametrdir. İndi bütün servletlər əvəzinə bütün digər sorğuları paylayan bir dispetçer elan etməlisiniz.
От Hello World до Spring Web MVC и при чём тут сервлеты - 9
Və burada heç bir sehr yoxdur. Baxsaq, bu, HttpServlet-dir, burada Spring onu çərçivəyə çevirən çox şey edir. Qalan yalnız xüsusi URL şablonunu servletlə əlaqələndirməkdir (xəritə etmək):
От Hello World до Spring Web MVC и при чём тут сервлеты - 10
Hər şey əvvəl etdiyimiz kimidir. İndi veb serverimizin göstərməli olduğu bir şey yaradaq. Məsələn, WEB-INF-də səhifələr alt kataloqu yaradaq və hello.jsp faylı olacaq. Məzmun ən primitiv ola bilər. Məsələn, html teqlərinin içərisində " Salam Dünya " mətni ilə h1 teqi var . applicationContext.xmlDaha əvvəl qeyd etdiyimiz faylı yaratmağı unutmayın . Spring sənədlərindən bir nümunə götürək: " 1.10.3. Avtomatik olaraq siniflərin aşkarlanması və paxla təriflərinin qeydiyyatı ".
От Hello World до Spring Web MVC и при чём тут сервлеты - 11
Çünki biz bu şəkildə avtomatik aşkarlamağı işə salırıq, indi 2 sinif yarada bilərik (xüsusi Bahar annotasiyalarının istifadəsi səbəbindən onlar Bahar paxlası hesab ediləcək), Spring indi özünü yaradacaq və onların köməyi ilə tətbiqimizi fərdiləşdirəcək:
  1. Veb konfiqurasiyası, məsələn, Java stil konfiqurasiyası:

    @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();
        }
    }

    Bu nümunə Spring Framework sənədlərində təsvir edilmişdir: " 1.11. MVC Config ".

    Burada biz jsp səhifələrinin harada yerləşdiyini müəyyən etməyə kömək edəcək ViewResolver-i qeydiyyatdan keçiririk. İkinci üsul " Defolt servletin " aktiv olmasını təmin edir.

    Bu barədə daha ətraflı burada oxuya bilərsiniz: " Default-servlet-handler ehtiyacı və istifadəsi nədir ".

  2. Müəyyən bir JSP-yə sorğuların xəritələşdirilməsini təsvir etmək üçün HelloController nəzarətçisi

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

    Burada biz " 1.4. Annotasiya edilmiş Nəzarətçilər " bölməsindəki sənədlərdə təsvir edilən @Controller annotasiyasından istifadə etdik .

İndi, tətbiqimiz yerləşdirildikdə, sorğu göndərdiyimiz zaman /webproject/hello(burada /webproject kontekst köküdür), əvvəlcə DispatcherServlet işlənəcək. O, əsas dispetçer olaraq müəyyən edəcək ki, biz /* cari sorğuya uyğun gəlir, yəni DispatcherServlet nəsə etməlidir. Sonra tapdığı bütün xəritələrdən keçəcək. O, /hello ilə əlaqələndirilmiş və onu icra edəcək tutacaq metodu ilə HelloController olduğunu görəcək. Bu üsul "salam" mətnini qaytaracaq. Bu mətn ViewResolver tərəfindən qəbul ediləcək və serverə müştəriyə göstərilməli olan jsp fayllarını harada axtarmaq lazım olduğunu söyləyəcək. Beləliklə, müştəri nəticədə o çox əziz səhifəni alacaq.

Nəticə

Ümid edirəm ki, məqalədən “kontekst” sözünün qorxulu olmadığı aydın olacaq. Bu spesifikasiyaların çox faydalı olduğu ortaya çıxır. Sənədləşmə isə düşmənimiz deyil, dostumuzdur. Ümid edirəm ki, Baharın nəyə əsaslandığı, necə bağlandığı və Servlet API-nin bununla nə əlaqəsi olduğu aydın olacaq.
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION