#14.1 — serwisy (warstwa pośrednia)

By 13 January 2015 September 18th, 2016 Kurs Javy

W tej lekcji dowiemy się, jak dodawać tzw. ser­wisy — ele­ment pośred­ni pomiędzy bazą danych (naszy­mi klasa­mi DAO) a widoka­mi (naszy­mi kontrolerami).

Ser­wisy to bard­zo ważny ele­ment każdej aplikacji — stanow­ią ele­ment, dzię­ki które­mu może­my real­i­zować funkcjon­al­noś­ci na różnych typach obiek­tów, które są w naszej aplikacji, zachowu­jąc właś­ci­wy podzi­ał kodu naszej aplikacji. Brz­mi trochę strasznie, wiem, ale jak zobaczysz na przykładach, nie jest to takie straszne :) Ale zaczniemy od odrobiny teorii, żeby lep­iej zrozu­mieć skąd to wynika

Lekcja

Trójwarstwowy podział aplikacji

Obec­nie dąży się do tego, żeby budować aplikac­je wg podzi­ału na 3 log­iczne ele­men­ty, pokazane na rysunku obok:

Może­my wyszczegól­nić trzy warstwy:

  • warst­wa per­sys­tencji — z punk­tu widzenia kodu są to wszys­tkie klasy, które są odpowiedzialne za pobieranie i zapisy­wanie danych w bazie danych, a więc enc­je oraz dao; do tej warst­wy częs­to zal­icza się także samą bazę danych (jak wspom­i­nałem, jest to podzi­ał wyłącznie logiczny)
  • warst­wa usług / logi­ki biz­ne­sowej — na tej warst­wie skupimy się dzisi­aj, są to klasy, które korzys­ta­ją z niższej warst­wy (per­sys­tencji) oraz z innych klas w tej samej warst­wie. Taka ‘pozioma’ komu­nikac­ja wys­tępu­je jedynie w tej warst­wie (np. jed­no DAO nie korzys­ta z innego DAO, podob­nie jak jeden kon­trol­er nie korzys­ta z innego kon­trol­era). Jest to więc warst­wa łączą­ca — powin­na udostęp­ni­ać wszys­tkie oper­ac­je, które mogą być real­i­zowane przez kon­trol­ery, a więc wszys­tko to, co udostęp­ni­a­ją klasy DAO a także dodatkowe oper­ac­je (np. wysłanie maila, ustaw­ie­nie powiadomienia itp). Ser­wisy może­my sobie podzielić dodatkowo na dwie pod­grupy (tech­nicznie taki podzi­ał nie ist­nieje — ale dla lep­szego zobra­zowa­nia sobie sytu­acji może­my tak to opisywać): 
    • ser­wisy domenowe — ser­wisy, które udostęp­ni­a­ją oper­ac­je związane z konkret­nym obiek­tem domenowym (np. odczyt/zapis do bazy danych, wyszuki­wanie w bazie danych, usuwanie, aktu­al­iza­c­ja itp)
    • ser­wisy biz­ne­sowe — ser­wisy, które real­izu­ją pewną funkcjon­al­ność, pro­ces biz­ne­sowy, inte­grac­je z innym sys­te­mem itp, dla przykładu weźmy EmailSer­vice — ser­wis, który wysyła maile (przyj­mu­jąc tylko adres odbior­cy, tytuł i treść, zaw­iera logikę odpowiedzial­ną za komu­nikację z ser­w­erem pocz­ty, logowaniem się do niego, fak­ty­cznym wysłaniem maila itp) czy też Cur­ren­cy­Ser­vice — ser­wis, który pozwala obliczyć wartość wyrażoną w innej walu­cie (wewnętrznie taki ser­wis będzie korzys­tał np. z inter­fe­jsów udostęp­ni­anych przez bank czy też kursów wpisy­wanych ręcznie przez admin­is­tra­to­ra). Ser­wisy mogą być uży­wane przez kon­trol­ery (np. for­mu­la­rz kon­tak­towy wysyła wiado­mość mailem do admin­is­tracji) jak i przez inne ser­wisy (np. ser­wis który obsługu­je rejes­trację użytkown­ików, wysyła pod­czas rejes­tracji maila z prośbą o potwierdzenie)
  • warst­wa prezen­tacji — tutaj mieszczą się zarówno same wido­ki (pli­ki JSP) jak też kon­trol­ery. Role kon­trol­era w aplikacji są dwie: 
    • pobranie danych za pośred­nictwem ser­wisów i przy­go­towanie ich do wyświ­etle­nia użytkown­ikowi koń­cowe­mu (np. zaokrąglając, sumu­jąc itp)
    • ode­branie danych od użytkown­i­ka (np. wysłanych poprzez for­mu­la­rz), wal­i­dac­ja (odsyłam do lekcji o obsłudze for­mu­la­rzy, gdzie uczyliśmy się jak to robić) oraz przekazanie tak przy­go­towanych danych do ser­wisów (w których to real­i­zowane są konkretne dzi­ała­nia, algo­rytm, wywoły­wane są inne sys­te­my itp)

Tworzenie serwisu w Springu

Ser­wis to nic innego jak klasa, która ma pub­liczne metody. Aby Spring ‘widzi­ał’ naszą klasę jako ser­wis, doda­je­my adno­tację @Service . Przykład­owy ser­wis wyglą­da następująco:

@Service
public class NiepotrzebnyService {
    public String zwrocJedenWyraz() {
        return "jeden";
    }

    public String zwrocDwaWyrazy() {
        return "dwa wyrazy";
    }
}

Aby użyć naszego ser­wisu w innym ser­wisie lub w kon­trol­erze, po pros­tu doda­je­my go jako pole w klasie, w której chce­my użyć, z adno­tacją @Autowired :

@Autowired
protected NiepotrzebnyService niepotrzebnyService;

Oczy­wiś­cie obec­nie nie jest on zbyt użyteczny, ale chodzi o pokazanie jak to dzi­ała. Kluc­zowa jest tutaj adno­tac­ja @Service (to jeden z tzw. stereo­typów — mówiliśmy o nich krótko w jed­nej z poprzed­nich lekcji), która ‘mówi’ Springowi, że jest to ser­wis, i pozwala nam uży­wać go w innych miejs­cach (korzys­ta­jąc z pomo­cy adno­tacji @Autowired).

Jako bardziej prak­ty­czny przykład, zaim­ple­men­tu­jmy ser­wis do wysył­ki maili.

Piszemy serwis do wysyłania maili

Do wysyła­nia maili wyko­rzys­tamy paki­et javax.mail, musimy więc dodać go najpierw w pliku pom.xml jako depen­den­cy (jes­li nie pamię­tasz jak, zerknij do lekcji o Mave­nie). W tym wypad­ku będziemy potrze­bowali dwóch zależnoś­ci — jed­na to API (inter­fe­jsy), dru­ga to imple­men­tac­ja (konkretne klasy i kod). Zna­jdziesz je pod postacią arte­fak­tów: javax.mail:javax.mail-api (inter­fe­jsy) oraz com.sun.mail:javax.mail (imple­men­tac­ja).

Oczy­wiś­cie dane potrzeb­ne do logowa­nia (adres email oraz hasło) musisz podać sama — musi to być dzi­ała­jące kon­to gmail (uwga: ze wzglę­du na nie do koń­ca bez­pieczny mech­a­nizm wyko­rzysty­wany przy logowa­niu, konieczne jest  wyłące­nie dodatkowych zabez­pieczeń!)

Kole­jnym krok­iem jest stworze­nie klasy EmailSer­vice z metodą sendE­mail, która przyjmie trzy argu­men­ty typu String — email odbior­cy, tytuł wiado­moś­ci i treść wiado­moś­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ęp­nie imple­men­tu­je­my logikę na pod­staw­ie doku­men­tacji paki­etu javax.mail . Poniżej przed­staw­iam dzi­ała­ją­cy kod dla pocz­ty gmail — nieste­ty, jeśli posi­adasz kon­to pocz­towe w innym ser­wisie, ustaw­ienia (szczegól­nie te związane z bez­pieczeńst­wem) mogą być nieco inne. Wybral­iśmy gmail ponieważ moż­na tam bezpłat­nie założyć kon­to, a więk­szość z nas takie kon­to posi­a­da. Musimy podać dane do naszego kon­ta email, ponieważ będzie ono uży­wane jako nadaw­ca 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;
	}

}

Uwa­ga! Aby ta meto­da dzi­ałała, musimy także zmienić ustaw­ienia w Gmailu, aby poz­wolił nam łączyć się w uproszc­zony sposób (nie korzys­ta­jąc z całkowicie bez­piecznych mech­a­nizmów). Moż­na to zro­bić (po zal­o­gowa­niu się) na stron­ie https://www.google.com/settings/security/lesssecureapps. Pamię­taj, aby po zakończe­niu testowa­nia (albo wprowadze­niu danych innego kon­ta) przy­wró­cić to ustaw­ie­nie do domyśl­nej wartości!

Podsumowanie

W dzisiejszej lekcji nauczyliśmy się, czym są ser­wisy i jak je tworzyć w Springu. Tak jak obiecy­wałem, było proś­ciej niż brzmi­ało ;) Ser­wisy to bard­zo waż­na część aplikacji i jed­nocześnie taka, którą najtrud­niej zau­tomaty­zować — jest ona bowiem związana ściśle z logiką biz­ne­sową, a nie stan­dar­d­owy­mi oper­ac­ja­mi. Dlat­ego warto poświę­cić chwilę dłużej i poczy­tać nieco więcej :)

Materiały dodatkowe / dokumentacja

  1. Opis wzor­ca Ser­vice Lay­er (EN)

Zadanie

Zmody­fikuj pro­gram, który już napisałaś, doda­jąc do niego warst­wę ser­wisów — póki co dla każdego repozy­to­ri­um (każdego DAO, czyli naszych inter­fe­jsów z adno­tacją @Repository) utwórz ser­wis, który ofer­u­je takie same oper­ac­je (i kieru­je je właśnie do naszego DAO uży­wa­jąc go za pomocą pola z adno­tacją @Autowired).

Licencja Creative Commons

Jeśli uważasz powyższą lekcję za przy­dat­ną, mamy małą prośbę: pol­ub nasz fan­page. Dzię­ki temu będziesz zawsze na bieżą­co z nowy­mi treś­ci­a­mi na blogu ( i oczy­wiś­cie, z nowy­mi częś­ci­a­mi kur­su Javy). Dzięki!