Penjelasan rinci tentang caching konten dinamis di bawah JSP 2.0
Penulis:Eve Cole
Waktu Pembaruan:2009-07-03 16:56:34
Cache konten adalah salah satu teknik pengoptimalan yang paling umum dalam aplikasi web dan dapat diterapkan dengan mudah. Misalnya, Anda dapat menggunakan tag JSP khusus - beri nama <jc:cache> - dengan <jc:cache> dan </jc:cache> untuk merangkum setiap fragmen halaman yang perlu di-cache. Tag khusus apa pun dapat mengontrol kapan bagian yang dikandungnya (yaitu, fragmen halaman yang telah dikemas sebelumnya) dijalankan, dan hasil keluaran dinamis dapat diambil. Tag <jc:cache> memungkinkan kontainer JSP (seperti Tomcat) untuk menghasilkan konten hanya sekali, sebagai variabel JSP di seluruh aplikasi, untuk menyimpan setiap fragmen cache. Setiap kali halaman JSP dijalankan, tag kustom akan memuat fragmen halaman cache tanpa harus mengeksekusi kode JSP lagi untuk menghasilkan output. Sebagai bagian dari proyek Jakarta, perpustakaan tag dikembangkan menggunakan teknologi ini. Ini berfungsi dengan baik ketika konten yang di-cache tidak perlu disesuaikan untuk setiap pengguna atau permintaan.
Artikel ini meningkatkan teknik yang dijelaskan di atas dengan menggunakan JSP 2.0 Expression Language (EL), yang memungkinkan halaman JSP menyesuaikan konten cache untuk setiap permintaan dan pengguna. Fragmen halaman cache dapat berisi ekspresi JSP yang tidak dievaluasi oleh penampung JSP. Nilai ekspresi ini ditentukan oleh tag khusus setiap kali halaman dijalankan. Oleh karena itu, pembuatan konten dinamis dioptimalkan, namun fragmen yang di-cache dapat berisi bagian konten yang dihasilkan oleh setiap permintaan menggunakan bahasa ekspresi JSP asli. Dengan bantuan JSP 2.0 EL API, pengembang Java dapat mewujudkan hal ini menggunakan bahasa ekspresi.
Caching Konten VS Caching Data Caching konten bukan satu-satunya pilihan. Misalnya, data yang diambil dari database juga dapat di-cache. Faktanya, cache data mungkin lebih efisien karena informasi yang disimpan tidak mengandung markup HTML dan memerlukan lebih sedikit memori. Namun, dalam banyak kasus, cache dalam memori lebih mudah diterapkan. Asumsikan bahwa dalam kasus tertentu, suatu aplikasi terdiri dari sejumlah besar objek transaksi, menggunakan sumber daya CPU yang penting, menghasilkan data yang kompleks, dan menggunakan halaman JSP untuk menyajikan data. Semuanya berfungsi dengan baik hingga suatu hari beban di server tiba-tiba meningkat dan diperlukan solusi darurat. Saat ini, membuat lapisan cache antara objek transaksi dan lapisan presentasi adalah solusi yang sangat baik dan efektif. Namun halaman JSP yang menyimpan konten dinamis dalam cache harus dimodifikasi dengan sangat cepat dan lancar. Dibandingkan dengan pengeditan halaman JSP sederhana, perubahan pada logika bisnis aplikasi biasanya memerlukan lebih banyak pekerjaan dan pengujian; selain itu, jika halaman mengumpulkan informasi dari berbagai sumber gabungan, lapisan Web hanya memerlukan sedikit perubahan. Masalahnya adalah ruang cache perlu dikosongkan ketika informasi yang di-cache menjadi basi, dan objek transaksi harus mengetahui kapan hal ini terjadi. Namun, ketika memilih untuk menerapkan cache konten atau cache data, atau teknik optimasi lainnya, ada banyak faktor yang harus dipertimbangkan, dan terkadang ada persyaratan khusus untuk program yang sedang dikembangkan.
Cache data dan cache konten tidak harus saling eksklusif, keduanya dapat digunakan bersama-sama. Misalnya, dalam aplikasi berbasis basis data; data yang diekstraksi dari basis data dan HTML yang menyajikan data tersebut di-cache secara terpisah. Ini agak mirip dengan menggunakan JSP untuk menghasilkan template secara real time. Teknik API berbasis EL yang dibahas dalam artikel ini menggambarkan cara menggunakan JSP EL untuk memuat data ke dalam template rendering.
Menyimpan konten dinamis dalam cache menggunakan variabel JSP Setiap kali Anda menerapkan mekanisme cache, Anda memerlukan metode untuk menyimpan objek cache. Dalam artikel ini, objek bertipe String dilibatkan. Salah satu pilihannya adalah menggunakan kerangka caching objek, atau menggunakan peta Java untuk mengimplementasikan skema caching khusus. JSP sudah memiliki apa yang disebut "atribut tercakup" atau "variabel JSP" untuk menyediakan pemetaan objek ID, yang diperlukan oleh mekanisme caching. Untuk menggunakan cakupan halaman atau permintaan, hal ini tidak masuk akal, sedangkan dalam cakupan aplikasi ini adalah tempat yang baik untuk menyimpan konten yang di-cache karena dibagikan oleh semua pengguna dan halaman. Lingkup sesi juga dapat digunakan ketika caching terpisah diperlukan untuk setiap pengguna, namun hal ini tidak terlalu efisien. Pustaka tag JSTL dapat digunakan untuk menyimpan konten dalam cache, dengan menggunakan variabel JSP seperti yang ditunjukkan dalam contoh berikut:
<%@ taglib prefix="c" uri=" http://java.sun.com/jsp/jstl/core " %><c:if test="${empty cachedFragment}">
<c:set var="cachedFragment" scope="aplikasi">
...
</c:set></c:jika>
Fragmen halaman yang di-cache mengeluarkan hasilnya menggunakan pernyataan berikut:
${applicationScope.cachedFragment}
Apa yang terjadi jika fragmen cache perlu disesuaikan untuk setiap permintaan? Misalnya, jika Anda ingin menyertakan penghitung, Anda perlu menyimpan dua fragmen dalam cache:
<%@ taglib prefix="c" uri=" http://java.sun.com/jsp/jstl/core " %><c:if test="${sessionScope.counter == null}"> <c :set var="counter" scope="session" value="0"/></c:if><c:set var="counter" value="${counter+1}" scope="session"/ ><c:if test="${kosongkan cachedFragment1}">
<c:set var="cachedFragment1" scope="aplikasi">
...
</c:set></c:if><c:if test="${empty cachedFragment2}">
<c:set var="cachedFragment2" scope="aplikasi">
...
</c:set></c:jika>
Anda dapat menggunakan pernyataan berikut untuk menampilkan konten cache:
${cachedFragment1} ${counter} ${cachedFragment2}
Dengan bantuan perpustakaan tag khusus, cache fragmen halaman yang memerlukan penyesuaian menjadi sangat mudah. Seperti disebutkan di atas, konten yang di-cache dapat diapit dengan tag pembuka (<jc:cache>) dan tag penutup (</jc:cache>). Setiap penyesuaian dapat diekspresikan menggunakan tag lain (<jc:dynamic expr="..."/>) untuk menghasilkan ekspresi JSP (${...}). Konten dinamis di-cache menggunakan ekspresi JSP dan ditetapkan setiap kali konten yang di-cache dikeluarkan. Anda dapat melihat bagaimana hal ini dicapai pada bagian di bawah ini. Counter.jsp menyimpan fragmen halaman yang berisi penghitung dalam cache. Penghitung akan otomatis bertambah 1 setiap kali pengguna me-refresh halaman.
<%@ taglib prefix="c" uri=" http://java.sun.com/jsp/jstl/core " %><%@ taglib prefix="jc" uri=" http://devsphere.com/ artikel/jspcache " %><c:if test="${sessionScope.counter == null}">
<c:set var="counter" scope="session" value="0"/></c:if><c:set var="counter" value="${counter+1}" scope="session "/><jc:cache id="cachedFragmentWithCounter">
... <jc:dynamic expr="sessionScope.counter"/>
...</jc:cache>
Variabel JSP mudah digunakan dan merupakan solusi cache konten yang bagus untuk aplikasi Web sederhana. Namun, jika aplikasi menghasilkan banyak konten dinamis, tidak adanya kendali atas ukuran cache pasti menjadi masalah. Kerangka kerja cache khusus dapat memberikan solusi yang lebih kuat, memungkinkan pemantauan cache, membatasi ukuran cache, mengendalikan kebijakan cache, dll...
Menggunakan API Bahasa Ekspresi JSP 2.0
Kontainer JSP (seperti Tomcat) mengevaluasi ekspresi di halaman JSP menggunakan EL API dan dapat digunakan dengan kode Java. Hal ini memungkinkan pengembangan di luar halaman Web menggunakan JSP EL, misalnya pada file XML, sumber daya berbasis teks, dan skrip khusus. EL API juga berguna ketika Anda perlu mengontrol kapan ekspresi di halaman Web ditetapkan atau ekspresi tertulis yang terkait dengannya. Misalnya, fragmen halaman yang di-cache dapat berisi ekspresi JSP khusus, dan EL API akan digunakan untuk menetapkan atau mengevaluasi ulang ekspresi ini setiap kali konten yang di-cache dikeluarkan.
Artikel ini memberikan contoh program (lihat bagian sumber daya di akhir artikel). Aplikasi ini berisi kelas Java (JspUtils) dan metode eval() di kelas ekspresi dan objek konteks JSP. Metode Eval() mendapatkan ExpressionEvaluator dari konteks JSP dan memanggil metode evaluasi(), meneruskan ekspresi, tipe ekspresi yang diharapkan, dan variabel yang diperoleh dari congtext JSP. Metode JspUtils.eval() mengembalikan nilai ekspresi.
paket com.devsphere.articles.jspcache;
impor javax.servlet.jsp.JspContext;
impor javax.servlet.jsp.JspException;
impor javax.servlet.jsp.PageContext;
import javax.servlet.jsp.el.ELException;
import javax.servlet.jsp.el.ExpressionEvaluator;
import java.io.IOException; kelas publik JspUtils {
evaluasi Objek statis publik(
String expr, Tipe kelas, JspContext jspContext)
melempar JspException {
mencoba {
jika (expr.indexOf("${") == -1)
pengembalian biaya;
Penilai ExpressionEvaluator
= jspContext.getExpressionEvaluator();
kembali evaluator.evaluate(expr, ketik,
jspContext.getVariableResolver(), null);
} tangkapan (ELException e) {
melempar JspException(e);
}
}
...}
Catatan: JspUtils.eval() terutama merangkum ExpressionEvaluator standar. Jika expr tidak berisi ${, JSP EL API tidak dipanggil karena tidak ada ekspresi JSP.
Membuat file deskriptor perpustakaan tag (TLD) Perpustakaan tag JSP memerlukan file deskriptor perpustakaan tag (TLD) untuk menyesuaikan penamaan tag, atributnya, dan kelas Java yang beroperasi pada tag. jspcache.tld menjelaskan dua tag khusus. <jc:cache> memiliki dua atribut: id fragmen halaman yang di-cache dan cakupan JSP—cakupan konten halaman JSP yang selalu perlu disimpan. <jc:dynamic> hanya memiliki satu atribut, ekspresi JSP yang harus dievaluasi setiap kali fragmen cache dikeluarkan. File TLD memetakan dua tag khusus ini ke kelas CacheTag dan DynamicTag sebagai berikut:
<?xml versi="1.0" pengkodean="UTF-8" ?><taglib xmlns=" http://java.sun.com/xml/ns/j2ee "
xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
xsi:schemaLocation=" http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd"
versi="2.0">
<tlib-versi>1.0</tlib-versi>
<nama-pendek>jc</nama-pendek>
<uri>http://devsphere.com/articles/jspcache</uri>
<tag>
<nama>cache</nama>
<tag-class>com.devsphere.articles.jspcache.CacheTag</tag-class>
<body-content>tanpa skrip</body-content>
<atribut>
<nama>id</nama>
<wajib>benar</wajib>
<rtexprvalue>benar</rtexrvalue>
</atribut>
<atribut>
<nama>ruang lingkup</nama>
<wajib>salah</wajib>
<rtexrvalue>salah</rtexrvalue>
</atribut>
</tag>
<tag>
<nama>dinamis</nama>
<tag-class>com.devsphere.articles.jspcache.DynamicTag</tag-class>
<body-content>kosong</body-content>
<atribut>
<nama>ekspr</nama>
<wajib>benar</wajib>
<rtexrvalue>salah</rtexrvalue>
</atribut>
</tag></taglib>
File TLD disertakan dalam file deskriptor aplikasi Web (web.xml). Kelima file ini juga berisi parameter awal yang menunjukkan apakah cache tersedia.
<?xml version="1.0" pengkodean="ISO-8859-1"?><web-app xmlns=" http://java.sun.com/xml/ns/j2ee "
xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
xsi:schemaLocation=" http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd"
versi="2.4">
<param-konteks>
<param-name>com.devsphere.articles.jspcache.enabled</param-name>
<nilai-param>benar</nilai-param>
</param-konteks>
<jsp-config>
<taglib>
<taglib-uri>http://devsphere.com/articles/jspcache</taglib-uri>
<lokasi-taglib>/WEB-INF/jspcache.tld</lokasi-taglib>
</taglib>
</jsp-config></web-aplikasi>
Pahami mekanisme kerja <jc:cache>. Kontainer JSP membuat instance CacheTag untuk setiap tag <jc:cache> di halaman JSP untuk memprosesnya. Kontainer JSP bertanggung jawab untuk memanggil metode setJsp(), setParent() dan setJspBody(), yang merupakan kelas CacheTag yang diwarisi dari SimpleTagSupport. Kontainer JSP juga memanggil metode penyetel untuk setiap atribut dari tag yang dimanipulasi. Metode SetId() dan setScope() menyimpan nilai atribut ke dalam bidang pribadi. Nilai ini telah diinisialisasi dengan nilai default menggunakan konstruktor CacheTag().
paket com.devsphere.articles.jspcache;
impor javax.servlet.ServletContext;
impor javax.servlet.jsp.JspContext;
impor javax.servlet.jsp.JspException;
impor javax.servlet.jsp.PageContext;
impor javax.servlet.jsp.tagext.SimpleTagSupport;
impor java.io.IOException;impor java.io.StringWriter;
kelas publik CacheTag memperluas SimpleTagSupport {
String akhir statis publik CACHE_ENABLED
= "com.devsphere.articles.jspcache.enabled";
id String pribadi;
ruang lingkup int pribadi;
cache boolean pribadiDiaktifkan; CacheTag publik() {
id = null; cakupan = PageContext.APPLICATION_SCOPE;
} public void setId(String id) {
ini.id = id;
} public void setScope(Cakupan string) {
this.scope = JspUtils.checkScope(ruang lingkup);
}
...}
Metode setScope() memanggil JspUtils.checkScope() untuk memverifikasi nilai properti cakupan yang telah dikonversi dari tipe String ke int.
...kelas publik JspUtils {
...
public static int checkScope(Cakupan string) {
if ("halaman".equalsIgnoreCase(ruang lingkup))
kembalikan PageContext.PAGE_SCOPE;
else if ("permintaan".equalsIgnoreCase(ruang lingkup))
kembalikan PageContext.REQUEST_SCOPE;
else if ("sesi".equalsIgnoreCase(ruang lingkup))
kembalikan PageContext.SESSION_SCOPE;
else if ("aplikasi".equalsIgnoreCase(ruang lingkup))
kembalikan PageContext.APPLICATION_SCOPE;
kalau tidak
melempar IllegalArgumentException baru(
"Cakupan tidak valid: " + cakupan);
}}
Setelah instance CacheTag siap beroperasi pada tag, kontainer JSP memanggil metode doTag() dan mendapatkan konteks JSP menggunakan getJspContext(). Objek ini dilemparkan sebagai PageContext, sehingga metode getServletContext() bisa dipanggil. Konteks servlet digunakan untuk mendapatkan nilai parameter inisialisasi, yang menunjukkan apakah mekanisme caching diaktifkan. Jika caching diaktifkan, doTag() mencoba mendapatkan fragmen halaman yang di-cache menggunakan nilai atribut id dan scope. Jika fragmen halaman belum di-cache, doTag() menggunakan getJspBody().invoke() untuk mengeksekusi kode JSP yang dienkapsulasi oleh <jc:cache> dan </jc:cache>. Output yang dihasilkan oleh badan JSP di-buffer dalam StringWriter dan diambil dengan metode toStirng(). Dengan cara ini, doTag() memanggil metode setAttribute() konteks JSP untuk membuat variabel JSP baru. Variabel ini mengontrol konten cache yang mungkin berisi ekspresi JSP (${…}). Ekspresi ini ditetapkan oleh JspUtils.eval() sebelum mengeluarkan konten menggunakan jspContext.getOut().print(). Perilaku ini hanya terjadi ketika cache diaktifkan. Jika tidak, doTag() hanya mengeksekusi isi JSP melalui getJspBody().invoke(null) dan hasil keluaran tidak di-cache.
...kelas publik CacheTag memperluas SimpleTagSupport {
...
public void doTag() melempar JspException, IOException {
JspContext jspContext = getJspContext();
Aplikasi ServletContext
= ((PageContext) jspContext).getServletContext();
String cacheEnabledParam
= aplikasi.getInitParameter(CACHE_ENABLED);
cacheEnabled = cacheEnabledParam != nol
&& cacheEnabledParam.equals("benar");
jika (cacheEnabled) {
String di-cacheOutput
= (String) jspContext.getAttribute(id, cakupan);
jika (cachedOutput == null) {
Buffer StringWriter = StringWriter baru();
getJspBody().invoke(buffer);
cachedOutput = buffer.toString();
jspContext.setAttribute(id, cachedOutput, cakupan);
} String dievaluasiOutput = (String) JspUtils.eval(
cachedOutput, String.kelas, jspContext);
jspContext.getOut().print(evaluatedOutput);
} kalau tidak
getJspBody().invoke(null);
}
...}
Perhatikan bahwa satu panggilan JspUtils.eval() mengevaluasi semua ekspresi ${…}. Karena teks yang berisi sejumlah besar struktur ${...} juga merupakan ekspresi. Setiap fragmen cache dapat diproses sebagai ekspresi JSP yang kompleks.
Metode IsCacheEnabled() mengembalikan nilai cacheEnabled, yang telah diinisialisasi oleh doTag().
...kelas publik CacheTag memperluas SimpleTagSupport {
... boolean publik isCacheEnabled() {
kembalikan cacheDiaktifkan;
}}
Tag <jc:cache> memungkinkan pengembang halaman memilih ID fragmen halaman yang di-cache. Hal ini memungkinkan fragmen halaman cache untuk dibagikan ke beberapa halaman JSP, yang berguna saat menggunakan kembali kode JSP. Namun beberapa protokol penamaan masih diperlukan untuk menghindari kemungkinan konflik. Efek samping ini dapat dihindari dengan memodifikasi kelas CacheTag untuk menyertakan URL di dalam ID otomatis.
Memahami apa yang dilakukan <jc:dynamic> Setiap <jc:dynamic> ditangani oleh instance kelas DynamicTag, dan metode setExpr() menyimpan nilai atribut expr ke dalam bidang pribadi. Metode DoTag() membuat ekspresi JSP dan menambahkan awalan ${ dan akhiran } ke nilai atribut expr. Kemudian, doTag() menggunakan findAncestorWithClass() untuk menemukan pengendali CacheTag dari <jc:cache> yang berisi elemen tag <jc:dynamic>. Jika tidak ditemukan atau caching dinonaktifkan, ekspresi JSP dievaluasi dengan JspUtils.eval() dan nilainya dikeluarkan. Jika tidak, doTag() akan menampilkan ekspresi yang tidak bernilai.
paket com.devsphere.articles.jspcache;
impor javax.servlet.jsp.JspException;
impor javax.servlet.jsp.tagext.SimpleTagSupport;
impor java.io.IOException;
kelas publik DynamicTag memperluas SimpleTagSupport {
pribadi String expr;
public void setExpr(String expr) {
ini.expr = expr;
} public void doTag() menampilkan JspException, IOException {
Keluaran string = "${" + expr + "}";
Leluhur CacheTag = (CacheTag) findAncestorWithClass(
ini, CacheTag.kelas);
if (leluhur == null || !ancestor.isCacheEnabled())
keluaran = (String) JspUtils.eval(
keluaran, String.kelas, getJspContext());
getJspContext().getOut().print(keluaran);
}}
Menganalisis kode di atas, Anda dapat melihat bahwa <jc:cache> dan <jc:dynamic> bekerja sama untuk mencapai solusi yang seefisien mungkin. Jika cache tersedia, fragmen halaman dimasukkan ke dalam buffer bersama dengan ekspresi JSP yang dihasilkan oleh <jc:dynamic> dan diberi nilai CacheTag. Jika caching dinonaktifkan, caching menjadi tidak berarti dan <jc:cache> hanya mengeksekusi bagian tubuh JSP-nya, membiarkan DynamicTag untuk memberikan nilai pada ekspresi JSP. Menonaktifkan caching terkadang diperlukan, terutama selama proses pengembangan ketika konten berubah dan halaman JSP dikompilasi ulang. Tentu saja, caching harus diaktifkan di lingkungan produksi setelah pengembangan.
Meringkaskan
Untuk mengembangkan aplikasi tingkat perusahaan besar, Anda harus mempertimbangkan penggunaan kerangka kerja yang mendukung mekanisme caching yang lebih baik daripada hanya menggunakan variabel JSP. Namun tidak diragukan lagi akan sangat membantu untuk memahami teknologi penyesuaian berdasarkan EL API.