Ich nehme derzeit an einer Akademie zum Java Fullstack Software Engineer teil. In den kommenden Wochen möchte ich hier meine Mitschrift, so gut es geht, aufzeichnen und mitteilen. Hier ist das, was ich vom zweiten und dritten Tag in Block 5 gelernt und behalten habe:
Beans
Dependenci Injection -Abhängig von deinem Leiden erhältst du eine Injektion
Inversion of Controll - Nicht das Programm, sondern der Aufrufer erhält die Kontrolle (In der Spring.xml wird über die Beans gesteuert)
Die Beans werden vom Classloader geladen. Man kann sie über AnnotationConfigApplicationContext aufrufen. Er sucht dann im angegebenen Package:
Wo kommt der Begriff "Beans" her?
- Beans tauchten zum ersten mal bei der GUI-Programmierung auf. Sie waren z.B. Knöpfe mit Eigenschaften, also Objekte mit einer graphischen Entsprechung
- Später gab es dann Enterprise Java Beans, die sind aber heute auch schon veraltet.
- Bean PoJo (Plain old Java Object). Jede Klasse die wir bauen ist ein PoJo. Bean und Pojo sind redundante Begriffe.
Aus Spring-Sicht sind Beans Objekte, die Spring kennt. Sollten neue Objekte hinzukommen, die nicht im Spring-Context liegen, kann man sie hinzufügen mit @Component:
Hier in dem Beispiel machen wir die Klasse MathServiceImpl zu einer Bean:
Hier werden die Beans über die Spring.xml eingeladen:
@Component bringt die Klassen in die entsprechenden Habitate
Unterschied Framework und BibliotheK:
Ein Framework ist komplizierter und hat laufende, aktive Prozesse. Eine Bibliothek bietet nur passive Prozesse, die man aufrufen muss.
Spring ist ein Framework. Es läuft aber auch in der Java Runtime Environment.
Übung ueb01-kreisrechner-spring:
Wir erstellen wieder einen Kreisrechner, mit einem Interface:
Zusätzlich erstellen wir einen Resourcen-Ordner und fügen dort eine spring.xml ein.
File/Settings/plugins/Spring&Java Tools könnte helfen, wir holen eine leere Vorlage aus einem anderen Projekt:
Leere spring.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
</beans>
So, bis jetzt machen wir kein Spring. Für die Nutzung brauchen wir die Klasspfad-Implementierung:
final ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"))
Das Objekt myLocalKreisRechner benutzt den Spring-Kontext um eine Bohne zu finden, die das Interface KreisRechner.class unterstützt. Wir instanzieren das Objekt in der App.java-Klasse:
Als nächstes erstellen wir eine Bohne im spring.xml. Wir nehmen die konkrete Klasse und nicht das Interface, da wir ja ein echtes Objekt instanziieren wollen:
<bean id="myKreisService" class="de.firma.util.BasicKreisRechner"/>
Somit läuft das Programm schon mal:
Jetzt suchen wir die Bohne nach ihrem Namen:
KreisRechner myLocalKreisRechner = (KreisRechner) springcontext.getBean("myKreisService"); //Lookup by ID / Name -> BasicKreisRechner
<bean id="myKreisService" class="de.firma.util.BasicKreisRechner"/>
Ich kann jetzt in der Konfigurationsdatei die Beans ändern und bin damit in Inversion of Controll:
In der App gehe ich über das Interface myRechner und danach über die Klasse:
App.java
public class App
{
public static void main( String[] args ) {
final ClassPathXmlApplicationContext springcontext = new ClassPathXmlApplicationContext("spring.xml");
// KreisRechner myLocalKreisRechner = new BasicKreisRechner(); // Klassischer Weg
// lookup by Interface -> KreisRechner
//KreisRechner myLocalKreisRechner = springcontext.getBean(KreisRechner.class); // lookup by Interface -> KreisRechner
// lookup by ID / NAME -> BasicKreisRechner
KreisRechner myLocalKreisRechner = (KreisRechner) springcontext.getBean("myKreisRechnerBasic"); // lookup by ID / NAME -> BasicKreisRechner
System.out.println( "Flaeche von 10 : " + myLocalKreisRechner.flaeche(10.0));
System.out.println( "Umfang von 10 : " + myLocalKreisRechner.umfang(10.0));
System.out.println("----------------------------------------------");
Rechner myRechner = springcontext.getBean(Rechner.class);
System.out.println( "Flaeche von 10 : " + myRechner.flaeche(10.0));
System.out.println( "Umfang von 10 : " + myRechner.umfang(10.0));
}
}
spring.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!--
<bean id="myKreisRechner" class="de.firma.util.BasicKreisRechner"/>
<bean id="myAngstKreisRechner" class="de.firma.util.AngstKreisRechner"/>
-->
<bean id="myKreisRechnerBasic" class="de.firma.util.BasicKreisRechner"/>
<bean id="myKreisRechnerAngst" class="de.firma.util.AngstKreisRechner"/>
<bean id="myRechner" class="de.firma.util.SimpleRechner" >
<constructor-arg ref="myKreisRechnerAngst"/>
</bean>
</beans>
Spring erzeugt eine Instanz vom Typ Kreisrechner:
Spring erzeugt ein Objekt "myKreisRechnerBasic" (roter Kringel) vom Typ BasicKreisrechner, welches das Interface Kreisrechner beinhaltet. Damit wird das Objekt zum Typ Kreisrechner.
Er registriert sich mit myKreisRechnerBasic, BasicKreisRechner, Kreisrechner in der Springcloudwolke.
Dann kommt die Laufzeitumgebung und sucht myKreisRechnerBasic in der Spring-Cloud.
MyRechner delegiert seine Berecnhung an myKreisRechnerAngst.
Rencher ist ein Interface und braucht eine konkrete Implementierung. SimpleRechner hat sich als Typ Rechner registriert, weil Simplerechner das Interface Rechner implementiert.
(Das war viel Stoff und recht kompliziert für einen Freitag. Ich denke im Beispiel unten (Ueb-kug-entity-dao) wird es evtl. noch mal übersichtlicher was Spring macht).
Tutorials:
- https://www.tutorialspoint.com/spring/index.htm
- https://www.javatpoint.com/spring-tutorial
- https://www.torsten-horn.de/techdocs/jee-spring.htm
Datenzugriff mit Spring
DAO - Data Access Object
Wir haben Daten/Dokumente. Diese wollen wir speichern, verändern,… Dasgeht über das Interface DAOIF. Dieses beinhaltet verschiedene Klassen, je nach Speicher-art.
Um das ganze Dynamisch zu konfigurieren, verwenden wir Spring:
Zur Erinnerung:
Das KUGBook ist eine Entity-Klasse, da sie Daten beinhaltet.
In Boundery ist eine Schnittstelle nach draußen (außerhalb des Java-Systems, IO-Datenaustausch)
Neues Projekt: Ueb-kug-entity-dao
Mit neuen Packages: de.firma.entity und de.firma.dao
Wir erstellen die Klasse KugBook im Entity Package und das Interface KugBookDAO im dao Package.
Der KugBookController sieht so aus:
Ein Bean ist immer eine konkrete Implementierung und kein Interface.
Wir wollen das Ganze aber jetzt flexibel halten und ändern in der Spring.xml und in der App und in KuGController folgendes:
Zuerst nehmen wir aus dem Controller den Springcontext raus (weil der Aufruf jetzt von Spring erfolgen soll und nicht von App.java):
Es geht auch über die Setter Methoden (eine der beiden unteren beans muss ich auskommentieren):
Jetzt sezten wir die Values in spring.xml
Spring + Datenbanken
Java-Datenbanken:
- https://db.apache.org/derby/ // Derby
- http://hsqldb.org/ // HSQLDB
- https://www.h2database.com/html/main.html // H2
Klassisch würden wir eine Datenbank so einbinden:
Hier mit Spring organisieren wir das aber über die spring.xml.
Zuerst brauchen wir einen Treiber. Den laden wir mit der pom.xml:
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.6.1</version>
</dependency>
Dann brauchen wir eine URL. Diese lautet in unserem Fall:
jdbc:hsqldb:file:kugdb
Username ist "sa" und passwort ist bei uns leer.
Die spring.xml sieht dann so aus:
Wir fügen in der spring.xml zwei Werte in die bean "dbStore" ein:
init-method="postConstruct"
destroy-method="preDestroy">
Wir fügen die zwei Methoden jeweils in KugBookDAO und KugBookFile ein:
Wir füllen den Inhalt der Konstruktoren wie folgt:
KugBookDAOFile.java
package de.firma.dao;import de.firma.entity.KugBook;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;public class KugBookDAOFile implements KugBookDAO{
// Eigenschaften
private String fileName;
List<KugBook> list;//Konstruktoren public KugBookDAOFile(String fileName) { this.fileName = fileName; } //Setter public void setFileName(String fileName) { this.fileName = fileName; } //LifeCycle-Methoden public void postConstruct() { System.out.println("KugBookDAOFile"); // Daten aus CSV-Datei einlesen this.list = new ArrayList<KugBook>(); String line; String fieldarray[] = new String[5]; try { // Puffer für die Datei einrichten BufferedReader br = new BufferedReader(new FileReader("data/"+this.fileName)); // Zeilenweise einlesen der Datei while ((line = br.readLine()) != null) { if (line.length() != 0) { // Spitten des CSV-Datensatzes beim Pipe-Zeichens fieldarray = line.split("\\|"); if (fieldarray.length == 5) { String sqlstmt; // Preis korrektur Dezimelkomma zum Dezimalpunkt fieldarray[3] = fieldarray[3].replace(',', '.'); this.list.add(new KugBook(fieldarray[4],fieldarray[0],fieldarray[1],Integer.parseInt(fieldarray[2]),Double.parseDouble(fieldarray[3]))); } } } } catch (IOException e) { e.printStackTrace(); } System.out.println(list.size()); } public void preDestroy(){ } // Business Methoden @Override public KugBook getById(String isbn) { return null; } @Override public List<KugBook> getall() { return null; } @Override public void save(KugBook kugBook) throws DAOException { } @Override public void update(KugBook kugBook) { } @Override public void delete(KugBook kugBook) { }}
KugBookDAODB.java
package de.firma.dao;import de.firma.entity.KugBook;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;public class KugBookDAODB implements KugBookDAO{
//Eigenschaften
private String driver;
private String url;
private String user;
private String pass;
List<KugBook> list;
//Konstruktorenpublic KugBookDAODB(String driver, String url, String user, String pass) { this.driver = driver; this.url = url; this.user = user; this.pass = pass; } //Setter public void setDriver(String driver) { this.driver = driver; } public void setUrl(String url) { this.url = url; } public void setUser(String user) { this.user = user; } public void setPass(String pass) { this.pass = pass; } //LifeCycle-Methodenpublic void postConstruct() {
this.list = new ArrayList<KugBook>();
System.out.println("KugBookDAODB");Connection con=null; try { // Versuch: Laden des Treibers Class.forName(this.driver); // Versuch: Aufbau der Verbindung zur Datenbank con = DriverManager.getConnection (this.url, this.user, this.pass); // SQL-Statement an die Datenbank senden // Datenbankcontainer erzeugen Statement stmt = con.createStatement(); // Datenbankcontainer ausfüren ResultSet rs = stmt.executeQuery ("SELECT * FROM kugtab"); while(rs.next()) { String titel = rs.getString("titel"); String autor = rs.getString("autor"); int jahr = rs.getInt("jahr"); float preis = rs.getFloat("preis"); String isbn = rs.getString("isbn"); this.list.add(new KugBook(isbn,titel,autor,jahr,preis)); } // Verbindung zur Datenbank trennen con.close (); } catch (SQLException e) { System.err.println (e); } catch (ClassNotFoundException e) { System.err.println (e); } System.out.println(list.size()); } public void preDestroy(){ } // Business Methoden @Override public KugBook getById(String isbn) { return null; } @Override public List<KugBook> getall() { return null; } @Override public void save(KugBook kugBook) throws DAOException { } @Override public void update(KugBook kugBook) { } @Override public void delete(KugBook kugBook) { }}
——————
Disclaimer
Alles was ich mitschrieb und verstanden habe ist ohne Gewähr.
Besten Dank an unseren sehr empfehlenswerten
Trainer: Hans-Joachim Blanke [email protected]
In den nächsten Tagen und Wochen geht’s weiter, so: stay tuned!
Gruß, Achim Mertens