W tej lekcji dowiemy się, jak dodawać tzw. serwisy — element pośredni pomiędzy bazą danych (naszymi klasami DAO) a widokami (naszymi kontrolerami).
Serwisy to bardzo ważny element każdej aplikacji — stanowią element, dzięki któremu możemy realizować funkcjonalności na różnych typach obiektów, które są w naszej aplikacji, zachowując właściwy podział kodu naszej aplikacji. Brzmi trochę strasznie, wiem, ale jak zobaczysz na przykładach, nie jest to takie straszne :) Ale zaczniemy od odrobiny teorii, żeby lepiej zrozumieć skąd to wynika
Lekcja
Trójwarstwowy podział aplikacji
Obecnie dąży się do tego, żeby budować aplikacje wg podziału na 3 logiczne elementy, pokazane na rysunku obok:
Możemy wyszczególnić trzy warstwy:
- warstwa persystencji — z punktu widzenia kodu są to wszystkie klasy, które są odpowiedzialne za pobieranie i zapisywanie danych w bazie danych, a więc encje oraz dao; do tej warstwy często zalicza się także samą bazę danych (jak wspominałem, jest to podział wyłącznie logiczny)
- warstwa usług / logiki biznesowej — na tej warstwie skupimy się dzisiaj, są to klasy, które korzystają z niższej warstwy (persystencji) oraz z innych klas w tej samej warstwie. Taka ‘pozioma’ komunikacja występuje jedynie w tej warstwie (np. jedno DAO nie korzysta z innego DAO, podobnie jak jeden kontroler nie korzysta z innego kontrolera). Jest to więc warstwa łącząca — powinna udostępniać wszystkie operacje, które mogą być realizowane przez kontrolery, a więc wszystko to, co udostępniają klasy DAO a także dodatkowe operacje (np. wysłanie maila, ustawienie powiadomienia itp). Serwisy możemy sobie podzielić dodatkowo na dwie podgrupy (technicznie taki podział nie istnieje — ale dla lepszego zobrazowania sobie sytuacji możemy tak to opisywać):
- serwisy domenowe — serwisy, które udostępniają operacje związane z konkretnym obiektem domenowym (np. odczyt/zapis do bazy danych, wyszukiwanie w bazie danych, usuwanie, aktualizacja itp)
- serwisy biznesowe — serwisy, które realizują pewną funkcjonalność, proces biznesowy, integracje z innym systemem itp, dla przykładu weźmy EmailService — serwis, który wysyła maile (przyjmując tylko adres odbiorcy, tytuł i treść, zawiera logikę odpowiedzialną za komunikację z serwerem poczty, logowaniem się do niego, faktycznym wysłaniem maila itp) czy też CurrencyService — serwis, który pozwala obliczyć wartość wyrażoną w innej walucie (wewnętrznie taki serwis będzie korzystał np. z interfejsów udostępnianych przez bank czy też kursów wpisywanych ręcznie przez administratora). Serwisy mogą być używane przez kontrolery (np. formularz kontaktowy wysyła wiadomość mailem do administracji) jak i przez inne serwisy (np. serwis który obsługuje rejestrację użytkowników, wysyła podczas rejestracji maila z prośbą o potwierdzenie)
- warstwa prezentacji — tutaj mieszczą się zarówno same widoki (pliki JSP) jak też kontrolery. Role kontrolera w aplikacji są dwie:
- pobranie danych za pośrednictwem serwisów i przygotowanie ich do wyświetlenia użytkownikowi końcowemu (np. zaokrąglając, sumując itp)
- odebranie danych od użytkownika (np. wysłanych poprzez formularz), walidacja (odsyłam do lekcji o obsłudze formularzy, gdzie uczyliśmy się jak to robić) oraz przekazanie tak przygotowanych danych do serwisów (w których to realizowane są konkretne działania, algorytm, wywoływane są inne systemy itp)
Tworzenie serwisu w Springu
Serwis to nic innego jak klasa, która ma publiczne metody. Aby Spring ‘widział’ naszą klasę jako serwis, dodajemy adnotację @Service . Przykładowy serwis wygląda następująco:
@Service
public class NiepotrzebnyService {
public String zwrocJedenWyraz() {
return "jeden";
}
public String zwrocDwaWyrazy() {
return "dwa wyrazy";
}
}
Aby użyć naszego serwisu w innym serwisie lub w kontrolerze, po prostu dodajemy go jako pole w klasie, w której chcemy użyć, z adnotacją @Autowired :
@Autowired
protected NiepotrzebnyService niepotrzebnyService;
Oczywiście obecnie nie jest on zbyt użyteczny, ale chodzi o pokazanie jak to działa. Kluczowa jest tutaj adnotacja @Service (to jeden z tzw. stereotypów — mówiliśmy o nich krótko w jednej z poprzednich lekcji), która ‘mówi’ Springowi, że jest to serwis, i pozwala nam używać go w innych miejscach (korzystając z pomocy adnotacji @Autowired).
Jako bardziej praktyczny przykład, zaimplementujmy serwis do wysyłki maili.
Piszemy serwis do wysyłania maili
Do wysyłania maili wykorzystamy pakiet javax.mail, musimy więc dodać go najpierw w pliku pom.xml jako dependency (jesli nie pamiętasz jak, zerknij do lekcji o Mavenie). W tym wypadku będziemy potrzebowali dwóch zależności — jedna to API (interfejsy), druga to implementacja (konkretne klasy i kod). Znajdziesz je pod postacią artefaktów: javax.mail:javax.mail-api (interfejsy) oraz com.sun.mail:javax.mail (implementacja).
Oczywiście dane potrzebne do logowania (adres email oraz hasło) musisz podać sama — musi to być działające konto gmail (uwga: ze względu na nie do końca bezpieczny mechanizm wykorzystywany przy logowaniu, konieczne jest wyłącenie dodatkowych zabezpieczeń!)
Kolejnym krokiem jest stworzenie klasy EmailService z metodą sendEmail, która przyjmie trzy argumenty typu String — email odbiorcy, tytuł wiadomości i treść wiadomości i zwraca wartość typu prawda/fałsz — czy wysyłanie się powiodło. Jej szkielet wygląda nastepująco:
public class EmailService {
public boolean sendEmail(String recipientEmail, String subject, String content) {
//logika wysyłania maili
return false;
}
}
Następnie implementujemy logikę na podstawie dokumentacji pakietu javax.mail . Poniżej przedstawiam działający kod dla poczty gmail — niestety, jeśli posiadasz konto pocztowe w innym serwisie, ustawienia (szczególnie te związane z bezpieczeństwem) mogą być nieco inne. Wybraliśmy gmail ponieważ można tam bezpłatnie założyć konto, a większość z nas takie konto posiada. Musimy podać dane do naszego konta email, ponieważ będzie ono używane jako nadawca wysyłanych przez nas maili.
@Service
public class EmailService {
protected String mailSmtpAuth = "true";
protected String mailSmtpHost = "smtp.gmail.com";
protected String mailSmtpPort = 587;
protected String mailSmtpStarttlsEnable = "true";
protected String mailEmailFrom = "[email protected]";
protected String username = "[email protected]";
protected String password = "haslo";
public boolean sendEmail(String recipientEmail, String subject, String content) {
Properties props = new Properties();
props.put("mail.smtp.auth", mailSmtpAuth);
props.put("mail.smtp.starttls.enable", mailSmtpStarttlsEnable);
props.put("mail.smtp.host", mailSmtpHost);
props.put("mail.smtp.port", mailSmtpPort);
props.put("mail.smtp.ssl.trust", mailSmtpHost);
Session session = Session.getInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(mailEmailFrom));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipientEmail));
message.setSubject(subject);
message.setText(content);
Transport.send(message);
return true;
} catch (MessagingException e) {
//tutaj należy obsłużyć wyjątek - zobacz http://kobietydokodu.pl/niezbednik-juniora-wyjatki-i-ich-obsluga/
}
return false;
}
}
Uwaga! Aby ta metoda działała, musimy także zmienić ustawienia w Gmailu, aby pozwolił nam łączyć się w uproszczony sposób (nie korzystając z całkowicie bezpiecznych mechanizmów). Można to zrobić (po zalogowaniu się) na stronie https://www.google.com/settings/security/lesssecureapps. Pamiętaj, aby po zakończeniu testowania (albo wprowadzeniu danych innego konta) przywrócić to ustawienie do domyślnej wartości!
Podsumowanie
W dzisiejszej lekcji nauczyliśmy się, czym są serwisy i jak je tworzyć w Springu. Tak jak obiecywałem, było prościej niż brzmiało ;) Serwisy to bardzo ważna część aplikacji i jednocześnie taka, którą najtrudniej zautomatyzować — jest ona bowiem związana ściśle z logiką biznesową, a nie standardowymi operacjami. Dlatego warto poświęcić chwilę dłużej i poczytać nieco więcej :)
Zadanie
Zmodyfikuj program, który już napisałaś, dodając do niego warstwę serwisów — póki co dla każdego repozytorium (każdego DAO, czyli naszych interfejsów z adnotacją @Repository) utwórz serwis, który oferuje takie same operacje (i kieruje je właśnie do naszego DAO używając go za pomocą pola z adnotacją @Autowired).
Jeśli uważasz powyższą lekcję za przydatną, mamy małą prośbę: polub nasz fanpage. Dzięki temu będziesz zawsze na bieżąco z nowymi treściami na blogu ( i oczywiście, z nowymi częściami kursu Javy). Dzięki!