Tworzenie obiektów
W pakiecie Entity utworzymy klasę User , a w niej będą znajdować się dwie prywatne zmienne typu string name i pass . Stwórzmy konstruktory (domyślny i taki, który akceptowałby obie wartości), gettery/settery, na wszelki wypadek nadpiszmy metodę toString() oraz metody równości() i hashCode() . Oznacza to, że zrobimy wszystko, co przy tworzeniu klasy robi porządny programista Java.public class User {
private String name;
private String password;
public User() {
}
public User(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (name != null ? !name.equals(user.name) : user.name != null) return false;
return password != null ? password.equals(user.password) : user.password == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (password != null ? password.hashCode() : 0);
return result;
}
}
Teraz możemy przystąpić do tworzenia listy użytkowników. Dodamy do niego użytkowników i skąd zabierzemy ich do wyświetlenia. Jednakże jest jeden problem. Nie tworzymy naszych obiektów serwletów, Tomcat robi to za nas . Metody, które w nich nadpisujemy, również są już dla nas zdefiniowane i nie możemy dodać parametru. Jak zatem możemy utworzyć wspólną listę, która będzie widoczna dla obu naszych serwletów? Jeśli po prostu utworzymy w każdym serwlecie własny obiekt listowy, okaże się, że do jednej listy dodamy użytkowników, a do innej wyświetlimy listę użytkowników korzystających z serwletu ListServlet . Okazuje się, że potrzebujemy obiektu, który byłby wspólny dla obu serwletów. Ogólnie rzecz biorąc, potrzebujemy obiektu, który byłby wspólny dla wszystkich klas w naszym programie; jedyny obiekt dla całego programu. Mam nadzieję, że słyszałeś coś o wzorcach projektowych. Być może dla niektórych jest to pierwsza realna potrzeba użycia wzorca Singletona w swoim programie. Można dać się zboczyć i stworzyć fajnego Singletona z podwójnymi kontrolami i synchronizacją (tak, mamy aplikację wielowątkową, ponieważ Tomcat uruchamia serwlety w różnych wątkach), ale ja skorzystam z opcji z wczesną inicjalizacją, bo tutaj jest całkiem odpowiednia wystarczające i odpowiada naszym celom.
Tworzenie modelu
Stwórzmy klasę (i zaimplementujmy w niej wzorzec Singleton ) w pakiecie modelu i nazwijmy ją czymś nietypowym. Powiedzmy Modelka . Stwórzmy w naszej klasie obiekt prywatnej listy użytkowników i zaimplementujmy dwie metody: jedną, dzięki której będziemy mogli dodać użytkownika, i drugą, która zwróci listę ciągów znaków (nazw użytkowników). Ponieważ nasz obiekt użytkownika składa się z nazwy i hasła, a nie chcielibyśmy „ujawniać” haseł użytkowników, będziemy mieli jedynie listę nazw.public class Model {
private static Model instance = new Model();
private List<User> model;
public static Model getInstance() {
return instance;
}
private Model() {
model = new ArrayList<>();
}
public void add(User user) {
model.add(user);
}
public List<String> list() {
return model.stream()
.map(User::getName)
.collect(Collectors.toList());
}
}
Trochę o mvc
Skoro słyszałeś o singletonie , to prawdopodobnie słyszałeś o innym wzorcu projektowym - MVC (kontroler widoku modelu, po rosyjsku kontroler widoku modelu lub tak samo jak w języku angielskim kontroler widoku modelu). Jego istotą jest oddzielenie logiki biznesowej od prezentacji. Oznacza to, że należy oddzielić kod określający, co należy zrobić, od kodu określającego sposób wyświetlania. Widok (widok lub po prostu widoki) odpowiada za formę, w jakiej prezentowane są niektóre dane. W naszym przypadku widoki to nasze strony JSP. Dlatego umieściłem je w folderze o nazwie widoki . Model to rzeczywiste dane, z którymi program współpracuje. W naszym przypadku są to użytkownicy (lista użytkowników). Cóż, kontrolery są ogniwem łączącym między nimi. Pobierają dane z modelu i przekazują je do widoków (lub odbierają dane z Tomcata, przetwarzają je i przekazują do modelu). Logikę biznesową (co dokładnie program powinien robić) trzeba w nich opisać, a nie w modelu czy w widoku. Zatem każdy robi swoje:- model przechowuje dane;
- widoki rysują piękną reprezentację danych;
- administratorzy zajmują się przetwarzaniem danych.
<form method="post">
<label>Name:
<input type="text" name="name"><br />
</label>
<label>Password:
<input type="password" name="pass"><br />
</label>
<button type="submit">Submit</button>
</form>
Tutaj formularz ma atrybut metody z wartością post . Oznacza to, że dane z tego formularza zostaną przesłane na serwer w formie żądania POST. Atrybut akcji nie jest określony, co oznacza, że żądanie zostanie wysłane na ten sam adres, z którego weszliśmy na tę stronę ( /add ). Tym samym nasz serwlet powiązany z tym adresem po otrzymaniu żądania GET zwraca ten jsp z formularzem dodawania użytkowników, a jeśli otrzyma żądanie POST, to znaczy, że formularz wysłał tam swoje dane (które pobierzemy z request w metodzie doPost() Warto zaznaczyć, że pola wejściowe posiadają parametr name (dla pola z nazwą ma wartość nazwa, a dla pola z hasłem wartość pass ). To dość ważny punkt. Ponieważ aby uzyskać te dane (nazwę i hasło, które zostanie wprowadzone) z żądania (już znajdującego się w serwlecie), użyjemy dokładnie tej nazwy i passu . Ale o tym później. Sam przycisk do wysyłania danych jest ponownie wykonany w formie przycisku , a nie jako pole wyjściowe, jak to zwykle jest w zwyczaju. Nie wiem na ile ta opcja jest uniwersalna, ale u mnie (przeglądarka Chrome) działa.
Przetwarzanie żądania POST za pomocą serwletu
Wróćmy do serwletu AddServlet . Przypomnę: aby nasz serwlet mógł „przechwytywać” żądania GET, nadpisaliśmy metodę doGet() z klasy HttpServlet . Aby nauczyć nasz serwlet przechwytywania żądań POST, musimy także zastąpić metodę doPost() . Otrzymuje podobne obiekty żądań i odpowiedzi od Tomcat , z którym będziemy pracować. Najpierw wyodrębnijmy z żądania nazwę i przekażmy parametry, które wysłał formularz (jeśli w formularzu nazwałeś je inaczej, to takie właśnie nazwy wpisujesz). Następnie na podstawie otrzymanych danych utworzymy nasz obiekt użytkownika. Następnie pobierzemy obiekt modelu i dodamy utworzonego użytkownika do modelu.@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
String password = req.getParameter("pass");
User user = new User(name, password);
Model model = Model.getInstance();
model.add(user);
}
Przekazywanie danych do przeglądania
Przejdźmy do serwletu ListServlet . Zaimplementowano już tutaj metodę doGet() , która po prostu przekazuje kontrolę do widoku list.jsp . Jeśli jeszcze tego nie masz, zrób to analogicznie tą samą metodą z serwletu AddServlet . Teraz byłoby miło pobrać listę nazw użytkowników z modelu i przekazać je do widoku, który je odbierze i ładnie wyświetli. W tym celu ponownie skorzystamy z obiektu żądania, który otrzymaliśmy od Tomcata . Do tego obiektu możemy dodać atrybut, nadać mu nazwę, a właściwie sam obiekt, który chcemy przenieść do obejrzenia . Z uwagi na to, że przenosząc proces wykonania z serwletu do widoku, przekazujemy tam te same obiekty żądań i odpowiedzi, które sam serwlet otrzymał, to dodając do obiektu żądania naszą listę nazw, możemy wówczas z tego żądania obiekt w widoku utwórz naszą listę nazw użytkowników i pobierz. Skończyliśmy z klasą ListServlet , więc oto kod całej klasy:package app.servlets;
import app.model.Model;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class ListServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Model model = Model.getInstance();
List<String> names = model.list();
req.setAttribute("userNames", names);
RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/list.jsp");
requestDispatcher.forward(req, resp);
}
}
Wykonywanie kodu Java w plikach jsp
Czas zacząć pracę nad plikiem list.jsp . Zostanie wykonany tylko wtedy, gdy ListServlet przejdzie tutaj proces wykonywania. Dodatkowo w tym serwlecie przygotowaliśmy już listę nazw użytkowników z modelu i przekazaliśmy ją tutaj w obiekcie żądania. Ponieważ mamy listę nazwisk, możemy ją przeglądać i drukować każde imię. Jak już powiedziałem, pliki jsp mogą wykonywać kod Java (co odróżnia je od statycznych stron HTML). Aby wykonać jakiś kod wystarczy w odpowiednim miejscu umieścić następującą konstrukcję:<!-- html kod -->
<%
// java kod
%>
<!-- html kod -->
Wewnątrz tej konstrukcji mamy dostęp do kilku zmiennych:
- request to nasz obiekt żądania, który przekazaliśmy z serwletu, gdzie nazywał się po prostu req ;
- odpowiedź - obiekt odpowiedzi, nazywany w serwlecie resp ;
-
out to obiekt typu JspWriter (odziedziczony po zwykłym Writerze ), za pomocą którego możemy „wpisać” coś bezpośrednio na samą stronę HTML. Wpis out.println("Witaj, świecie!") jest bardzo podobny do wpisu System.out.println("Witaj, świecie!") , ale nie myl tych dwóch!
out.println() „zapisuje” na stronie HTML, a System.out.println zapisuje na wyjściu systemowym. Jeśli wywołasz metodę jsp System.out.println() w sekcji z kodem Java , wyniki zobaczysz w konsoli Tomcat , a nie na stronie.
<ul>
<%
List<String> names = (List<String>) request.getAttribute("userNames");
if (names != null && !names.isEmpty()) {
for (String s : names) {
out.println("<li>" + s + "</li>");
}
}
%>
</ul>
Jeśli chcesz wyświetlić listę tylko wtedy, gdy istnieją użytkownicy, lub w inny sposób wyświetlić ostrzeżenie, że nie ma jeszcze użytkowników, możemy nieco przepisać tę sekcję:
<%
List<String> names = (List<String>) request.getAttribute("userNames");
if (names != null && !names.isEmpty()) {
out.println("<ui>");
for (String s : names) {
out.println("<li>" + s + "</li>");
}
out.println("</ui>");
} else out.println("<p>There are no users yet!</p>");
%>
Teraz, gdy możemy przekazywać dane z serwletów do widoków, możemy nieco ulepszyć nasz AddServlet, tak aby wyświetlało się powiadomienie po pomyślnym dodaniu użytkownika. W tym celu w metodzie doPost() po dodaniu nowego użytkownika do modelu możemy dodać nazwę tego użytkownika do atrybutów obiektu req i przekazać kontrolę z powrotem do widoku add.jsp . A w nim już utwórz sekcję z kodem Java, w której sprawdzane jest, czy taki atrybut znajduje się w żądaniu, a jeśli tak, to wyświetla komunikat informujący, że użytkownik został pomyślnie dodany. Po tych zmianach kompletny kod serwletu AddServlet będzie wyglądał mniej więcej tak:
package app.servlets;
import app.entities.User;
import app.model.Model;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class AddServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/add.jsp");
requestDispatcher.forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
String password = req.getParameter("pass");
User user = new User(name, password);
Model model = Model.getInstance();
model.add(user);
req.setAttribute("userName", name);
doGet(req, resp);
}
}
Tutaj na końcu metody doPost() ustawiamy atrybut z nazwą użytkownika dodanego do modelu, po czym wywołujemy metodę doGet() , do której przekazujemy aktualne żądanie i odpowiedź. Natomiast metoda doGet() przekazuje już kontrolę do widoku, gdzie wysyła obiekt żądania z dołączoną jako atrybut nazwą dodanego użytkownika. Pozostaje tylko poprawić add.jsp tak, aby wyświetlał takie powiadomienie, jeśli taki atrybut jest obecny. Ostateczny plik add.jsp to :
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Add new user</title>
</head>
<body>
<div>
<h1>Super app!</h1>
</div>
<div>
<%
if (request.getAttribute("userName") != null) {
out.println("<p>User '" + request.getAttribute("userName") + "' added!</p>");
}
%>
<div>
<div>
<h2>Add user</h2>
</div>
<form method="post">
<label>Name:
<input type="text" name="name"><br />
</label>
<label>Password:
<input type="password" name="pass"><br />
</label>
<button type="submit">Submit</button>
</form>
</div>
</div>
<div>
<button onclick="location.href='/'">Back to main</button>
</div>
</body>
</html>
Korpus strony składa się z:
- div-a z nagłówkiem;
- div kontener na treść, sprawdza, czy istnieje atrybut z nazwą użytkownika;
- div z formularzem dodawania użytkowników;
- a na końcu znajduje się stopka z przyciskiem powrotu do strony głównej.
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Users</title>
</head>
<body>
<div>
<h1>Super app!</h1>
</div>
<div>
<div>
<div>
<h2>Users</h2>
</div>
<%
List<String> names = (List<String>) request.getAttribute("userNames");
if (names != null && !names.isEmpty()) {
out.println("<ui>");
for (String s : names) {
out.println("<li>" + s + "</li>");
}
out.println("</ui>");
} else out.println("<p>There are no users yet!</p>");
%>
</div>
</div>
<div>
<button onclick="location.href='/'">Back to main</button>
</div>
</body>
</html>
Tym samym mamy w pełni działającą aplikację internetową, która może przechowywać i dodawać użytkowników, a także wyświetlać listę ich nazw. Pozostaje tylko ozdobić... :)
Dodawanie stylów. Używamy frameworka W3.CSS
W tej chwili nasza aplikacja działa, ale jest absolutnie szalona. Dlatego dodamy tło, kolor tekstu i przycisków, listy stylów, dokonamy wyrównania, dodamy wcięcia i tym podobne. Jeśli piszesz style ręcznie, może to zająć dużo czasu i nerwów. Dlatego sugeruję użycie frameworka CSS W3.CSS. Ma już gotowe klasy ze stylami, pozostaje tylko umieścić w odpowiednich miejscach klasy CSS, które chcemy zastosować. Aby dodać je do naszych stron, najpierw załączymy plik ze stylami. Można to zrobić na dwa sposoby:-
przejrzyj nasze strony i w sekcji head wstaw bezpośredni link do pliku ze stylami
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
Ta opcja jest odpowiednia, jeśli masz stałe połączenie z Internetem. Następnie, gdy otworzysz strony na serwerze lokalnym, style zostaną pobrane z Internetu.
-
Jeśli chcesz mieć wszystkie style lokalnie i nie być zależnym od połączenia internetowego, pobierz plik ze stylami i umieść go gdzieś w folderze web (na przykład web/styles/w3.css ), a następnie przejrzyj wszystkie nasze strony ( index.html, add.jsp, list.jsp ) i w sekcji head wpisz link do tego pliku ze stylami
<link rel="stylesheet" href="styles/w3.css">
Następnie po prostu przejrzyj tagi i dodaj style, które Ci się podobają. Nie będę się nad tym szczegółowo rozwodzić, ale od razu podam gotowe wersje moich trzech plików z ułożonymi klasami stylów.
indeks.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Super app!</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body class="w3-light-grey">
<div class="w3-container w3-blue-grey w3-opacity w3-right-align">
<h1>Super app!</h1>
</div>
<div class="w3-container w3-center">
<div class="w3-bar w3-padding-large w3-padding-24">
<button class="w3-btn w3-hover-light-blue w3-round-large" onclick="location.href='/list'">List users</button>
<button class="w3-btn w3-hover-green w3-round-large" onclick="location.href='/add'">Add user</button>
</div>
</div>
</body>
</html>
dodaj.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Add new user</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body class="w3-light-grey">
<div class="w3-container w3-blue-grey w3-opacity w3-right-align">
<h1>Super app!</h1>
</div>
<div class="w3-container w3-padding">
<%
if (request.getAttribute("userName") != null) {
out.println("<div class=\"w3-panel w3-green w3-display-container w3-card-4 w3-round\">\n" +
" <span onclick=\"this.parentElement.style.display='none'\"\n" +
" class=\"w3-button w3-margin-right w3-display-right w3-round-large w3-hover-green w3-border w3-border-green w3-hover-border-grey\">×</span>\n" +
" <h5>User '" + request.getAttribute("userName") + "' added!</h5>\n" +
"</div>");
}
%>
<div class="w3-card-4">
<div class="w3-container w3-center w3-green">
<h2>Add user</h2>
</div>
<form method="post" class="w3-selection w3-light-grey w3-padding">
<label>Name:
<input type="text" name="name" class="w3-input w3-animate-input w3-border w3-round-large" style="width: 30%"><br />
</label>
<label>Password:
<input type="password" name="pass" class="w3-input w3-animate-input w3-border w3-round-large" style="width: 30%"><br />
</label>
<button type="submit" class="w3-btn w3-green w3-round-large w3-margin-bottom">Submit</button>
</form>
</div>
</div>
<div class="w3-container w3-grey w3-opacity w3-right-align w3-padding">
<button class="w3-btn w3-round-large" onclick="location.href='/'">Back to main</button>
</div>
</body>
</html>
lista.jsp
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Users list</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body class="w3-light-grey">
<div class="w3-container w3-blue-grey w3-opacity w3-right-align">
<h1>Super app!</h1>
</div>
<div class="w3-container w3-center w3-margin-bottom w3-padding">
<div class="w3-card-4">
<div class="w3-container w3-light-blue">
<h2>Users</h2>
</div>
<%
List<String> names = (List<String>) request.getAttribute("userNames");
if (names != null && !names.isEmpty()) {
out.println("<ul class=\"w3-ul\">");
for (String s : names) {
out.println("<li class=\"w3-hover-sand\">" + s + "</li>");
}
out.println("</ul>");
} else out.println("<div class=\"w3-panel w3-red w3-display-container w3-card-4 w3-round\">\n"
+
" <span onclick=\"this.parentElement.style.display='none'\"\n" +
" class=\"w3-button w3-margin-right w3-display-right w3-round-large w3-hover-red w3-border w3-border-red w3-hover-border-grey\">×</span>\n" +
" <h5>There are no users yet!</h5>\n" +
"</div>");
%>
</div>
</div>
<div class="w3-container w3-grey w3-opacity w3-right-align w3-padding">
<button class="w3-btn w3-round-large" onclick="location.href='/'">Back to main</button>
</div>
</body>
</html>
To wszystko :) Jeśli masz jeszcze jakieś pytania, uwagi, albo wręcz przeciwnie, coś nie gra - zostaw komentarz. UPD: jeśli masz problemy z błędem 404 podczas klikania przycisków, mimo że wszystko zostało zrobione poprawnie, być może powinieneś poprawić konfigurację wdrożenia w pomyśle. W tym celu należy przejść do opcji Edytuj konfiguracje (na górze przy przycisku start), przejść do zakładki Wdrożenie po prawej stronie okna i upewnić się, że w kontekście aplikacji jest to po prostu zaznaczone /No cóż, ja' Dołączę kilka zrzutów ekranu pokazujących, co z tego wyszło.
- utwórz serwlet i plik jsp, aby usunąć użytkownika i kilka innych, aby zmienić/edytować istniejącego użytkownika. Otrzymasz prawdziwą aplikację internetową CrUD :) na serwletach));
- zamień listę (List) na pracę z bazą danych, aby dodani użytkownicy nie zniknęli po ponownym uruchomieniu serwera :)
Co jeszcze warto przeczytać: |
---|
Tworzenie prostego projektu internetowego w IntelliJ Idea Enterprise. Krok po kroku, ze zdjęciami |
GO TO FULL VERSION