Spring jednoduše 3. díl

Konfigurovat třídy tak, aby je Spring mohl použít pro dependency injection je možné též pomocí anotací.

@Component
Obecná anotace, ze které ostatní anotace dědí.

@Service
Speciální typ @Component. Tato anotace je určena pro třídu, která představuje službu (service)

@Repository
Speciální typ @Component. Tato anotace je určena pro třídu, která zprostředkovává přístup k datům.

@Controller
Speciální typ @Component. Tato anotace je používána v Spring-MVC

Pokud si nejste jistí, vždy můžete použít anotaci @Component.

V následujícím příkladu ukáži použití anotací. Zde je struktura projektu.

C:.
│   pom.xml
│
├───src
│   ├───main
│   │   └───java
│   │       └───cz
│   │           └───vitfo
│   │               └───spring02
│   │                   │   Class01.java
│   │                   │   Class03.java
│   │                   │   Main.java
│   │                   │
│   │                   └───class02
│   │                           Class02.java
│   │
│   ├───resources
            beans.xml

spring03

Soubor pom.xml zůstává stejný. Dále máme tři třídy s názvy Class01, Class02 a Class03, z toho Class02 je v podbalíčku s názvem class02.

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-2.5.xsd">

	<context:component-scan base-package="cz.vitfo.spring02" />

</beans>

Class01.java

import org.springframework.stereotype.Component;

@Component
public class Class01 {

	public void sayHello() {
		System.out.println("Hello from " + this.getClass().getSimpleName() + " class");
	}
}

Class02.java

import org.springframework.stereotype.Component;

@Component
public class Class02 {

	public void doSomething() {
		System.out.println("Metoda v " + this.getClass().getSimpleName());
	}
}

Class03.java

import org.springframework.stereotype.Component;

@Component
public class Class03 {

	public String getText() {
		return "Text z metody v " + this.getClass().getSimpleName();
	}
}

Main.java

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cz.vitfo.spring02.class02.Class02;

public class Main 
{
    public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
		
		Class01 cl01 = context.getBean(Class01.class);
		Class02 cl02 = context.getBean(Class02.class);
		Class03 cl03 = context.getBean(Class03.class);
		
		cl01.sayHello();
		cl02.doSomething();
		System.out.println(cl03.getText());
	}
}

Výsledek

Hello from Class01 class
Metoda v Class02
Text z metody v Class03

Spring projde package určený v konfiguračním souboru (beans.xml) a zaregistruje si všechny oanotované třídy (@Component). Jinak řečeno, component-scan říká Springu, aby prohledal všechny třídy v daném package (i v podbalíčcích jak je vidět u Class02) a zjistil, zda některá z nich neobsahuje anotaci @Component, @Service, @Repository či @Controller. Pokud takovou třídu najde, zaregistruje si ji (a pak může vytvářet na požádání její instance). Není tedy třeba pro každou třídu uvádět konfiguraci do souboru beans.xml.

Pokud byste anotaci neuvedli a třídu se snažili ze Spring kontextu získat, dostanete org.springframework.beans.factory.NoSuchBeanDefinitionException.

Zdroje:

Spring jednoduše 2. díl

V předchozím dílu jsme pro získání instance použili metodu context.getBean(„myClass“).

MyClass mc1 = (MyClass) context.getBean("myClass");

Získá instanci na základě id, které je definováno v souboru beans.xml. Metoda getBean(String name) vrací Object, takže je nutné přetypování. Existují ale i další možnosti.

MyClass mc2 = context.getBean(MyClass.class, "myClass");

Tato metoda je stejná jako předchozí, jen vrací instanci třídy zadané jako parametr a tím pádem není nutné provádět přetypování jako v předchozím případě.

MyClass mc3 = context.getBean(MyClass.class);

Získá instanci dané třídy. V tomto případě stačí mít v beans.xml pouze následující definici (bez id).

   <bean class="cz.vitfo.spring01.MyClass">
       <property name="message" value="Hello World!"/>
   </bean>

Vysvětlení pojmů

Kontejner je komponenta, která v sobě obsahuje jinou komponentu a poskytuje jí nějakou službu.

Spring kontejner je kontejner, který zodpovídá za všechny beany (instance), které vytváří, a které poskytuje aplikaci. Je také zodpovědný, že beany (instance) budou k dispozici ve správný čas na správném místě. Aby toto mohl provádět, potřebuje konfiguraci. V našem případě to konfigurace v souboru beans.xml, ale Spring umožňuje i jiné formy konfigurace.

Bean je třída, která obsahuje proměnné, gettery a settery. Je to forma POJO (Plain Old Java Object).

Hašování retězce pomocí různých algoritmů v Javě

Nejdříve vysvětlení pojmů.

Hašovací funkce je matematická funkce (resp. algoritmus) pro převod vstupních dat do (relativně) malého čísla. Výstup hašovací funkce se označuje výtah, miniatura, otisk, fingerprint či hash (česky též někdy jako haš).
Mezi hlavní vlastnosti této funkce patří:

  • jakékoliv množství vstupních dat poskytuje stejně dlouhý výstup (otisk)
  • malou změnou vstupních dat dosáhneme velké změny na výstupu (tj. výsledný otisk se od původního zásadně na první pohled liší)
  • z hashe je prakticky nemožné rekonstruovat původní text zprávy (což je rozdíl oproti klasickému šifrování)
  • v praxi je vysoce nepravděpodobné, že dvěma různým zprávám odpovídá stejný hash, jinými slovy pomocí hashe lze v praxi identifikovat právě jednu zprávu (ověřit její správnost).

Hašování v základní variantě dovoluje testovat vstupní data na shodu, tedy rovnost. Nezachovává podobnost dat ani uspořádání. wiki

Zdůrazňuji, že jakékoliv množství vstupních dat poskytuje stejně dlouhý výstup a malou změnou vstupních dat dosáhneme velké změny na výstupu.

Pro hashování v Javě existuje třída MessageDigest. Digest v překladu v tomto kontextu znamená suma, souhrn. V rámci standardní Javy jsou k dispozici hašovací algoritmy MD5, SHA-1, SHA-256.

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class App {

	public static void main(String[] args) {
		String message = "Hello World!";
		
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
			MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
			
			byte[] md5Digest = md5.digest(message.getBytes());
			byte[] sha1Digest = sha1.digest(message.getBytes());
			byte[] sha256Digest = sha256.digest(message.getBytes());
			
			System.out.println(formatResult(md5Digest));
			System.out.println(formatResult(sha1Digest));
			System.out.println(formatResult(sha256Digest));
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
	}
	
	private static String formatResult(byte[] bytes) {
		StringBuilder sb = new StringBuilder();
		sb.append("Délka pole: ");
		sb.append(bytes.length);
		sb.append("\t");
		sb.append("Pole: ");
		sb.append(Arrays.toString(bytes));
		return sb.toString();
	}
}

MessageDigest potřebuje na vstupu pole bytů (message.getBytes()) a vrátí také pole bytů, která má ale předem danou délku (je jedno jak dlouhý text hašujete) a představuje otisk původního pole.

Rozdíl výsledku při změně jediného znaku (z Hello World! na Hallo World!) pro všechny tři algoritmy.

Délka pole: 16	Pole: [-19, 7, 98, -121, 83, 46, -122, 54, 94, -124, 30, -110, -65, -59, 13, -116]
Délka pole: 16	Pole: [98, 119, 56, -33, 57, 107, 57, -72, -69, -54, 87, 79, 122, 72, -72, 50]

Délka pole: 20	Pole: [46, -9, -67, -26, 8, -50, 84, 4, -23, 125, 95, 4, 47, -107, -8, -97, 28, 35, 40, 113]
Délka pole: 20	Pole: [65, 63, 96, -127, -42, 94, -26, 2, -115, 124, -43, -81, -110, 32, 39, 12, -125, 121, 61, -5]

Délka pole: 32	Pole: [127, -125, -79, 101, 127, -15, -4, 83, -71, 45, -63, -127, 72, -95, -42, 93, -4, 45, 75, 31, -93, -42, 119, 40, 74, -35, -46, 0, 18, 109, -112, 105]
Délka pole: 32	Pole: [85, 65, 51, 18, -114, -77, 16, 91, -101, -83, 102, 27, -115, 61, 17, -117, -53, -39, -47, 86, -115, -46, -5, 112, 8, 127, -119, 4, 27, 14, 3, -80]

Je vidět, že drobná změna vede k velmi rozdílnému výsledku každého ze zkoušený hašovacích algoritmů.

Zásobník v Javě

Třída Stack (zásobník) je datová stuktura, která funguje na principu LIFO (Last In First Out). Jedná se o kolekci objektů, kdy poslední vložený objekt je vybírán jako první (a první vložený jako poslední).

Vytvoříme si zásobník obsahující čtyři objekty.

Stack<Integer> stack = new Stack<Integer>();
stack.push(5);
stack.push(7);
stack.push(2);
stack.push(7);

Jedná se o kolekci, takže je možné ji procházet.

for (Integer i : stack) {
	System.out.println(i);
}

Pro přidání objektu na konec zásobníku se používá metoda push(). Pro získání posledně přidaného objektu se používají metody peek() a pop().

peek()

System.out.println(stack.peek());
System.out.println(stack.peek());

Výsledek (pořád vrací poslení objekt)

7
7
7
7
7

pop()

System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());

Výsledek

7
2
7
5
Exception in thread "main" java.util.EmptyStackException

Čtvrté volání metody pop() vrátí poslední objekt (první přidaný) a zásobník je prázdný. Pokud zavoláme pop() ještě jednou (na prázdném zásobníku) dostaneme chybu EmptyStackException.

Spring jednoduše 1. díl

Spring je obrovský ekosystém pokrývající rozsáhlou oblast programování. Základní funkcionalitou frameworku je Inversion of Control, což jednoduše řečeno znamená, že objekty, které potřebují další objekty, si tyto objekty nevytváří pomocí klíčového slova new sami, ale přenechávají tuto činnost Springu (Spring kontejneru). Ten je pak zodpovědný za vytváření objektů (za celý životní cyklus objektu od vzniku po zánik). Důvod pro tuto činnost je ten, že díky Springu nejsou komponenty těsně se sebou svázány (a je např. jednodušší vyměnit jednu implementaci za druhou).

Zde je struktura jednoduchého Maven projektu, používajícího Spring.

spring01
│   .classpath
│   .project
│   pom.xml
│
├───.settings
│       org.eclipse.core.resources.prefs
│       org.eclipse.jdt.core.prefs
│       org.eclipse.m2e.core.prefs
│
└───src
    ├───main
    │   └───java
    │       └───cz
    │           └───vitfo
    │               └───spring01
    │                       Main.java
    │                       MyClass.java
    │
    ├───resources
    │       beans.xml
    │
    └───test

Pokud odmažu soubory a složky, které nejsou důležité, vypadá projekt následovně.

spring01
│   pom.xml
│
└───src
    ├───main
    │   └───java
    │       └───cz
    │           └───vitfo
    │               └───spring01
    │                       Main.java
    │                       MyClass.java
    │
    └───resources
            beans.xml

Skládá se z souboru pom.xml, který slouží Mavenu a jsou v něm definovány závislosti na knihovách na Springu.

&<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>cz.vito</groupId>
	<artifactId>spring01</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>spring01</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.2.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.2.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>4.2.3.RELEASE</version>
		</dependency>
	</dependencies>
</project>e

Druhým nezbytným souborem je beans.xml (na názvu nezáleží, ale je zvykem ho pojmenovávat beans.xml). Tento soubor slouží jako konfigurace Springu (Spring kontejneru) a je v něm definováno, jak a jaké objekty má Spring vytvářet. V tomto příkladu je definována jediná beana s názvem (id) ‚myClass‘, která bude instancí třídy cz.vitfo.spring01.MyClass.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="myClass" class="cz.vitfo.spring01.MyClass">
       <property name="message" value="Hello World!"/>
   </bean>

</beans>

Nakonec zde ještě máme třídu MyClass

package cz.vitfo.spring01;

public class MyClass {

	private String message;

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}

a třídu s metodou main, která slouží jako vstupní bod programu.

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

package cz.vitfo.spring01;

public class Main 
{
    public static void main(String[] args) {
    	// beans.xml musí být na class path
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
		
		MyClass mc = (MyClass) context.getBean("myClass");
		
		System.out.println(mc.getMessage());
	}
}

Pokud program spustíte (run ve třídě s metodou main), dostanete následující výpis do konzole.

Čvc 01, 2016 3:48:51 ODP. org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1f17ae12: startup date [Fri Jul 01 15:48:51 CEST 2016]; root of context hierarchy
Čvc 01, 2016 3:48:51 ODP. org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [beans.xml]
Hello World!
Picked up JAVA_TOOL_OPTIONS: -Djava.vendor="Sun Microsystems Inc."

Pro nás nejdůležitější je řádek obsahující ‚Hello World!‘. Došlo ke spuštění programu a výpisu proměnné message Springem vytvořeného objektu mc.

Pokud jste dostali následující chybový výpis

Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [beans.xml]; nested exception is java.io.FileNotFoundException: class path resource [beans.xml] cannot be opened because it does not exist

ujistěte se, že soubor beans.xml skutečně existuje, a že je umístěn na classpath (např. v package src/resources).

V případě této chyby

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'myClass' is defined

se ujistěte, že v beans.xml máte nadefinovánu beanu s id myClass a nedopustili jste se nějakého překlepu.