PA165/Cvičení Spring: Porovnání verzí

Z FI WIKI
Přejít na: navigace, hledání
(Konfigurace Spring)
(Inversion of Control Container)
Řádka 229: Řádka 229:
 
</java>
 
</java>
  
Spusťte aplikaci. (Klikněte pravým tlačítkem, vyberte v pop-up menu ''Run Main.main()'').
+
Spusťte aplikaci.  
  
 
Je vidět, že použití anotací šetří psaní konfigurace, ale explicitní konfigurace v XML umožňuje specifikovat inicializaci i složitých hodnot typu Lista a Map.
 
Je vidět, že použití anotací šetří psaní konfigurace, ale explicitní konfigurace v XML umožňuje specifikovat inicializaci i složitých hodnot typu Lista a Map.
  
Anotace [http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-autowired-annotation @Autowired] způsobuje,
+
Anotace [http://static.springsource.org/spring/docs/current/reference/beans.html#beans-autowired-annotation @Autowired] způsobuje,
že Spring se pokouší najít spring-bean vhodného ''typu''. Pokud jich existuje víc, dojde k chybě. V tom případě je třeba použít anotaci [http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-resource-annotation @Resource], která způsobuje hledání vhodného spring-beanu pomocí ''jména'', odvozeného buď od názvu set metody nebo specifikovaného parametrem @Resource(name="").
+
že Spring se pokouší najít spring-bean vhodného ''typu''. Pokud jich existuje víc, dojde k chybě. V tom případě je třeba použít anotaci [http://static.springsource.org/spring/docs/current/reference/beans.html#beans-resource-annotation @Resource], která způsobuje hledání vhodného spring-beanu pomocí ''jména'', odvozeného buď od názvu set metody nebo specifikovaného parametrem @Resource(name="").
  
 
=== Transakční zpracování ===
 
=== Transakční zpracování ===

Verze z 22. 10. 2012, 15:19


V tomto cvičení si ukážeme práci s frameworkem Spring [1].

Vyzkoušíme si taky vývojové prostředí Intellij IDEA, které má skvělou podporu pro Spring.

Nastavení prostředí

module add jdk-1.7.0_03
module add netbeans-7.2
module add maven-3.0.4
netbeans

Vytvoření projektu

Zvolte v menu File - New Project. V okně vyberte ve levém sloupci Categories položku Maven a v pravém sloupci Projects položku Java Application. V dalším okně změňte položku Package na cz.muni.fi.pa165.spring a klikněte na Finish.


Tím je projekt vytvořen. Přepněte se na záložku Files a do souboru pom.xml dopište závislost projektu na knihovnách Spring, a že má Maven projekt zabalit do jednoho JARu:

 
     <properties>
        <org.springframework.version>3.1.2.RELEASE</org.springframework.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <!--
        JDBC Data Access Library (depends on spring-core, spring-beans, spring-context, spring-tx, spring-aop)
            -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
    </dependencies>
    
        <build>
        <plugins>
            <!-- zabalit vsechno do jednoho spustitelneho JARu -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>1.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>cz.muni.fi.pa165.spring.App</mainClass>
                                </transformer>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.handlers</resource>
                                </transformer>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.schemas</resource>
                                </transformer>
 
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

Konfigurace Spring

Vytvořte adresář src/main/resources a v něm soubor spring-context.xml. (tj. vlevo v okně struktury projektu najděte adresář src/main, klikněte na něj pravým tlačítkem myši a vyberte z menu příslušné položky)

Do spring-context.xml napište:

 
<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"
>
    <context:component-scan base-package="cz.muni.fi.pa165.spring"/>
    <context:annotation-config/>
    
    
</beans>


Zeditujte třídu třídu App, přidejte do její metodu main následující kód:

 
    public static void main(String[] args) {
        ApplicationContext springCtx = new ClassPathXmlApplicationContext("spring-context.xml");
        
    }

Tím je aplikace připravena na použití Springu.

Inversion of Control Container

Hlavní část Springu je tzv. Inversion-of-Control container, dokumentace viz Chapter 3. The IoC container. Jeho hlavní vlastností je Dependency injection, což znamená, že instance tříd neshánějí samy instance tříd, na kterých závisejí, ale pouze poskytují konstruktor nebo set metody pro jejich nastavení.

Instance tříd, tzv. spring beans, lze specifikovat dvojím způsobem. Buď v XML nebo pomocí anotací. Předvedeme si obojí.

Vytvořte v balíku cz.muni.fi.pa165.spring třídu Fazole s anotací @Component a závislostí na třídě Zrnko tj.

 
package cz.muni.fi.pa165.spring;
 
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
 
@Component("fazole")
public class Fazole {
 
    private Zrnko zrnko;
 
    @Autowired
    public void setZrnko(Zrnko a) {
        System.out.println("setZrnko(" + a + ")");
        this.zrnko = a;
    }
}

a vytvořte ve stejném balíku třídu Zrnko bez anotací:

 
package cz.muni.fi.pa165.spring;
 
import java.util.List;
import java.util.Map;
 
public class Zrnko {
 
    private String neco;
    private List<String> seznam;
    private Map<String,Integer> mapa;
    private Fazole fazole;
 
    public void setNeco(String neco) {
        this.neco = neco;
    }
 
    public void setSeznam(List<String> seznam) {
        this.seznam = seznam;
    }
 
    public void setFazole(Fazole fazole) {
        this.fazole = fazole;
    }
 
    public void setMapa(Map<String, Integer> mapa) {
        this.mapa = mapa;
    }
 
    @Override
    public String toString() {
        return "Zrnko{" +
                "neco='" + neco + '\'' +
                ", seznam=" + seznam +
                ", mapa=" + mapa +
                ", fazole=" + fazole +
                '}';
    }
}
 

a ve spring-context.xml doplňte:

 
    <bean id="zrnko" class="cz.muni.fi.pa165.spring.Zrnko">
        <property name="neco" value="z1"/>
        <property name="fazole" ref="fazole"/>
        <property name="seznam">
            <list>
                <value>prvni</value>
                <value>druhy</value>
                <value>3</value>
            </list>
        </property>
        <property name="mapa">
            <map>
                <entry key="k1" value="1"/>
                <entry key="k2" value="234"/>
            </map>
        </property>
    </bean>

Do metody main pak doplňte získání instancí obou tříd, tj:

 
   public static void main(String[] args) {
        ApplicationContext springCtx = new ClassPathXmlApplicationContext("spring-context.xml");
 
        Fazole f = springCtx.getBean("fazole",Fazole.class);
        System.out.println("f = " + f);
 
        Zrnko z = (Zrnko) springCtx.getBean("zrnko");
        System.out.println("z = " + z);
    }

Spusťte aplikaci.

Je vidět, že použití anotací šetří psaní konfigurace, ale explicitní konfigurace v XML umožňuje specifikovat inicializaci i složitých hodnot typu Lista a Map.

Anotace @Autowired způsobuje, že Spring se pokouší najít spring-bean vhodného typu. Pokud jich existuje víc, dojde k chybě. V tom případě je třeba použít anotaci @Resource, která způsobuje hledání vhodného spring-beanu pomocí jména, odvozeného buď od názvu set metody nebo specifikovaného parametrem @Resource(name="").

Transakční zpracování

Spring velmi usnadňuje práci s databázovými transakcemi.

Práce s JDBC ve Springu je popsána v hesle Spring JDBC a v Spring ve webových aplikacích, proto ji zde nebudu opakovat.

Předvedeme si klasický případ transakce, kdy je třeba přepsat peníze z účtu na účet v bance, a nesmí se přečerpat účet. tedy je nutné provést atomicky tři operace

  • zkontrolovat zůstatek na zdrojovém účtu
  • snížit částku na zdrojovém účtu
  • zvýšit částku na cílovém účtu

Vytvořte tedy interface Banka:

 
package cz.muni.fi.pa165.spring;
 
import java.util.List;
import java.util.Map;
 
public interface Banka {
 
    int zustatekNaUctu(String cisloUctu);
 
    boolean prevodPenez(int castka, String ucet1, String ucet2);
 
    List<Map<String, Object>> zustatky();
}

a jeho implementaci BankaImpl:

 
package cz.muni.fi.pa165.spring;
 
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
 
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
 
@Component("banka")
public class BankaImpl implements Banka {
 
    JdbcTemplate jdbc;
 
    @Resource
    public void setDataSource(DataSource dataSource) {
        jdbc = new JdbcTemplate(dataSource);
    }
 
 
    public int zustatekNaUctu(String cisloUctu) {
        return jdbc.queryForInt("select zustatek from ucty where cislo=?", cisloUctu);
    }
 
    @Transactional
    public boolean prevodPenez(int castka, String ucet1, String ucet2) {
        int zustatek = zustatekNaUctu(ucet1);
        if (zustatek < castka) return false;
        jdbc.update("update ucty set zustatek = zustatek - ? where cislo=?", castka, ucet1);
        jdbc.update("update ucty set zustatek = zustatek + ? where cislo=?", castka, ucet2);
        return true;
    }
 
 
    public List<Map<String, Object>> zustatky() {
        return jdbc.queryForList("select  cislo,zustatek from ucty");
    }
}

Spojení na databázi

Do spring-context.xml je třeba doplnit toto:

 
    <tx:annotation-driven/>
 
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.postgresql.Driver"/>
        <property name="url" value="jdbc:postgresql://acrab.ics.muni.cz:5432/banka"/>
        <property name="username" value="pokus"/>
        <property name="password" value="pokus"/>
    </bean>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
    </bean>

a do pom.xml závislost na PostgreSQL JDBC ovladači:

 
        <dependency>
            <groupId>postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>8.4-701.jdbc4</version>
        </dependency>

a do Main.java zavolání převodu peněz:

 
        Banka banka = (Banka) springCtx.getBean("banka");
        System.out.println("pred "+ banka.zustatky());
        try {
            banka.prevodPenez(1000,"111-22222","333-44444");
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
        System.out.println("po   "+ banka.zustatky());

Spusťte aplikaci, převod proběhne. Pak dopiště v BankaImpl vyhození výjimky mezi změny částek na účtech:

 
        simple.update("update ucty set zustatek = zustatek - ? where cislo=?",castka,ucet1);
 
        //umele preruseni prevodu
        if(true) throw new RuntimeException("problem");
 
        simple.update("update ucty set zustatek = zustatek + ? where cislo=?",castka,ucet2);

a vyzkoušejte.

Dokumentace je v kapitole Chapter 10. Transaction management dokumentace ke Springu.

AOP - Aspect Oriented Programming

AOP čili Aspektově Orientované Programování je programovací paradigma (styl programování), které umožňuje postihnout tzv. cross-cutting concerns, tj. záležitosti jdoucí napříč normálním rozdělením programu na části.

Při AOP programátor definuje tzv. aspekty, kde aspekt zahrnuje jednak kusy kódu, tzv. advice, a přípojné body v programu, tzv. pointcuts, kde budou kusy kódu vykonány.

Podrobnosti implementace AOP ve Springu jsou popsány v kapitole Chapter 7. Aspect Oriented Programming with Spring, uveďme si zde jednoduchý příklad.

Zavedeme aspekt, který bude reagovat na spuštění libovolné metody na interface Banka, a změří dobu vykonávání metody.

Do spring-context.xml dopište aktivaci vyhledávání aspektů:

 
  <aop:aspectj-autoproxy/>

a do pom.xml dopište závislost na AspectJ:

 
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.6.8</version>
        </dependency>

Vytvořte novou třídu AOPDemo s metodou profile(), která bude měřit dobu volání libovolné metody na interfacu Banka:

 
package cz.muni.fi.pa165.spring;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
 
 
@Aspect
@Component("profiler")
public class AOPDemo {
    
    @Around("execution(* cz.muni.fi.pa165.spring.Banka.*(..))")
    public Object profile(ProceedingJoinPoint call) throws Throwable {
        StopWatch clock = new StopWatch("Profilovani " + call.toShortString());
        try {
            clock.start(call.toShortString());
            return call.proceed();
        } finally {
            clock.stop();
            System.out.println(clock.prettyPrint());
        }
    }
}

Spusťte program a prohlédněte si výstup. Bude obsahovat měřění doby vykonávání metod:

StopWatch 'Profilovani execution(Banka.zustatky())': running time (millis) = 67
-----------------------------------------
ms     %     Task name
-----------------------------------------
00067  100%  execution(Banka.zustatky())