Java Server Pages to technologia pozwalająca tworzyć dynamiczne treści. Podobnie jak serwlety działa po stronie serwera. Jednakże w odróżnieniu od nich bardziej nadają się do wyświetlania odpowiedzi, gdyż w tym przypadku zaszywamy Javę w statycznej treści html, a nie na odwrót. Dość szybko widać jak niewygodne jest tworzenie odpowiedzi w wygodnej i ładnej formie w całości w obiekcie out. Przed przystąpieniem do tej lekcji warto zapoznać się z artykułem poświęconym na temat serwletów.
Podstawową różnicą jest zmiana podejścia. W JSP główną częścią pliku powinien być kod odpowiedzialny za warstwę prezentacji. Kod Javy umieszczamy pod specjalnymi znacznikami.
Drugą różnicą jest cykl życia aplikacji gdyż strony JSP są konwertowane do postaci serwletu.
Kiedy zatem korzystać z JSP? Wtedy, gdy odpowiedź od serwera powinna być wyświetlona w przystępnej dla użytkownika formie stosując np. css. Natomiast w przypadku, gdy ważniejsza jest sama logika aplikacji wygodniej jest używać serwletów.
Różne znaczniki pełnią specjalne role w dokumentach JSP. Mogą pełnić rolę informacyjną dla kontenera, wskazywać miejsce na kod Javy lub po prostu pełnić funkcję komentarza.
<%@ page dyrektywa %> – dostarczają informacji do kontenera JSP na temat strony, w jaki sposób przetwarzać informacje. Poniżej przykład dyrektywy.
1 2 |
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> |
<%@ include … %> – Umożliwia załączenie pliku do dokumentu JSP. Przykładem może być podział wynikowej strony na wiele fragmentów np. nagłówek, stopka i część główna. Zazwyczaj tylko ta ostatnia jest zmienna, w związku z tym, żeby nie powielać tego samego kodu możemy go umieścić w osobnym pliku. Następnie załączamy go tym tagiem w odpowiednim miejscu.
<%@ taglib … %> – Pozwala wczytać dodatkowe tagi.
<% kod Javy %> – umieszczamy dowolny kod napisany w języku Java. W większości przypadków wynik nie jest widoczny dla użytkownika końcowego. Wyjątkiem jest wywołanie metod print obiektu out.
<%! deklaracja zmiennych %> – w takim znaczniku można umieścić deklarowane zmienne, które można użyć później w dowolnym miejscu w pliku JSP. Jeśli chcemy wykorzystać je na innej stronie JSP musimy wykorzystać np. Sesję lub zmienną request.
1 2 3 |
<%! int a; String b; %> |
<%= wyrażenie %> – element, który po uruchomieniu jest konwertowany do postaci łańcucha znakowego, a następnie wyświetlany.
1 2 3 |
<%= request.getParameter("parametr") %> |
<%– Komentarz w JSP –%> – komentarze podobnie jak w normalnej Javie nie są widoczne, ani nie powodują wykonywania żadnych czynności. Są całkowicie pomijane przez środowisko JSP.
W przypadku stron JSP możemy od razu korzystać z niektórych zdefiniowanych wcześniej obiektów, które w serwletach trzeba samemu utworzyć. Poniżej znajduje się ich lista.
HttpServletRequest
– zawiera dane żądania od klienta np. dostęp do przesłanych danych z formularza.HttpServletResponse
– zawiera informacje odpowiedzi dla klienta od serwera.HttpSession
– informacje na temat sesji.JspWriter
– treść wyświetlana po stronie klienta.Przykład aplikacji JSP
Podobnie jak w serwletach potrzebny nam będzie Dynamic Web Project. Tworzymy go w Eclipse, a następnie w katalogu Web Content tworzymy np. Index.jsp (zamiast html). Poniżej znajduje się przykładowa treść takiego dokumentu.
1 2 3 4 5 6 7 8 9 10 |
<%@ page language=”Java” contentType=”text/html” pageEncoding=”UTF-8” info="Programista Java JSP" %> <html> <head> <title>JSP</title> </head> <body> <% out.print ("Pierwszy dokument JSP"); %> </body> </html> |
Wróćmy na chwilę do przykładu aplikacji, która obliczała wyznacznik macierzy kwadratowej 3×3. Pamiętamy, że w przypadku serwletu problemem jest wyświetlenie odpowiedzi, gdyż każdą linię należy napisać przy pomocy metody out.print(). Lepszym rozwiązaniem jest rozdzielenie warstwy prezentacji, która będzie się znajdowała w pliku JSP, natomiast serwlet będzie obsługiwał logikę aplikacji.
Aplikacja będzie wyglądać tak samo, natomiast ze względu na rozdzielenie warstw prezentacji od logiki będziemy potrzebować dodatkowego pliku demo.jsp, który serwer odeśle jako odpowiedź.
Nowością będzie przekazanie przekazanie metodą setAttribute()
wartości, które są dostępne w serwlecie. Następnie pozostaje wyświetlić te dane na stronie JSP. Poniżej znajdują się kody źródłowe opisanego projektu. Pliki *.html i *.jsp należy umieścić w katalogu WebContent, a plik serwletu w JavaResources\src.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<!DOCTYPE html> <html> <HEAD> </HEAD> <style> body { background: #f2f2f2; } textarea { resize: none; width: 150px; height: 120px; font-size: 1.5rem; } p { font-size: 1.2rem; } </style> <body> <H1>Przekazywanie parametrów metodą post</H1> <form action="/MojServlet/Post" method="post"> <p>Wypisz macierz 3x3 do obliczenia jej wyznacznika:</p></br> <textarea name="macierz" rows="3" cols="10"></textarea> <input TYPE="submit" VALUE="Oblicz"> </form> </body> </html> |
Plik index.html pozostał bez zmian.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
package pl.programistajava; import java.io.IOException; import java.util.Arrays; import java.util.Scanner; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/Post") public class Macierz extends HttpServlet { private static final long serialVersionUID = 1L; public Macierz() { super(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub int[][] matrix = new int[3][3]; String req = request.getParameter("macierz").trim(); Scanner sc = new Scanner(req); sc.useDelimiter("\\D+"); while(sc.hasNextInt()){ for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { matrix[i][j] = sc.nextInt(); } } } sc.close(); request.setAttribute("mat", Arrays.deepToString(matrix)); request.setAttribute("det", det(matrix)); RequestDispatcher rd = request.getRequestDispatcher("demo.jsp"); rd.forward(request, response); } private int det(int[][] mat) { int det = mat[0][0]*mat[1][1]*mat[2][2] + mat[0][1]*mat[1][2]*mat[2][0] + mat[0][2]*mat[1][0]*mat[2][1] - mat[0][2]*mat[1][1]*mat[2][0] - mat[0][0]*mat[1][2]*mat[2][1] - mat[0][1]*mat[1][0]*mat[2][2]; return det; } } |
Różnicą w tym dokumencie jest końcówka metody doPost()
. Poprzednio przesyłaliśmy odpowiedź od razu do klienta. Tym razem przekazujemy wyniki obliczeń do dokumentu JSP. W tym celu wykorzystujemy obiekt klasy RequestDispatcher
oraz metodę forward()
. Zwróćmy uwagę, że w metodzie getRequestDispatcher()
znajduje się nazwa strony i musi ona istnieć w folderze WebContent.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?xml version="1.0" encoding="UTF-8" ?> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Obliczanie wyznacznika macierzy</title> <style> body { background: #f2f2f2; } </style> </head> <body> <h1>Odpowiedź od serwera</h1> <p>Wyznacznikiem macierzy <%= request.getAttribute("mat") %> jest <%= request.getAttribute("det") %></p> </body> </html> |
Takie rozwiązanie jest znacznie wygodniejsze i bardziej czytelne. W tym pliku nie znajdują się żadne obliczenia, jedynie przekazywane są wartości z serwletu. Wyświetlamy je przy pomocy tagu wyrażeń <%= %>.
Najprostszą możliwością jest obsługa błędów w bloku try-catch. Nie jest to jednak dobry styl i lepiej wykorzystać specjalny obiekt exception dostarczony nam domyślnie przez JSP. Dzięki zastosowaniu tej metody mamy obsługa wszystkich wyjątków znajduje się w jednym miejscu. Należy wykonać następujące czynności.
W pliku JSP, w którym może wystąpić wyjątek dopisujemy następującą deklarację
1 |
<%@ page errorPage="errorpage.jsp"%> |
A w pliku errorpage.jsp dopisujemy.
1 |
<%@ page isErrorPage="true"%> |
W wyniku powyższych czynności możemy użyć obiektu exception
i wywołać np. metodę getMessage()
.
W poprzednim rozdziale pokazaliśmy jak łatwo jest używać Javy pisząc jej kod w skryptletach. Jednakże nie jest to dobra praktyka, gdyż w przypadku większej aplikacji nieładnie wygląda przeplatanie Javy i kodu html. Znacznie lepiej jest rozdzielić warstwę prezentacji danych od ich przetwarzania. Przydatne w tym jest zrozumienie wzorca projektowego MVC (ang. model view controller).
Wspomniany JSTL jest biblioteką zawierającą wiele tagów pomagających używanie typowych struktur. W ich skład wchodzą instrukcje warunkowe, pętle, SQL oraz XML.
Pierwszym krokiem powinno być pobranie z sieci dostępnej biblioteki. Następnie należy umieścić ją w w projekcie w folderze WebContent\webinf\lib. Ostatnim krokiem jest dodanie dyrektywy.
1 |
<%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %> |
W tej grupie znajdują się najczęściej wykorzystywane tagi. Wśród nich znajdziemy obsługę pętli, instrukcji warunkowych, przekierowań oraz wyświetlanie danych oraz ich zapis. Poniżej pokażemy kilka przykładów.
Zapisywanie i wyświetlanie zmiennych.
1 2 |
<c:set var = "a" scope = "session" value = "10"/> <c:out value = "${a}"/> |
Instrukcja warunkowa if.
1 2 3 |
<c:if test = "instrukcja warunkowa"> Zrób coś </c:if> |
Instrukcja warunkowa choose jest odpowiednikiem if-else.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<c:choose> <c:when test = "${instrukcja warunkowa}"> Zrób coś </c:when> <c:when test = "${instrukcja warunkowa}"> Zrób coś innego </c:when> <c:otherwise> W przeciwnym wypadku </c:otherwise> </c:choose> |
Poniżej pokażemy przykładowy projekt składający się z trzech plików. Startowym będzie serwlet symulujący pobranie danych z bazy, Drugim dokument JSP wyświetlający dane (warstwa prezentacji), a trzeci będzię zwykłą klasą reprezentującą model.
Zwróćmy uwagę na klasę Gory
, która reprezentuje nasz model. Utworzyliśmy ją zgodnie z dobrą praktyką czyli pole jest prywatne. W związku z tym niezbędne jest dodanie metod – getNazwa()
i setNazwa()
trzymając się konwencji nazewniczej. W przeciwnym przypadku kontener zwróci błąd, gdyż nie będzie mógł dostać się do prywatnego pola.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package pl.programistajava; import java.io.IOException; import java.util.Arrays; import java.util.List; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/Demo") public class Demo extends HttpServlet { private static final long serialVersionUID = 1L; public Demo() { super(); // TODO Auto-generated constructor stub } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { List<Gory> gory = Arrays.asList(new Gory("Tatry"), new Gory("Bieszczady"), new Gory("Pieniny")); request.setAttribute("listaGor", gory); RequestDispatcher rd = request.getRequestDispatcher("demo.jsp"); rd.forward(request, response); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?xml version="1.0" encoding="UTF-8" ?> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Lista Gór</title> <style> body { background: #f2f2f2; } </style> </head> <body> <h1>Lista Gór</h1> <c:set var = "s" scope = "page" value = "${listaGor.size()}"/> <c:if test="${s > 0}"> Przesłano <c:out value="${s}"></c:out> elementy<br><br> <c:forEach var="g" items="${listaGor}"> <c:out value="${g.nazwa}"></c:out><br> </c:forEach> </c:if> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package pl.programistajava; public class Gory { private String nazwa; public String getNazwa() { return nazwa; } public void setNazwa(String nazwa) { this.nazwa = nazwa; } public Gory(String nazwa) { super(); this.nazwa = nazwa; } } |
Wynik uruchomienia powyższego kodu jest następujący.
1 |
<%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/sql" %> |
JSTL pozwala na połączenie z bazą danych bez potrzeby używania kodu Javy. Wystarczy użyć specjalnych tagów. Po dokładniejsze informacje na temat baz danych opisaliśmy już w jednym z poprzednich w wpisów.
Pierwszym krokiem jest wskazanie źródła danych i służy do tego tag <sql:setDataSource/>
.
1 |
<sql:setDataSource var="db" driver="com.mysql.cj.jdbc.Driver" url="jdbc:mysql://localhost/database" user="sql" password="sql"/> |
Po połączeniu się z bazą danych możemy wysłać do niej zapytanie. Tym razem wykorzystamy tag <sql:query/>
1 2 3 |
<sql:query var="rs" dataSource="${db}"> SELECT * FROM Gory </sql:query> |
Wewnątrz tagów znajduje się zapytanie napisane w języku SQL. Dodatkowo wskazujemy dwa atrybuty.
Ostatnim krokiem jest wyświetlenie otrzymanych danych. W tym celu wykorzystamy znane już tagi core.
1 2 3 |
<c:forEach var="gora" items="${rs.rows}"> <c:out value="${gora.Nazwa}"></c:out> </c:forEach> |
Pobieramy dane do wyświetlenia ze zmiennej gora
. Następnie wybieramy do wyświetlenia kolumnę Nazwa.
About the author