[JAVA] Wichtige Änderungen in der Kernfunktionalität von Spring Framework 5.0

Dies ist der zweite Teil der Reihe "Wichtige Änderungen an Spring Framework 5.0" und die wichtigsten Änderungen der Kernfunktionen (neue Funktionen, Verbesserungen usw.). Ich möchte vorstellen.

Serie

Version zur Überprüfung des Betriebs

Änderungen in der Kernfunktionalität

In Spring Framework 5.0 wurden die folgenden Änderungen an allgemeinen Funktionen (= Kernfunktionen) vorgenommen, die nicht vom Anwendungstyp abhängen.

Artikelnummer Änderungen
1 Mit den von JDK 8 unterstützten Mechanismen können Sie effizient auf Methodenparameter zugreifen.[Zu Details:arrow_right:]

**Note:**Da es sich um eine interne Implementierung handelt, ändern sich die externen Spezifikationen nicht.
2 Einige Schnittstellen implementieren jetzt die von JDK 8 unterstützten Standardmethoden.[Zu Details:arrow_right:]

**Note:**BeimErstelleneinerImplementierungsklasse(Erweiterungsklasse) für diese Schnittstellen müssen nur die erforderlichen Methoden implementiert werden. Daher gibt es Maßnahmen wie "Erstellen einer Adapterklasse, die eine leere Implementierung bereitstellt" und "unnötiges Ausführen einer leeren Implementierung". Es wird unnötig sein.
3 Unterstützt in JDK 7StandardCharsetsWird verwendet.[Zu Details:arrow_right:]

**Note:**Da es sich um eine interne Implementierung handelt, ändern sich die externen Spezifikationen nicht.
4 Wird in JDK 9 veraltet seinClass#newInstance()AnstattConstructor#newInstance()Wird aufgerufen, um eine Instanz zu erstellen.[Zu Details:arrow_right:]

**Note:**Da es sich um eine interne Implementierung handelt, ändern sich die externen Spezifikationen nicht.
5 spring-jclModule wurden hinzugefügt und Log4j 2 über die Commons Logging API.x, SLF4J, JUL(java.util.logging)Sie können das Protokoll über ausgeben.[Zu Details:arrow_right:]

Note: Spring Framework 4.Die Bibliothek für Log Bridge, die bis zu 3 benötigt wurde, wird nicht mehr benötigt.
6 Schnittstelle zum Abstrahieren von Ressourcen(Resource)ZuisFile()メソッドが追加され、リソースがファイルシステム上Zu存在するか判定できるようZuなります。[Zu Details:arrow_right:]
7 Schnittstelle zum Abstrahieren von Ressourcen(Resource)ZureadableChannel()Methode hinzugefügt,ReadableByteChannel(New I/O)経由でリソースのデータを読み込むことができるようZuなります。[Zu Details:arrow_right:]

Auf Methodenparameter kann über die in JDK 8 hinzugefügte API zugegriffen werden: thumbsup:

[SPR-14055]: Ab JDK 8 sind die Klassen "java.lang.reflect.Method" und "java.lang.reflect.Constructor" Es erbt jetzt die von JDK 8 hinzugefügte Klasse "java.lang.reflect.Executable", und Spring Framework 5.0 verwendet jetzt die Methoden der Klasse "Executable", um auf Informationen zu Methoden und Konstruktorparametern zuzugreifen. wurde. Da die diesmal geänderte Klasse im Grunde genommen eine Klasse ist, die bei der internen Verarbeitung des Frameworks verwendet wird, wird sie meiner Meinung nach nicht oft direkt von Anwendungsentwicklern verwendet, sondern beim Erstellen von AP-Infrastrukturteilen wie Framework-Erweiterungen. Es kann eine Möglichkeit geben.

Zum Beispiel ...

package com.example;

import org.springframework.beans.factory.annotation.Value;

public class Foo {
	public Foo(String text, int number) {
		// ...
	}
	public String bar(@Value("${text:dummy}") String text) {
		return text;
	}
}

Schauen wir uns den Code an, der auf die Argumente des Konstruktors und der Methode dieser Klasse zugreift.

Verwenden Sie in Spring Framework 4.3 die Methode forMethodOrConstructor der Klasse MethodParameter, um die Parameterinformationen abzurufen. (Veraltete API in Spring Framework 5.0)

〜4.3


package com.example;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.core.MethodParameter;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

class CoreTest {

	@Test
	void constructorParameter() throws Exception {
		Constructor<?> constructor = Foo.class.getConstructor(String.class, int.class);
		MethodParameter parameter0 = MethodParameter.forMethodOrConstructor(constructor, 0);
		MethodParameter parameter1 = MethodParameter.forMethodOrConstructor(constructor, 1);

		Assertions.assertEquals(String.class, parameter0.getParameterType());
		Assertions.assertEquals(int.class, parameter1.getParameterType());
	}

	@Test
	void methodParameter() throws Exception {
		Method method = Foo.class.getMethod("bar", String.class);
		MethodParameter parameter0 = MethodParameter.forMethodOrConstructor(method, 0);

		Assertions.assertEquals(String.class, parameter0.getParameterType());
		Assertions.assertEquals("${test:dummy}", parameter0.getParameterAnnotation(Value.class).value());
	}

}

Verwenden Sie in Spring Framework 5.0 die Methode "forExecutable" oder "forParameter" der Klasse "MethodParameter", um Parameterinformationen abzurufen.

5.0〜


package com.example;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.core.MethodParameter;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

class CoreTest {

	@Test
	void constructorParameter() throws Exception {
		Constructor<?> constructor = Foo.class.getConstructor(String.class, int.class);
		MethodParameter parameter0 = MethodParameter.forExecutable(constructor, 0);
		MethodParameter parameter1 = MethodParameter.forExecutable(constructor, 1);

		Assertions.assertEquals(String.class, parameter0.getParameterType());
		Assertions.assertEquals(int.class, parameter1.getParameterType());
	}

	@Test
	void methodParameter() throws Exception {
		Method method = Foo.class.getMethod("bar", String.class);
		MethodParameter parameter0 = MethodParameter.forParameter(method.getParameters()[0]);

		Assertions.assertEquals(String.class, parameter0.getParameterType());
		Assertions.assertEquals("${text:dummy}", parameter0.getParameterAnnotation(Value.class).value());
	}

}

Note:

Übrigens ... Wenn Sie den tatsächlichen Parameternamen erhalten möchten, müssen Sie "DefaultParameterNameDiscoverer" auf "MethodParameter" setzen. (Wenn Sie diesen Mechanismus verwenden möchten, müssen Sie " -parameters "oder" -g` "als Kompilierungsoption angeben.)

MethodParameter parameter0 = MethodParameter.forParameter(method.getParameters()[0]);
parameter0.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
// ...
Assertions.assertEquals("text", parameter0.getParameterName());

#In der Schnittstelle implementierte Standardmethode:thumbsup:

[SPR-14432]:EinigedervomSpringFrameworkbereitgestelltenSchnittstellenimplementierenjetztdievonJDK8unterstütztenStandardmethoden.MitdieserÄnderungmüssenbeimErstelleneinerImplementierungsklasse(Erweiterungsklasse) für diese Schnittstellen nur die erforderlichen Methoden implementiert werden. "Erstellen Sie eine Adapterklasse, die eine leere Implementierung bereitstellt" oder "Führen Sie eine leere Implementierung unnötig aus". Es besteht keine Notwendigkeit, Maßnahmen wie.

Zum Beispiel Frühling-Wird vom Beans-Modul bereitgestelltBeanPostProcessorAuf der SchnittstellepostProcessBeforeInitialization(Eine Methode, die vor der ersten Verarbeitung der Bean zurückgerufen wird)WannpostProcessAfterInitialization(Eine Methode, die nach der ersten Verarbeitung von Bnea zurückgerufen wird)Wannいう2つのメソッドがありますが、そちらか一方だけ実装したいWannいうケースもあります。

Spring Framework 4.In 3 musste ich zwei Methoden unbedingt implementieren:

〜4.3


@Configuration
public class AppConfig {
	@Bean
	BeanPostProcessor myBeanPostProcessor() {
		return new BeanPostProcessor() {
			@Override
			public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 // Beispiel) Implementieren Sie Code, der den Bean-Status ändert, bevor der Initialisierungsprozess ausgeführt wird.
				return bean;
			}

			@Override
			public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
 return bean; // Benötige eine Vorlagenimplementierung, die die Bean so zurückgibt, wie sie ist ...
			}
		};
	}
}

Spring Framework 5.Bei 0 müssen Sie nur die erforderlichen Methoden wie unten gezeigt implementieren.

5.0〜


@Configuration
public class AppConfig {
	@Bean
	BeanPostProcessor myBeanPostProcessor() {
		return new BeanPostProcessor() {
			@Override
			public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 // Beispiel) Implementieren Sie Code, der den Bean-Status ändert, bevor der Initialisierungsprozess ausgeführt wird.
				return bean;
			}
		};
	}
}

#Standard-Zeichensätze werden für die Verarbeitung innerhalb des Frameworks verwendet:thumbsup:

[SPR-14492]:InderinternenVerarbeitungdesFrameworks"UTF-Standardwie8"CharsetBeiderAngabe"Charset#forName(String)Unterstützt von JDK 7 stattStandardCharsetsWird verwendet. Dies hat keine Auswirkungen auf Framework-Benutzer.StandardCharsets"UNS-ASCII」「ISO-8859-1」「UTF-8」「UTF-16BE」「UTF-16LE」「UTF-Es gibt Konstanten für "16", also lasst uns alle diese Konstanten aktiv verwenden!

Constructor#newInstance()Wird generiert mit:thumbsup:

[SPR-14486]:InderinternenVerarbeitungdesFrameworksNichtempfohlenfürJDK9WirdseinClass#newInstance()AnstattConstructor#newInstance()Wirdaufgerufen,umeineInstanzzuerstellen.Anscheinend...Class#newInstance()Wirdverwendet,umgeprüfteAusnahmenzubehandeln,wenneinegeprüfteAusnahmemiteinerDeklarationinderthrows-KlauseldesKonstruktorsauftritt.(FügenSiediecatch-oderthrow-Klauselhinzu)Wird nicht erzwungen, so scheint es, dass die aufgetretene Ausnahme unbeabsichtigt nach oben geworfen wird. Dies ist auch eine Geschichte, die die Framework-Benutzer überhaupt nicht betrifft, aber es scheint Fälle zu geben, in denen Sie Ihre eigene Instanziierung mithilfe von Reflektion implementieren. Lassen Sie uns also sehen, wo das Problem liegt. ..

Es ist nicht wirklich möglich, aber es wird vom Standardkonstruktor erzwungenIOExceptionBereiten Sie eine Klasse vor, die generiert.

public class Foo {
	public Foo () throws  IOException {
		throw new IOException("error.");
	}
	// ...
}

Wenn Sie eine Instanz der obigen Klasse erstellen,new Foor()Wenn, der CompilerIOExceptionSie werden gebeten, damit umzugehen.

new


@Test
void newInstance() {
	try {
		new Foo();
		Assertions.fail("does not occurred a IOException.");
 } catch (IOException e) {// ★★★ Der Compiler erzwingt die Behandlung von IOException! !!
		// NOP
	}
}

NächsterClass#newInstanceWenn Sie eine Instanz mit erstellenInstantiationExceptionWannIllegalAccessExceptionのハンドリングを行うように求めてきます。 この状態でテストケースを実行するWann・・・

Class.newInstance


@Test
void classNewInstance() {
	try {
		Foo.class.newInstance();
		Assertions.fail("does not occurred a IOException.");
	} catch (InstantiationException | IllegalAccessException e) {
		// NOP
	}
}
java.io.IOException: error.

	at com.example.CoreTest$Foo.<init>(CoreTest.java:97)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at java.lang.Class.newInstance(Class.java:442)
	at com.example.CoreTest.classNewInstance(CoreTest.java:75)

Und der Test schlägt fehl. Mit anderen Worten ...InstantiationExceptionWannIllegalAccessExceptionTrat im Konstruktor auch nach der Handhabung aufIOExceptionWird nicht behandelt und eine Ausnahme wird unbeabsichtigt an den Anrufer gesendet.

DannClass#newInstancenichtConstructor#newInstance()Mal sehen, was passiert, wenn Sie verwenden. Wenn Sie den folgenden Test ausführen,InvocationTargetExceptionTritt ein,InvocationTargetExceptionAusnahme, die im Konstruktor aufgetreten ist(IOException)Ist eingewickelt.

Constructor.newInstance


@Test
void constructorNewInstance() {
	try {
		Foo.class.getDeclaredConstructor().newInstance();
		Assertions.fail("does not occurred a IOException.");
 	} catch (NoSuchMethodException | InstantiationException | IllegalAccessException e) {
		Assertions.fail("does not occurred a IOException.");
 } catch (InvocationTargetException e) {// ★★★ Der Compiler zwingt Sie, die Ausnahme zu behandeln, die die im Konstruktor aufgetretene Ausnahme (IOException) umschließt: sweat_smile! !!
		Assertions.assertEquals(IOException.class, e.getTargetException().getClass());
	}
}

spring-jcl Modul wird hinzugefügt:thumbsup:

[SPR-14512]:spring-Einjcl-ModulwurdehinzugefügtundLog4j2überdieCommonsLoggingAPI,ohne"OriginalCommonsLogging"oder"BridgeLibrary,diedieCommonsLoggingAPIimplementiert"zuverwenden..x,SLF4J,JUL(java.util.logging)Sie können das Protokoll über ausgeben. Das Spring Framework verwendet die Commons Logging API für die Protokollausgabe, und die Bibliothek für die tatsächliche Protokollausgabe entspricht dem vom Entwickler gewählten Stil. Vor langer Zeit "Commons Logging"+Protokollbibliothek(Log4j usw.)War üblich, aber vor kurzem "SLF4J"+Protokollligator, der die SLF4J-API implementiert(Zum Beispiel Logback)Wird (wahrscheinlich) mehr Mainstream:sweat_smile:)。 Zum Beispiel ... "SLF4J+In dem Fall, in dem "Logback" das Anwendungsprotokoll ausgibt, ist die vom Spring Framework ausgegebene Protokollbibliothek eine Bridge-Bibliothek, die die "Commons Logging API" implementiert.(jcl-over-slf4j etc.) + SLF4J +Es sollte als "Logback" konfiguriert sein.

Mal sehen, wie die abhängigen Bibliotheken für die obige Konfiguration aufgelöst werden.

Spring Framework 4.In 3 Frühling-Da der Kern von "Commons Logging of the head family" abhängt, sollten Sie bei Verwendung der Bridge-Bibliothek den Frühling einstellen-Ich musste "Original Commons Logging" aus den abhängigen Bibliotheken des Kerns ausschließen.

xml:pom.xml(〜4.3)


<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>4.3.8.RELEASE</version>
      <exclusions>
        <exclusion>
          <groupId>commons-logging</groupId>
 <artifactId> commons-logging </ifactId> <! - ★★★ Schließen Sie die ursprüngliche Commons Logging-> aus
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>
</dependencyManagement>
<!-- ... -->
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.3</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
 <artifactId> jcl-over-slf4j </ifactId> <! - ★★★ "Bridge-Bibliothek, die die Commons Logging API implementiert" ist erforderlich->
  <version>1.7.25</version>
</dependency>

Spring Framework 5.Springen Sie ab 0 den Teil der "Bridge-Bibliothek, die die Commons Logging API implementiert".-Weil jcl verantwortlich ist, jcl-over-Verwenden der Commons Logging API "Log 4j 2", ohne eine Bridge-Bibliothek wie slf4j hinzuzufügen.x」「SLF4J」「JUL(java.util.logging)Sie können das Protokoll über ausgeben. Frühlingsrahmen 5.Ab 0 Frühling-Kern ist Frühling statt "Commons Logging der Kopffamilie"-Frühling, weil es von jcl abhängt-Sie müssen lediglich die von jcl unterstützte Protokollbibliothek zu den abhängigen Bibliotheken hinzufügen.

xml:pom.xml(5.0〜)


<!-- ... -->
<dependency>
  <groupId>ch.qos.logback</groupId>
 <artifactId> logback-classic </ifactId> <! - ★★★ Sie müssen nur Logback hinzufügen, das die SLF4J-API-> implementiert
  <version>1.2.3</version>
</dependency>

Note:

Wenn die zum Erstellen der Anwendung verwendeten Bibliotheken von "Original Commons Logging" oder "Bridge Library, die die Commons Logging API implementiert" abhängen, müssen Sie diese Bibliotheken ausschließen.

Wenn ich versuche, den Code auszuführen, der den DI-Container von Spring generiert ...

@Test
void applicationContext() {
	try (ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class)) {
		// ...
	}
}

Das folgende Protokoll wurde ordnungsgemäß ausgegeben:v:

01:55:50.072 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [systemProperties] PropertySource with lowest search precedence
01:55:50.079 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [systemEnvironment] PropertySource with lowest search precedence
01:55:50.080 [main] DEBUG org.springframework.core.env.StandardEnvironment - Initialized StandardEnvironment with PropertySources [systemProperties,systemEnvironment]
01:55:50.129 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning
01:55:50.179 [main] INFO org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@46daef40: startup date [Fri May 12 01:55:50 JST 2017]; root of context hierarchy
 ... (weggelassen)
01:55:50.786 [main] INFO org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@46daef40: startup date [Fri May 12 01:55:50 JST 2017]; root of context hierarchy
01:55:50.786 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
01:55:50.787 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@482cd91f: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,coreTest.AppConfig,myBeanPostProcessor]; root of factory hierarchy

ResourceKann festgestellt werden, ob es sich im Dateisystem befindet:thumbsup:

[SPR-14484]:SchnittstellezumAbstrahierenvonRessourcen(Resource)ZuisFile()メソッドが追加され、リソースがファイルシステム上Zu存在するか判定できるようZuなります。

@Test
void resourceIsFile() throws IOException {
	Resource fileResource = new FileSystemResource("pom.xml");
	Resource webResource = new UrlResource("http://google.com");

	Assertions.assertTrue(fileResource.isFile());
	Assertions.assertFalse(webResource.isFile());
}

ResourceDatenReadableByteChannelKann über erhalten werden:thumbsup:

[SPR-14698]:SchnittstellezumAbstrahierenvonRessourcen(Resource)ZureadableChannel()Methodehinzugefügt,ReadableByteChannel(NewI/O)経由でリソースのデータを読み込むことができるようZuなります。ちなみZu・・・RC1WritableResourceZuwritableChannelMethode hinzugefügt,WritableByteChannel経由でデータを書き込めるようZuなっていたので、あわせて実装サンプルを紹介しておきます。

@Test
void resourceUsingByteBuffer() throws IOException {
	ByteBuffer buffer = ByteBuffer.allocate(2048);
	Resource srcResource = new FileSystemResource("pom.xml");
	WritableResource destResource = new FileSystemResource("pom.xml.bk");
	try (ReadableByteChannel readableChannel = srcResource.readableChannel();
			WritableByteChannel writableChannel = destResource.writableChannel()) {
		while (true) {
			buffer.clear();
			if (readableChannel.read(buffer) <= 0) {
				break;
			}
			buffer.flip();
			writableChannel.write(buffer);
		}
	}

	Assertions.assertEquals(srcResource.contentLength(), destResource.contentLength());
}

#Zusammenfassung

Dieses Mal haben wir die wichtigsten Änderungen in den Kernfunktionen eingeführt. Es gab auch eine Einführung von Klassenänderungen, die nicht direkt bei der Entwicklung von Anwendungen verwendet werden, die das Framework verwenden. Es wurden jedoch Verbesserungen vorgenommen, um die "Benutzerfreundlichkeit" und "Effizienz" des Frameworks zu verbessern. Es ist ein Eindruck. nächstes Mal,"Wichtige Änderungen in Bezug auf DI-ContainerWerden vorgestellt.

Recommended Posts

Wichtige Änderungen in der Kernfunktionalität von Spring Framework 5.0
Wichtige Änderungen in Spring Boot 1.5
Spring Framework 5.0 Zusammenfassung der wichtigsten Änderungen
Wichtige Änderungen im Zusammenhang mit Spring Framework 5.0 Test
Wichtige Änderungen in Bezug auf Spring Framework 5.0 Web MVC
Wichtige Änderungen im Zusammenhang mit dem Spring Framework 5.0 DI-Container
Änderungen des Kernstandorts in iOS 14
Änderungen in Mockito 2
Änderungen in mybatis-spring-boot-Starter 2.0
Änderungen im Mybatis-Spring-Boot-Starter 2.1
Änderungen im Mybatis-Spring-Boot-Starter 1.3
Änderungen in Java 11
RESTful API-Beispiel für mehrere Module mit IntelliJ + Jersey + Spring Framework
Injizieren Sie den Logger im Frühjahr
[Spring Framework] Konfigurationsaufteilung
Mehrsprachige Unterstützung für Spring Framework
1. Starten Sie Spring Framework von 1
Spring Framework Zusammenfassung - Über DI
Verwenden Sie Interceptor im Frühjahr
Änderungen in JUnit5M4-> M5
Holen Sie sich Cookies im Frühling
Erstellen Sie die Pivotal TC Server Developer Edition in Spring Framework (STS).
Beispiel für eine minimale RESTful-API-Konfiguration mit Jersey + Spring Framework
Änderungen im Mybatis-Spring-Boot-Starter 2.1
Änderungen im Mybatis-Spring-Boot-Starter 1.3
Änderungen im Mybatis-Spring-Boot-Starter 1.2
Änderungen in Mockito 2
Änderungen in Java 11
Änderungen in JUnit5M4-> M5
Änderungen des Kernstandorts in iOS 14
Wichtige Änderungen in Spring Boot 1.5
Java-Versionsnotation, die sich in Java 10 ändert
Wichtige Änderungen in der Kernfunktionalität von Spring Framework 5.0