Code Monkey home page Code Monkey logo

20170420-szkolenie's Introduction

Spring Framework - ćwiczenia

Ten projekt zawiera przykłady (ćwiczenia oraz rozwiązania ćwiczeń) do szkolenia "Tworzenia aplikacji internetowych z wykorzystaniem Spring Framework"

Ćwiczenia

Celem zadań będzie napisanie aplikacji która pobiera tłumaczenia słów wpisanych z konsoli oraz wyświetla je na ekranie. Wraz z rozwojem aplikacji (i poznawaniem Spring Framework) będziemy w kolejnych ćwiczeniach dodawać kolejne funkcjonalności.

To jest bazowa wersja programu; aplikacja zawiera podstawową konfigurację aplikacji w Springu oraz prosty kontroler umożliwiający podawanie komand z linii poleceń.

Zadanie 1 – serwis tłumaczący

Dodaj serwis pobierający tłumaczenia angielskiego słowa z serwisu http://www.dict.pl dostępny pod komendą: search [słowo]. Możesz bezpośrednio wykorzystać klasę TranslationService, którą należy odpowiednio rozbudowac o adnotację Spring Framework. Metoda TranslationService#getTranslationsForWord()) zwraca listę obiektów DictionaryWord (tupli) zawierających parę: słowo polskie, słowo angielskie.

Tłumaczenie mogą zostać wyświetlone na ekranie.

Zadanie 2 – testy jednostkowe

Napisany przez nas w ćwiczeniu 1 serwis należałoby przetestować, najlepiej w sposób automatyczny. Bazując na istniejącym ExampleConfigurationTests napisz nowy test dla serwisu.

Wiedząc o tym co mówi konwencja Spring Framework nt. plików konfiguracyjnych dla testów, utwórz nową konfigurację XML dla testu albo wykorzystaj wewnętrzną klasę JavaConfig.

Zadanie 2a

Test korzystający bezpośrednio z klasy TranslationService nie jest najlepszy – odwołuje się do zasobów w Internecie, przez co (w przypadku braku dostępu do Internetu nie zadziała – nie będzie działał stabilnie). Co więcej, ze względu na wykonywanie połączenia ze zdalnym serwerem, test działa nieporównywalnie dłużej niż jakby wywoływał się w całości lokalnie (albo w całości w pamięci).

Aby uniknąć takiego typu zachowania, należałoby zastąpić bezpośrednie odwołanie do Internetu, np. uniezależniając serwis TranslationService od konkretnego adresu URL (i przekazując go jako parametr, zmienną klasy – poprzez adnotację @Value).

W Spring Framework najłatwiej uzyskać to poprzez dodanie zewnętrznego pliku properties, zdefiniowanie adresu URL, a następnie odwołanie się do niego w konfiguracjach XML albo JavaConfig

<context:property-placeholder location="classpath:META-INF/spring/dict.properties"/>
@Configuration
@ComponentScan(value = { "com.example.dictionary", "com.example.helloworld" })
@PropertySource("META-INF/spring/dict.properties")
public static class AppConfiguration {

	@Bean
	public PropertySourcesPlaceholderConfigurer properties() {
		return new PropertySourcesPlaceholderConfigurer();
	}

}

Do lokalnego pliku można dostać się bezpośrednio poprzez ClassPath this.getClass().getResource("/words/book.html").toExternalForm();

Zadanie 3 – walidacja parametrów

Do serwisu TranslationService dodaj metodą sprawdzającą poprawność przekazanych parametrów. Dla serwisu wyszukującego poprawne komenda to: search [słowo do znalezienia]

Do walidacji wykorzystaj standard Bean Validation (walidacja przez adnotacje). W tym celu dodaj do projektu następujące zależności.

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>5.2.4.Final</version>
</dependency>
Tip
walidacja bazuje na obiektach (innych niż String) więc na początek zmień parametry (komendy) na obiekt. Dodatkowo, przeprowadź refaktoryzację klasy Command i TranslationService aby logikę przetwarzania parametrów wejściowych zawierać w jednym miejscu.

Na przykład, wprowadź klasę CommandParameters o następującej strukturze, używaj jej do przekazywania parametrów CLI oraz na jej bazie przeprowadź walidację.

public class CommandParameters {
	private String commandName;
	private String[] attributes;
}

Zadanie 4 – audit logs

Odwołanie się do aplikacji zewnętrznej (w naszym przypadku do serwisu http://dict.pl) jest niezmierni ważne z biznesowego punktu widzenia. Wyobraźmy sobie sytuację że to nie jest zwykły słownik a zaawansowany Web Service, za którego wywołania pobierana jest opłata. Co miesiąc przychodzi faktura od dostawcy za wszystkie wywołania serwisu.

Aby zweryfikować taką fakturę, należy upewnić się ile razy serwis był wywoływany.

Jako że nie jest to wymaganie typowe dla naszego serwisu (można powiedzieć że jest to typowe wymaganie nie funkcjonalne) zaimplementuj je w sposób nie zmieniający głównego algorytmu działania serwisu TranslationService

Do implementacji logowania użyj komponentów Spring AOP

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
</dependency>

Zaloguj wywołanie publicznej metody serwisu, zaloguj informację o wywołaniu wraz z przekazywanym parametrem.

Zadanie 5 – zapisywanie wyników

Dotychczas zupełnie pomijaliśmy kwestie zwracanych wyników – wyświetlaliśmy je na ekranie. Pora to zmienić.

Zacznijmy od wyświetlenia wszystkich słów na ekranie, w formie listy, a następnie wprowadźmy komendę zapisującą konkretne tłumaczenia: save [numer wyników z listy]

Zapisane tłumaczenie póki co przechowujmy w dowolnym miejscu w systemie (np. jako listy w kontrolerze).

Tip
aby poprawnie obsługiwać listy należy przechować wyniki wyszukiwania oraz osobno listę zapisanych słów. Co powoduje konieczność implementacji kolejnych komend poza save: show-saved oraz show-found

Zadanie 5a - repozytorium

„Pamięć” zaimplementowana w poprzednim ćwiczeniu jest rozwiązaniem stosunkowo naiwnym. Wszystkie dane przechowujemy w kontrolerze co czyni go grubym (antywzorzec Fat Controller). Aby to naprawić utwórzmy dodatkowy komponent obsługujący przechowywanie danych.

public interface Repository {

	public List<DictionaryWord> getSavedWords();

	public void addWord(DictionaryWord word);

	public void printSavedWords();
}

Zadanie 6 – zapis słów do bazy danych

Nasze repozytorium przechowuje dane w pamięci. Nic nie stoi na przeszkodzie abyśmy zaczęli zapisywać je do bazy danych. W tym celu utwórzmy tabelę words

create table words (
	id int identity primary key,
	polish_word varchar(100),
	english_word varchar(100)
);

Do zapisywania danych użyjmy klasy JdbcTemplate

Tip
Konfiguracja bazy danych wymaga dodania sterownika HSQLDB, MySQL lub PostreSQL.
<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <version>2.3.2</version>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.26</version>
</dependency>
<dependency>
	<groupId>postgresql</groupId>
	<artifactId>postgresql</artifactId>
	<version>9.1-901.jdbc3</version>
</dependency>

Dodatkowo należy dodać bibliotekę Spring odpowiedzialną za połączenie za bazą danych.

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jdbc</artifactId>
</dependency>

Przydatne informację dot. połączenia

driver = com.mysql.jdbc.Driver
url = "jdbc:hsqldb:file:/tmp/testdb" //(1)
url = "jdbc:hsqldb:mem:testmemdb" //(2)
  1. Baza w pliku

  2. Baza w pamięci

Zadanie 6a – wykorzystanie mappera obiektowego

Pobierając dane z bazy danych możemy skorzystać z kolejnych dobrodziejstw Spring Framework, klasy RowMapper, umożliwiającej automatyczne mapowanie kolejnych elementów ResultSet na obiekt (w naszym przypadku DictionaryWord).

Zadanie 7 – wykorzystanie JPA

Bardzo często spotykanym sposobem połączenia z bazą danych jest wykorzystanie standardu JPA. W przypadku naszej prostej aplikacji jest to niezwykle proste, wszakże posługujemy się już obiektem domenowym DictionaryWord który moglibyśmy zapisać bezpośrednio w bazie danych.

Zmodyfikuj klasę DictionaryWord oraz dodaj nowe repozytorium JpaRepository – w ten sposób korzystając z JPA do zapisu danych do bazy

Przydatne biblioteki do dołączenia do projektu:

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-orm</artifactId>
</dependency>
<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-entitymanager</artifactId>
	<version>5.2.6.Final</version>
</dependency>

<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
	<version>1.6.4</version>
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-log4j12</artifactId>
	<version>1.6.4</version>
</dependency>

Zadanie 8 – transakcje

Nowym wymaganiem w naszym systemie, jest zapisywanie tłumaczeń także do pliku. Przed zapisem do bazy danych należy parę słów zapisać także do pliku o losowej nazwie. Dopiero w kolejnym kroku można zapisać dane w bazie. Jeżeli transakcja w bazie danych się nie powiedzie, należy usunąć uprzedni utworzony plik.

Plik może znajdować się w katalogu tymczasowym a jego nazwa może być dowolnie generowana, np.:

public String createFile(String data) {
    UUID id = UUID.randomUUID();
    String filename = "/tmp/wordfile-" + id.toString();

    try (PrintWriter out = new PrintWriter(filename)) {
        out.println(data);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

    log.info("Saved file: "  + filename);
    return filename;
}

Wykorzystując klasy Spring TransactionSynchronisationManager oraz interfejs TransactionSynchronisation zaimplementuj poprawną obsługę transakcji i błędów.

Zadanie 9 - Spring WebMVC

Kolejnym krokiem będzie rozbudowa aplikacji o cześć serwerową - dodanie usługi www, umożliwiającej wykonanie tych samych operacji poprzez webservice REST.

Webservice ma udostępniać następujące metody

GET /show-saved 		(1)
GET /search/{word}		(2)
POST /search/{word}/{n}		(3)
  1. Wyświetlenie wszystkich zapisanych słóœ

  2. Wyświetlenie tłumaczeń dla słowa {word}

  3. Zapisanie wybranego tłumaczenia słow {word}, będącego {n}-tym elementem listy

Aby ułatwić sobie pracę, wykorzystajmy już istniejącą aplikację (projekt Spring). W tym celu najlepiej utworzyć nowy projekt zawierający następujące zależności

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
		http://maven.apache.org/maven-v4_0_0.xsd"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example</groupId>
	<artifactId>web</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>war</packaging>

	<properties>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
		<failOnMissingWebXml>false</failOnMissingWebXml>
	</properties>

	<dependencyManagement>
	    <dependencies>
	        <dependency>
	            <groupId>io.spring.platform</groupId>
	            <artifactId>platform-bom</artifactId>
	            <version>2.0.0.RELEASE</version>
	            <type>pom</type>
	            <scope>import</scope>
	        </dependency>
	    </dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
		    <groupId>com.example</groupId>
		    <artifactId>app</artifactId>		(1)
		    <version>1.0-SNAPSHOT</version>
		</dependency>

		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-webmvc</artifactId>
		</dependency>
		<dependency>
		    <groupId>com.fasterxml.jackson.core</groupId>
		    <artifactId>jackson-core</artifactId>
		    <version>2.5.0</version>
		</dependency>
		<dependency>
		    <groupId>com.fasterxml.jackson.core</groupId>
		    <artifactId>jackson-databind</artifactId>
		    <version>2.5.0</version>
		</dependency>

		<dependency>
		    <groupId>javax.servlet</groupId>
		    <artifactId>javax.servlet-api</artifactId>
		    <version>3.0.1</version>
		    <scope>provided</scope>
		</dependency>
	</dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
            </plugin>
        </plugins>
    </build>
</project>
  1. Zależność od bazowego projektu

Samą aplikację możemy skonfigurować zarówno poprzez plik web.xml jak i poprzez adnotacje i JavaConfig

DispatcherConfig.java
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.web")
public class DispatcherConfig {

}
WebInitializer.java
public class WebInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) {
        // Create the 'root' Spring application context
        AnnotationConfigWebApplicationContext rootContext =
                new AnnotationConfigWebApplicationContext();
        rootContext.register(AppJavaConfig.AppConfiguration.class);

        // Manage the lifecycle of the root application context
        container.addListener(new ContextLoaderListener(rootContext));

        // Create the dispatcher servlet's Spring application context
        AnnotationConfigWebApplicationContext dispatcherContext =
                new AnnotationConfigWebApplicationContext();
        dispatcherContext.register(DispatcherConfig.class);

        // Register and map the dispatcher servlet
        ServletRegistration.Dynamic dispatcher =
                container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/*");
    }

}

Do pełni działającej aplikacji potrzeba już jedynie odpowiedniej konfiguracji kontrolerów.

20170420-szkolenie's People

Contributors

kubamarchwicki avatar mzimecki avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.