Apa yang dilakukan kerangka kerja Spring? Haruskah saya menggunakannya? Mengapa atau mengapa tidak?


237

Jadi, saya memulai proyek baru di Jawa, dan saya mempertimbangkan untuk menggunakan Spring. Mengapa saya mempertimbangkan Spring? Karena banyak orang bilang aku harus menggunakan Spring! Serius, setiap kali saya mencoba membuat orang menjelaskan apa sebenarnya Spring atau apa itu Spring, mereka tidak pernah bisa memberi saya jawaban langsung. Saya telah memeriksa intro di situs SpringSource, dan mereka sangat rumit atau sangat fokus pada tutorial, dan tidak ada yang memberi saya ide bagus mengapa saya harus menggunakannya, atau bagaimana hal itu akan membuat hidup saya lebih mudah. Kadang-kadang orang melempar istilah "injeksi ketergantungan", yang justru membuat saya semakin bingung, karena saya pikir saya memiliki pemahaman yang berbeda tentang arti istilah itu.

Bagaimanapun, ini sedikit tentang latar belakang dan aplikasi saya:

Sudah berkembang di Jawa untuk sementara waktu, melakukan pengembangan web back-end. Ya, saya melakukan banyak pengujian unit. Untuk memfasilitasi ini, saya biasanya membuat (setidaknya) dua versi metode: yang menggunakan variabel contoh, dan yang hanya menggunakan variabel yang diteruskan ke metode. Yang menggunakan variabel instan memanggil yang lain, memasok variabel instan. Ketika tiba saatnya untuk unit test, saya menggunakan Mockito untuk mengejek objek dan kemudian melakukan panggilan ke metode yang tidak menggunakan variabel instan. Inilah yang selalu saya pahami sebagai "injeksi ketergantungan".

Aplikasi saya cukup sederhana, dari perspektif CS. Proyek kecil, 1-2 pengembang untuk memulai. Sebagian besar operasi tipe CRUD dengan banyak pencarian dilemparkan. Pada dasarnya sekelompok layanan web yang tenang, ditambah front-end web dan akhirnya beberapa klien seluler. Saya sedang berpikir untuk melakukan front-end dalam HTML / CSS / JS / JQuery lurus, jadi tidak ada rencana nyata untuk menggunakan JSP. Menggunakan Hibernate sebagai ORM, dan Jersey untuk mengimplementasikan layanan web.

Saya sudah mulai membuat kode, dan saya sangat ingin mendapatkan demo di luar sana sehingga saya bisa berkeliling dan melihat apakah ada yang mau berinvestasi. Jadi jelas waktu adalah esensi. Saya mengerti Spring memiliki cukup kurva belajar, ditambah sepertinya itu memerlukan sejumlah besar konfigurasi XML, yang biasanya saya coba hindari seperti wabah. Tetapi jika itu dapat membuat hidup saya lebih mudah dan (terutama) jika membuatnya dapat membuat pengembangan dan pengujian lebih cepat, saya bersedia menggigit peluru dan belajar Spring.

Jadi tolong. Ajari aku. Haruskah saya menggunakan Spring? Mengapa atau mengapa tidak?


10
Saya pikir Anda benar-benar perlu mencobanya sebentar untuk diri Anda sendiri untuk melihat apakah Anda menyukainya dan apakah itu cocok untuk proyek Anda. Secara pribadi saya benci itu.
Richard

1
Meskipun Anda dapat menggunakan XML atau anotasi; perlu diingat bahwa Spring mengambil konvensi atas mentalitas konfigurasi. Itu belum tentu daftar item yang Anda harus alamat.
Aaron McIver

14
Meskipun benar bahwa pertanyaan ini agak luas, saya pikir itu harus tetap terbuka. Saya membacanya sebagai "Apa manfaat yang ditawarkan Spring untuk proyek berukuran sedang?", Dan itu pertanyaan yang bagus.
sleske

1
Saya sangat merekomendasikan membaca buku teknis favorit saya: Spring in Action, Edisi Ketiga oleh Craig Walls. Ini adalah bacaan yang bagus dan akan mengubah cara Anda memprogram.
alfredaday

4
Mempertimbangkan Enterprise Java, lebih mudah untuk menjawab apa yang tidak dilakukan Spring ...
m3th0dman

Jawaban:


108

Apa yang dilakukan kerangka kerja Spring? Haruskah saya menggunakannya? Mengapa atau mengapa tidak?

Spring adalah kerangka kerja yang membantu Anda untuk "menyatukan" berbagai komponen secara bersamaan. Ini sangat berguna dalam kasus di mana Anda memiliki banyak komponen dan Anda mungkin memutuskan untuk menggabungkannya dengan cara yang berbeda, atau ingin membuatnya mudah untuk menukar satu komponen dengan yang lain tergantung pada pengaturan atau lingkungan yang berbeda.

Inilah yang selalu saya pahami sebagai "injeksi ketergantungan".

Saya akan menyarankan definisi yang berbeda:

"Rancang benda-bendamu sehingga mereka bergantung pada kekuatan luar untuk memasok mereka dengan apa yang mereka butuhkan, dengan harapan bahwa ketergantungan ini selalu disuntikkan sebelum ada orang yang meminta mereka untuk mulai melakukan pekerjaan seperti biasanya."

Bandingkan itu dengan: "Setiap objek bertanggung jawab untuk keluar dan menemukan segala sesuatu dan semua orang yang dibutuhkannya ketika mulai."

sepertinya memerlukan sejumlah besar konfigurasi XML

Nah, sebagian besar hal-hal XML (atau berbasis anotasi) mengatakan hal-hal Spring seperti:

  • Ketika seseorang meminta "HammerStore", saya ingin Anda membuat instance example.HammerStoredan mengembalikannya. Cache instance untuk waktu berikutnya, karena hanya perlu ada satu toko.
  • Ketika seseorang meminta "SomeHammer", saya ingin Anda bertanya pada diri sendiri untuk "HammerStore", dan mengembalikan hasil metode toko makeHammer(). Jangan tidak men-cache hasil ini.
  • Ketika seseorang meminta "SomeWrench", saya ingin Anda membuat instance example.WrenchImpl, Gunakan pengaturan konfigurasi gaugeAmountdan masukkan ke setWrenchSize()properti instance . Jangan simpan hasilnya.
  • Ketika seseorang meminta "LocalPlumber", saya ingin Anda membuat instance dari example.PlumberImpl. Masukkan string "Pedro" ke dalam setName()metodenya, masukkan "SomeHammer" ke dalam setHammer()metodenya, dan masukkan "SomeWrench" ke dalam setWrench()metodenya. Kembalikan hasilnya, dan cache hasilnya untuk nanti karena kita hanya perlu satu tukang ledeng.

Dengan cara ini, Spring memungkinkan komponen koneksi Anda, melabelinya, mengontrol siklus / caching mereka, dan mengubah perilaku berdasarkan konfigurasi.

Untuk memfasilitasi [pengujian] saya biasanya membuat (setidaknya) dua versi metode: satu yang menggunakan variabel contoh, dan satu yang hanya menggunakan variabel yang diteruskan ke metode.

Kedengarannya seperti banyak overhead karena tidak banyak manfaatnya bagi saya. Sebagai gantinya, buat variabel instan Anda memiliki protectedatau visibilitas paket , dan temukan tes unit di dalam com.mycompany.whateverpaket yang sama . Dengan begitu Anda dapat memeriksa dan mengubah variabel instan kapan pun Anda inginkan selama pengujian.


65

Pertama, apa itu injeksi ketergantungan?

Sederhana. Anda memiliki kelas, memiliki bidang pribadi (disetel ke nol) dan Anda mendeklarasikan setter publik yang memberikan nilai untuk bidang itu. Dengan kata lain, ketergantungan kelas (bidang) sedang disuntikkan oleh kelas eksternal (melalui setter). Itu dia. Tidak ada yang ajaib.

Kedua, Spring dapat digunakan tanpa XML (atau sangat sedikit)

Jika Anda menyelam dengan Spring 3.0.5.GA atau lebih tinggi maka Anda dapat menggunakan dukungan injeksi dependensi dari JDK6 +. Ini berarti Anda dapat memasang dependensi menggunakan @Componentdan @Resourceanotasi.

Mengapa menggunakan Spring sama sekali?

Jelas, injeksi ketergantungan mempromosikan pengujian unit yang sangat mudah karena semua kelas Anda memiliki seting untuk dependensi penting dan ini dapat dengan mudah diejek menggunakan kerangka kerja mengejek favorit Anda untuk memberikan perilaku yang diperlukan.

Selain itu, Spring juga menyediakan banyak templat yang bertindak sebagai kelas dasar untuk membuat penggunaan teknologi standar JEE menjadi mudah. Misalnya, JdbcTemplate berfungsi dengan baik dengan JDBC, JpaTemplate melakukan hal-hal baik dengan JPA, JmsTemplate membuat JMS cukup mudah. RestTemplate sangat luar biasa dalam kesederhanaannya. Sebagai contoh:

RestTemplate restTemplate = new RestTemplate();
MyJaxbObject o = restTemplate.getForObject("https://secure.example.org/results/{param1}?param2={param2}",MyJaxbObject.class,"1","2");

dan kamu sudah selesai. Parameter disuntikkan dan Anda hanya perlu memberikan anotasi JAXB untuk MyJaxbObject. Seharusnya tidak memerlukan waktu sama sekali jika Anda membuatnya secara otomatis dari XSD menggunakan plugin Maven JAXB. Perhatikan bahwa tidak ada casting yang terjadi di sana, juga tidak ada kebutuhan untuk menyatakan marshaller. Ini semua dilakukan untukmu.

Saya bisa terus bertele-tele tentang keajaiban Spring, tetapi mungkin hal terbaik untuk dilakukan adalah mencoba lonjakan kode sederhana di mana Anda mencoba untuk memasang layanan web RESTful untuk memompa data dari DAO yang disuntikkan yang mendukung transaksi.


4
ya RestTemplate sangat luar biasa. Saya punya 100 baris kode yang bisa saya buang dan ganti dengan 2-3 baris.
Kevin

11
Untuk melakukan nitpick: Dependency Injection juga mencakup pendekatan berbasis konstruktor. Anda tidak perlu memiliki setter.
Darien

28

Pertama-tama, pemahaman Anda tentang injeksi ketergantungan tidak pada dasarnya salah, tetapi sangat berbeda dari apa yang kebanyakan orang maksudkan ketika mereka menggunakan istilah itu. Apa yang Anda gambarkan adalah cara yang agak aneh dan tidak konvensional untuk mencapai testabilitas. Saya menyarankan Anda untuk menjauh darinya, karena pengembang lain akan agak bingung dengan kode semacam itu.

Injeksi ketergantungan seperti yang dipahami secara umum (dan diimplementasikan oleh Spring) berarti bahwa dependensi yang dimiliki suatu kelas (misalnya JDBC Datasource) tidak diambil oleh kelas itu sendiri, tetapi "disuntikkan" oleh wadah ketika instance dibuat. Jadi Anda tidak memiliki dua versi dari setiap metode yang menggunakan Datasource; sebagai gantinya, Anda memiliki satu konfigurasi injeksi dependensi di mana Datasource "asli" disuntikkan dan satu di mana tiruan disuntikkan. Atau, jika injeksi terjadi melalui konstruktor atau pengambil, kode uji dapat melakukan injeksi secara eksplisit.

Kedua, Spring bukan hanya injeksi ketergantungan, meskipun itulah fungsi intinya. Ini juga menyediakan transaksi deklaratif, penjadwalan pekerjaan, otentikasi, dan banyak fungsi lainnya (termasuk kerangka kerja MVC yang lengkap) yang mungkin Anda butuhkan. Ada kerangka kerja lain yang menyediakan fungsionalitas yang sama, tetapi selain Spring, hanya Java EE yang semuanya terintegrasi.


OP memahami DI dengan sangat baik
Basilevs

19

Untuk alasan mengapa Anda ingin menggunakan Spring, Anda bisa membacanya di http://www.wrox.com/WileyCDA/Section/Why-Gunakan-the-Spring-Framework-.id-130098.html

Singkatnya :

  • Aplikasi J2EE cenderung mengandung kode "plumbing" yang berlebihan. Banyak ulasan kode berulang kali mengungkapkan proporsi tinggi kode yang tidak melakukan apa-apa: kode pencarian JNDI, Transfer Objects, coba / tangkap blok untuk memperoleh dan melepaskan sumber daya JDBC. . . . Menulis dan memelihara kode saluran air membuktikan pemborosan besar pada sumber daya yang harus difokuskan pada domain bisnis aplikasi.

  • Banyak aplikasi J2EE menggunakan model objek terdistribusi di mana ini tidak pantas. Ini adalah salah satu penyebab utama dari kode berlebihan dan duplikasi kode. Secara konsep, ini juga salah dalam banyak kasus; aplikasi yang didistribusikan secara internal lebih kompleks daripada aplikasi yang berlokasi bersama, dan seringkali jauh lebih sedikit performannya. Tentu saja, jika persyaratan bisnis Anda menentukan arsitektur terdistribusi, Anda perlu mengimplementasikan arsitektur terdistribusi dan menerima trade-off yang timbul (dan Spring menawarkan fitur untuk membantu dalam skenario seperti itu). Tetapi Anda tidak harus melakukannya tanpa alasan kuat.

  • Model komponen EJB terlalu rumit. EJB dipahami sebagai cara mengurangi kompleksitas ketika menerapkan logika bisnis dalam aplikasi J2EE; itu belum berhasil dalam tujuan ini dalam praktiknya.

  • EJB terlalu sering digunakan. EJB pada dasarnya dirancang untuk aplikasi transaksional yang didistribusikan secara internal. Sementara hampir semua aplikasi non-sepele bersifat transaksional, distribusi tidak boleh dibangun ke dalam model komponen dasar.

  • Banyak "pola desain J2EE" sebenarnya bukan pola desain, tetapi merupakan solusi untuk keterbatasan teknologi. Terlalu sering menggunakan distribusi, dan penggunaan API kompleks seperti EJB, telah menghasilkan banyak pola desain yang dipertanyakan; penting untuk memeriksa ini secara kritis dan mencari pendekatan yang lebih sederhana, lebih produktif,.

  • Aplikasi J2EE sulit untuk unit test. API J2EE, dan terutama, model komponen EJB, ditentukan sebelum gerakan gesit lepas landas. Dengan demikian desain mereka tidak memperhitungkan kemudahan pengujian unit. Melalui API dan kontrak implisit, secara mengejutkan sulit untuk menguji aplikasi berdasarkan EJB dan banyak API J2EE lainnya di luar server aplikasi. Namun pengujian unit di luar server aplikasi sangat penting untuk mencapai cakupan pengujian yang tinggi dan untuk mereproduksi banyak skenario kegagalan, seperti kehilangan konektivitas ke database. Penting juga untuk memastikan bahwa pengujian dapat dijalankan dengan cepat selama proses pengembangan atau pemeliharaan, meminimalkan waktu tidak produktif menunggu pemindahan.

  • Teknologi J2EE tertentu telah gagal. Pelaku utama di sini adalah entitas kacang, yang telah terbukti sedikit bencana untuk produktivitas dan kendala mereka pada orientasi objek.


13

Dulu kami menulis aplikasi web dan layanan sederhana, efisien, cepat menggunakan hanya inti Java, Servlets dan JSP, html dan xml, JDBC API. Cukup bagus; JUnit adalah alat yang bagus untuk menguji. Kami merasa tenang karena kode kami berfungsi.

Hibernate hadir untuk menyederhanakan SQL dan mengaktifkan pemetaan sebenarnya dari tabel Database dengan objek Java, memungkinkan hubungan hierarkis tercermin dalam Pemetaan Hubungan Objek atau ORM seperti yang kita sebut. Saya menyukainya. Terutama bahwa kita tidak harus memetakan ResultSet kembali ke objek atau tipe data Java.

Struts datang untuk menambahkan pola Model View Controller ke aplikasi Web kami, itu bagus.

EJB adalah overhead besar, rasa sakit, dan anotasi membuat kode tampak seperti goresan ayam dan sekarang Spring bermunculan pada kami orang-orang yang tidak bersalah. Sepertinya berlebihan kepada saya.

Sebagai contoh kita sekarang mengemas url jdbc sederhana kita, pengguna, masuk ke properti jdbc.prop pertama, kemudian ke properti hibernate kedua dan kemudian ke kacang Spring ketiga kalinya!

Lawan semua itu, pertimbangkan untuk mendapatkan koneksi di mana Anda membutuhkannya benar-benar sederhana seperti yang ditunjukkan di bawah ini di java murni, yang benar-benar kami lakukan setelah melalui semua hal-hal udara panas dengan Spring:

Connection connection = DriverManager.getConnection(url, user, pass);

Itu dengan sendirinya cukup jelas bahwa itu adalah putaran besar dan pembungkus berulang-ulang untuk melakukan hal yang sederhana dengan cepat dan mudah tanpa manfaat besar lainnya benar-benar. Ini seperti membungkus berton-ton kertas kado di sekitar kado mungil yang bagus yang hanya Anda simpan saja.

Contoh lain adalah pembaruan batch. Dengan Spring itu berbelit-belit melibatkan beberapa kelas, antarmuka sebelum Anda dapat menggunakan JdbcTemplate untuk melakukan pembaruan batch. Dengan jdbc sederhana itu hanya:

Statement statement = connection.createStatement();
statement.addBatch(sqlquery);
statement.executeBatch();

Tidak bisa lebih sederhana atau lebih cepat dari itu.

Saya tidak mendukung kerangka kerja ini. Maaf. Siapa yang mau suntikan setiap kali mereka butuh sesuatu?


6
kode itu sederhana, tetapi di mana penanganan transkasi Anda ditentukan dan bagaimana Anda mengujinya? Kedua hal itu dibuat lebih sederhana pada musim semi. Juga, kode java Anda secara langsung terkait dengan koneksi db, kecuali Anda menyimpan url dan kata sandi koneksi secara eksternal, ini juga dibuat lebih sederhana pada musim semi.
NimChimpsky

+1 untuk jawaban ini, untuk membahas komentar di atas, manusia menggunakan pola desain ringan (GOF) Anda sendiri, tunggal untuk koneksi pooling, kelas proxy yang menyediakan sql (string) dan nilai (array) ke Objek Database (metode mana , tergantung pada metode http), dbobject menangani pooling koneksi, transaksi dll. mengeksekusi kueri diikuti oleh rilis. Satu objek digunakan dalam semua modul, tidak ada yang membutuhkan kode pelat ketel berlebih. titik bonus dengan mengatur unit test pada kelas proxy dan dbobject.
user2727195

Lihatlah Spring-Data-JPA! Beri anotasi pada pojo ke dalam entitas, terapkan antarmuka, dan tentukan tanda tangan metode menggunakan nama yang masuk akal, seperti findHammerByWeight (), dan pegas mengimplementasikan metode untuk Anda, memberi Anda repositori injeksi yang dapat Anda gunakan di semua layanan bisnis atau kelas pengontrol Anda .
mancini0

13

Apa yang dilakukan kerangka kerja Spring?

Musim semi tidak hanya hari ini, apa yang dikenal sebagai kerangka kerja sederhana, itu adalah ekosistem yang lengkap.

Topik yang dicakup oleh ekosistem mata air:

  • Kerangka Kerja Pegas (mis. Injeksi Ketergantungan, AOP ...)

  • Cloud Musim Semi

  • Data Musim Semi

  • Keamanan Musim Semi

  • Spring Batch

  • Sosial Musim Semi

Lihat di sini untuk cakupan penuh ekosistem. Dimungkinkan untuk memilih proyek ceri, sehingga Anda dapat menggunakan Google Guice untuk DI dan misalnya Spring Security untuk menangani hal-hal yang terkait dengan keamanan. ANDA tidak harus membeli ke seluruh ekosistem.

Kerangka Spring itu sendiri mencakup hari ini terutama

  • Injeksi Ketergantungan

  • Pemrograman Berorientasi-Aspek termasuk manajemen transaksi deklaratif Spring

  • Aplikasi web Spring MVC dan kerangka kerja layanan web yang tenang

  • Dukungan mendasar untuk JDBC, JPA, JMS

Sumber spring.io

Secara umum, Anda dapat mengatakan, bahwa Spring adalah kumpulan Pola dan Praktik yang diterapkan dalam kode, yang dapat membantu meningkatkan atau mempercepat siklus pengembangan aplikasi Anda.

Untuk apa itu (kerangka-inti) yang paling dikenal adalah kemampuannya di bidang injeksi ketergantungan . Pegas itu sendiri memiliki, apa yang disebut Pembalikan wadah kontrol atau Kontainer IoC pendek atau bahkan lebih pendek wadah (yang "pegas" kadang-kadang digunakan identik).

Apa itu injeksi ketergantungan?

Injeksi ketergantungan berarti, bahwa objek Anda menerima setiap ketergantungan ke objek lain melalui mekanisme eksternal.

Katakanlah, Anda punya mobil, cara khasnya, itu diterapkan adalah:

public class Car {

    Engine e;

    public Car() { 
        e = new Engine(); 
    }

}

Objek mobil tergantung pada mesin. Karena mesin diimplementasikan sebagai anggota mobil, itu tidak dapat diganti untuk misalnya mesin uji.

Sekarang injeksi ketergantungan mulai digunakan:

public class Car {

    Engine e;

    public Car(Engine e) { 
        this.e = e; 
    }

}

Setelah itu, Anda dapat beralih mesin. Apa yang Anda lihat di atas disebut injeksi konstruktor . Ada jenis lain seperti misalnya setter -injection atau metode -injection. Bagaimana Spring membantu Anda dengan ini? Pegas memungkinkannya menandai komponen yang akan disuntikkan dengan anotasi @Autowireddan melakukan pengkabelan objek yang disuntikkan secara otomatis - kemungkinan, bahwa komponen yang ingin Anda injeksi memiliki dependensinya sendiri. Suntikan - bisa dikatakan - ditandai melalui@Component

public class Car {

    Engine e;

    @Autowired
    public Car(Engine e) { 
        this.e = e; 
    }

}

Tapi itu hanya satu dari sekian banyak fitur yang ditawarkan Spring.

Haruskah saya menggunakan Spring? Mengapa atau mengapa tidak?

Karena Spring tidak terlalu mengganggu dan menawarkan banyak bantuan, Anda harus mempertimbangkan untuk menggunakan spring. Khusus untuk proyek-proyek baru, Spring Boot sangat menarik. start.spring.io menawarkan point'n'click -interface yang mudah digunakan untuk membuat templat proyek untuk memulai. Bahkan dimungkinkan untuk digunakan curluntuk mengambil templat:

curl start.spring.io

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

:: Spring Initializr ::  https://start.spring.io

This service generates quickstart projects that can be easily customized.
Possible customizations include a project's dependencies, Java version, and
build system or build structure. See below for further details.

The services uses a HAL based hypermedia format to expose a set of resources
to interact with. If you access this root resource requesting application/json
as media type the response will contain the following links:
+-----------------+-----------------------------------------+
| Rel             | Description                             |
+-----------------+-----------------------------------------+
| gradle-build    | Generate a Gradle build file            |
| gradle-project  | Generate a Gradle based project archive |
| maven-build     | Generate a Maven pom.xml                |
| maven-project * | Generate a Maven based project archive  |
+-----------------+-----------------------------------------+

...

Di sisi lain, kerangka kerja seperti percikan atau dropwizard menawarkan titik awal yang baik untuk pembuatan aplikasi web cepat juga.


1
Jawaban yang sangat informatif!
GOXR3PLUS

Setuju, jawaban yang sangat bagus. OP, saya bersimpati dengan Anda. Saya juga memiliki beberapa orang yang menunjukkan demo Musim Semi di mana mereka "menyederhanakan" kode dengan menambahkan file XML dan 2 lapisan tipuan supaya mereka bisa memanggil konstruktor di kelas :) Anda benar-benar hanya perlu menyelam atau pergi ke presentasi yang sangat bagus dan pada akhirnya akan menjadi jelas di mana manfaat dan kelemahannya
Adam Hughes

4

Ini adalah kerangka kerja yang ditulis dalam Java dengan banyak hal termasuk untuk membuat aplikasi web Anda berfungsi (misalnya dukungan untuk internasionalisasi). Ini juga menyediakan cara yang baik untuk menyusun aplikasi Anda menjadi beberapa lapisan. Gunakan itu, itu akan menghemat banyak waktu dalam jangka panjang.

Buku yang bagus untuk dipelajari tentang Spring adalah: Expert Spring MVC dan Web Flow

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.