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 vierten Tag in Block 5 gelernt und behalten habe:
(Zur Wiederholung:) Abstrakte Klassen sind wie Interfaces, können aber auch Methoden beinhalten. Diese werden mit dem Wort "astract" eingeleitet. Dadurch wird die ganze Klasse abstrakt.
Verschiedene Bean-Typen
ParentBean
MatheserviceA darf max. bis 100 rechnen, Matheservice B darf max. bis 10 rechnen. In der spring.xml gibt es damit quasi auch eine Vererbung:


Verschiedene Konfigurationsdateien
Wir haben 2 spring-Dateien:

Wir binden beide Spring-Dateien ein. Letztendlich sind sie wie eine große Datei:

Parent Context
Die Springdateien können auch ineinander verschachtelt sein:

Simple Argument Types
Die Datentypen, die hier übergeben werden sind primitiv:

ListTypes:
Die Übergabeparameter in den Beans sind vom Typ "List":

ConstructorInjection
Wir haben keine spring.xml-Datei mehr. Wir laden immer noch den Springcontext in den Klassloader. Der Klassloader soll die Klassen melden, wo eine @Component Annotation dran steht.


Regelwerk von Stereotypen:

Es gibt noch 4 andere Stereotypen (@Controller, @Indexed, @Repository, @Service) als @Component. Wir erinnern uns (im Zusammenhang mit Habitaten):

Die Annotations definieren alle eine Bean, die aber jeweils in unterschiedlichen Kontexten liegen.
Bei der ConstructorInjection gibt es kein "new", keine Instanzen. Die Beans melden sich am Classloader über ihre Annotationen an.
In der App wird nur der Spring-Context in den Classloader geladen. Mathservice ist die Klasse, die geladen wird:

Man kann bei den Annotationen auch Namen (ID-Register) mitgeben:

Es gibt noch andere Annotations, die brauchen wir aber im Moment nicht:
Siehe auch: http://www.cheat-sheets.org/saved-copy/javaEE6ReferenceSheet.pdf. Hier ein Auszug:

Methodeninjection
Die App ist quasi gleich geblieben:

Es gibt aber keinen Konstruktor mehr.
Über @Autowired werden die Services Sumservice und DiffService der Spring-Wolke zur Verfügung gestellt:

Da die Reflection Api außerhalb des eigentlichen Java-Heap-Speichers arbeitet, können auch private Variablen ausgelesen werden. Damit ist @autowire mächtig, aber auch gefährlich. Hier im Beispiel gibt es weder Konstruktoren, noch getter und setter Methoden:

Die Application kann auch zur Componente erklärt werden, die sich über autowired in Spring indizieren lässt:

Übergabe einer Resource:

Man kann auch die Annotations mit der spring.xml mixen. In der spring.xml wird über context:annotation-config, bzw. context:component-scan base-package darauf hingewiesen, dass es Componenten auch im Java Code gibt.

Übung
ueb02-temeraturrechner-spring
Erstelle folgendes:
Schnittstellen:
- Celsius2Fahrenkeit
- Fahrenheit2Celsius2
- TemperaturRechner
Technik (Spring):
- Statische oder Dynamische Kopplung (ICO)
- Injetion (DI)
Application:
*) Integration von Log4J (ohne Security-Problemen) -Logging auf Info-Level
- Logging der Berechnungs-Aufrufe --> Klasse, Methode, Parameter und Rückgabewerte
**) Logging des Spring-Frameworks auf Debug-Level
Hier das funktionierende Ergebnis:
Im Kommentar ist eine Bemerkung falsch. Die Spring-Objekte werden nicht in der Reflection-Api sondern auch, wie der andere Java-Code, im Heap-Speicher geladen.

app.java:
package de.firma;import de.firma.interfaces.TemperaturConvertable;
import org.springframework.context.support.ClassPathXmlApplicationContext;/**
Hello world!
*/
public class App {
public static void main(String[] args) {
System.out.println("Temperaturconverter");
try (final ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("spring.xml")) { //hier wird die Springwelt erschaffen.
// Die spring.xml wird ausgelesen und in die Spring-Welt (Regestry) eingelesen. Objekte werden in der Reflection-API erstellt.
final TemperaturConvertable temperaturConvertable = ctx.getBean(TemperaturConvertable.class); //Hier wird aus der Spring-CLoud die Bohne TemperaturConvertable geholt
System.out.println(temperaturConvertable.celsius2Fahrenkeit(10));
System.out.println(temperaturConvertable.fahrenheit2Celsius(50));}
}
}
TemperaturConverterImpl.java:
package de.firma.beans;import de.firma.interfaces.C2FConvertable;
import de.firma.interfaces.F2CConvertable;
import de.firma.interfaces.TemperaturConvertable;public class TemperaturConverterImpl implements TemperaturConvertable {
private C2FConvertable c2f;
private F2CConvertable f2c;public TemperaturConverterImpl(C2FConvertable c2f, F2CConvertable f2c) { //Spring wird hier 2 Beans übergeben, die dafür sorgen, dass zwei Objekte im Classloader gebaut werden. this.c2f = c2f; this.f2c = f2c; } @Override public double celsius2Fahrenkeit(double celsius) { return c2f.celsius2Fahrenkeit(celsius); } @Override public double fahrenheit2Celsius(double fahrenheit) { return f2c.fahrenheit2Celsius(fahrenheit); }
}
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="C2FBean" class="de.firma.beans.C2FImpl"/> <bean id="F2CBean" class="de.firma.beans.F2CImpl"/> <bean id="C2FBean2" class="de.firma.beans.C2FImpl2"/> <bean id="TemperaturConverterBean" class="de.firma.beans.TemperaturConverterImpl"> <constructor-arg ref="C2FBean"/> <!-- <constructor-arg ref="C2FBean2"/> Wenn gewünscht, dann diese Methode --> <constructor-arg ref="F2CBean"/> </bean>
</beans>
TemperaturConvertable.java:
package de.firma.interfaces;public interface TemperaturConvertable extends C2FConvertable, F2CConvertable {
}
F2CConvertable.java
package de.firma.interfaces;public interface C2FConvertable {
public double celsius2Fahrenkeit (double celsius);
}
F2CImpl.java:
package de.firma.beans;import de.firma.interfaces.F2CConvertable;
public class F2CImpl implements F2CConvertable {
@Override
public double fahrenheit2Celsius(double fahrenheit) {
return (fahrenheit -32)*5/9;
}
}
Aktivierung von Logging:
In der app.java hinzufügen:
private static final Logger logger = LogManager.getLogger(App.class);logger.warn("Spring-Context wurde erfolgreich erstellt");

Log4j2.xml:
<?xml version="1.0" encoding="UTF-8"?> <!-- https://howtodoinjava.com/log4j2/ https://howtodoinjava.com/log4j2/multiple-appenders/ --> <Configuration status="WARN"> <Properties> <Property name="LOG_DIR">./logs/</Property> </Properties> <Appenders> <Console name="console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %c %logger{36} - %msg%n"/> </Console> <RollingFile name="SizeRollingFile" fileName="${LOG_DIR}/application.log" filePattern="${LOG_DIR}/application.%i.log.gz" ignoreExceptions="false"> <PatternLayout> <Pattern>%d{yyyy-MM-dd HH:mm:ss} %c %-5p %m%n</Pattern> </PatternLayout> <!-- //Warning level up to Error to file, redundant, can be done only with loggers --> <LevelRangeFilter minLevel="FATAL" maxLevel="WARN"/> <Policies> <SizeBasedTriggeringPolicy size="10MB" /> </Policies> <DefaultRolloverStrategy max="5" /> </RollingFile> </Appenders> <Loggers> <Logger level="warn" name="de.firma" additivity="true" > <AppenderRef ref="SizeRollingFile" /> </Logger> <!-- If not further specified, log as error --> <Root level="debug"> <AppenderRef ref="console"/> </Root> </Loggers> </Configuration>
pom.xml:
<?xml version="1.0" encoding="UTF-8"?><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>de.firma</groupId>
<artifactId>ueb02-temperaturrechner-spring</artifactId>
<version>1.0-SNAPSHOT</version><name>ueb02-temperaturrechner-spring</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url><properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties><dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.14</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.1</version>
</dependency></dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Den Logger kann man auch anders einbinden:

Die spring.xml sieht dann so aus:

——————
Disclaimer
Alles was ich mitschrieb und verstanden habe ist ohne Gewähr.
Besten Dank an unseren sehr empfehlenswerten
Trainer: Hans-Joachim Blanke blanke@4point.de

In den nächsten Tagen und Wochen geht’s weiter, so: stay tuned!
Gruß, Achim Mertens