[JAVA] Changements majeurs dans la fonctionnalité de base de Spring Framework 5.0

Ceci est le deuxième volet de la série «Spring Framework 5.0 Major Changes», et les principaux changements dans les fonctions de base (nouvelles fonctions, améliorations, etc.) Je voudrais présenter.

séries

Version de vérification de fonctionnement

Changements dans les fonctionnalités de base

Dans Spring Framework 5.0, les modifications suivantes ont été apportées aux fonctions générales (= fonctions principales) qui ne dépendent pas du type d'application.

Numéro d'article Changements
1 Vous pourrez accéder efficacement aux paramètres de méthode en utilisant les mécanismes pris en charge par JDK 8.[Vers les détails:arrow_right:]

**Note:**Puisqu'il s'agit d'une implémentation interne, les spécifications externes ne changent pas.
2 Certaines interfaces implémenteront désormais les méthodes par défaut prises en charge par JDK 8.[Vers les détails:arrow_right:]

**Note:**Lorsdelacréationd'uneclassed'implémentation(classed'extension) pour ces interfaces, il est seulement nécessaire d'implémenter les méthodes nécessaires, il existe donc des mesures telles que «créer une classe Adapter qui fournit une implémentation vide» et «effectuer inutilement une implémentation vide». Ce ne sera pas nécessaire.
3 Pris en charge dans JDK 7StandardCharsetsSera utilisé.[Vers les détails:arrow_right:]

**Note:**Puisqu'il s'agit d'une implémentation interne, les spécifications externes ne changent pas.
4 Sera obsolète dans JDK 9Class#newInstance()Au lieu deConstructor#newInstance()Sera appelé pour créer une instance.[Vers les détails:arrow_right:]

**Note:**Puisqu'il s'agit d'une implémentation interne, les spécifications externes ne changent pas.
5 spring-jclDes modules ont été ajoutés et Log4j 2 via l'API Commons Logging.x, SLF4J, JUL(java.util.logging)Vous pourrez sortir le journal via.[Vers les détails:arrow_right:]

Note: Spring Framework 4.La bibliothèque pour le pont de journaux, qui était requise jusqu'à 3, n'est plus nécessaire.
6 Interface pour extraire les ressources(ResourceisFile()メソッドが追加され、リソースがファイルシステム上À存在するか判定できるようÀなります。[Vers les détails:arrow_right:]
7 Interface pour extraire les ressources(ResourcereadableChannel()Méthode ajoutée,ReadableByteChannel(New I/O)経由でリソースのデータを読み込むことができるようÀなります。[Vers les détails:arrow_right:]

Les paramètres de méthode sont accessibles via l'API ajoutée dans JDK 8: thumbsup:

[SPR-14055]: À partir du JDK 8, les classes java.lang.reflect.Method et java.lang.reflect.Constructor sont Il hérite désormais de la classe java.lang.reflect.Executable ajoutée à partir de JDK 8, et Spring Framework 5.0 utilise désormais les méthodes de la classe ʻExecutable` pour accéder aux informations de méthode et de paramètre du constructeur. devenu. La classe modifiée cette fois est essentiellement une classe utilisée dans le traitement interne du framework, donc je pense qu'elle n'est pas souvent utilisée directement par les développeurs d'applications, mais elle est utilisée lors de la création de parties d'infrastructure AP telles que les extensions de framework. Il peut y avoir une possibilité.

Par exemple ...

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;
	}
}

Jetons un coup d'œil au code qui accède aux arguments du constructeur et de la méthode de cette classe.

Dans Spring Framework 4.3, utilisez la méthode forMethodOrConstructor de la classe MethodParameter pour obtenir les informations de paramètre. (API obsolète dans 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());
	}

}

Dans Spring Framework 5.0, utilisez la méthode forExecutable ou forParameter de la classe MethodParameter pour obtenir des informations sur les paramètres.

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:

Au fait ... Si vous voulez obtenir le nom réel du paramètre, vous devez définir DefaultParameterNameDiscoverer sur MethodParameter. (Si vous souhaitez utiliser ce mécanisme, vous devez spécifier " -parameters "ou" -g` "comme option de compilation.)

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

#Méthode par défaut implémentée dans l'interface:thumbsup:

[SPR-14432]:CertainesdesinterfacesfourniesparSpringFrameworkimplémenterontdésormaislesméthodespardéfautprisesenchargeparJDK8.Aveccechangement,lorsdelacréationd'uneclassed'implémentation(classed'extension) pour ces interfaces, seules les méthodes nécessaires doivent être implémentées, donc «Créer une classe Adapter qui fournit une implémentation vide» ou «Effectuer inutilement une implémentation vide». Il n'est pas nécessaire de prendre des mesures telles que.

Par exemple, printemps-Fourni par le module beansBeanPostProcessorSur l'interfacepostProcessBeforeInitialization(Une méthode qui est rappelée avant d'effectuer le traitement initial du bean)QuandpostProcessAfterInitialization(Une méthode qui est rappelée après avoir effectué le traitement initial de Bnea)Quandいう2つのメソッドがありますが、そちらか一方だけ実装したいQuandいうケースもあります。

Spring Framework 4.En 3, j'ai dû implémenter deux méthodes sans faute:

〜4.3


@Configuration
public class AppConfig {
	@Bean
	BeanPostProcessor myBeanPostProcessor() {
		return new BeanPostProcessor() {
			@Override
			public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 // Exemple) Implémentez du code qui modifie l'état du bean avant que le processus d'initialisation ne soit effectué.
				return bean;
			}

			@Override
			public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
 return bean; // Besoin d'une implémentation de modèle qui retourne le bean tel quel ...
			}
		};
	}
}

Spring Framework 5.À 0, il vous suffit de mettre en œuvre les méthodes requises comme indiqué ci-dessous.

5.0〜


@Configuration
public class AppConfig {
	@Bean
	BeanPostProcessor myBeanPostProcessor() {
		return new BeanPostProcessor() {
			@Override
			public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 // Exemple) Implémentez du code qui modifie l'état du bean avant que le processus d'initialisation ne soit effectué.
				return bean;
			}
		};
	}
}

#Les jeux de caractères standard sont utilisés pour le traitement dans le cadre:thumbsup:

[SPR-14492]:Dansletraitementinterneduframework,"UTF-Standardtelque8"CharsetLorsdelaspécification,"Charset#forName(String)Pris en charge par JDK 7 au lieu deStandardCharsetsSera utilisé. Cela n'a aucun effet sur les utilisateurs du framework,StandardCharsets"NOUS-ASCII」「ISO-8859-1」「UTF-8」「UTF-16BE」「UTF-16LE」「UTF-Il existe des constantes pour «16», alors utilisons tous activement ces constantes!

Constructor#newInstance()Est généré en utilisant:thumbsup:

[SPR-14486]:DansletraitementinterneduframeworkNonrecommandépourJDK9SeraClass#newInstance()AulieudeConstructor#newInstance()Seraappelépourcréeruneinstance.Apparemment...Class#newInstance()Permetdegérerlesexceptionsvérifiéeslorsqu'uneexceptionvérifiéeavecunedéclarationdanslaclausethrowsduconstructeurseproduit.(Ajouteruneclausecatchouthrows)N'est pas appliquée, il semble donc que l'exception qui s'est produite est involontairement renvoyée au sommet. C'est aussi une histoire qui n'affecte pas du tout les utilisateurs du framework, mais il semble qu'il y ait des cas où vous implémentez votre propre instanciation en utilisant la réflexion, alors voyons quel est le problème. ..

Ce n'est pas vraiment possible, mais c'est forcé par le constructeur par défautIOExceptionPréparez une classe qui génère.

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

Lors de la création d'une instance de la classe ci-dessus,new Foor()Si, le compilateurIOExceptionIl vous sera demandé de gérer.

new


@Test
void newInstance() {
	try {
		new Foo();
		Assertions.fail("does not occurred a IOException.");
 } catch (IOException e) {// ★★★ Le compilateur force la gestion de IOException! !!
		// NOP
	}
}

prochainClass#newInstanceLorsque vous créez une instance en utilisantInstantiationExceptionQuandIllegalAccessExceptionのハンドリングを行うように求めてきます。 この状態でテストケースを実行するQuand・・・

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)

Et le test échoue. En d'autres termes ...InstantiationExceptionQuandIllegalAccessExceptionSe produit dans le constructeur même après la manipulationIOExceptionN'est pas gérée et une exception est lancée involontairement à l'appelant.

PuisClass#newInstancene pasConstructor#newInstance()Voyons ce qui se passe lorsque vous utilisez. Si vous exécutez le test ci-dessous,InvocationTargetExceptionSe produit,InvocationTargetExceptionException survenue dans le constructeur(IOException)Est enveloppé.

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) {// ★★★ Le compilateur vous oblige à gérer une exception qui encapsule une exception (IOException) qui s'est produite dans le constructeur: sweat_smile! !!
		Assertions.assertEquals(IOException.class, e.getTargetException().getClass());
	}
}

spring-le module jcl est ajouté:thumbsup:

[SPR-14512]:spring-Unmodulejclaétéajouté,etLog4j2vial'APICommonsLoggingsansutiliser"OriginalCommonsLogging"ou"Bridgelibraryquiimplémentel'APICommonsLogging"..x,SLF4J,JUL(java.util.logging)Vous pourrez sortir le journal via. Spring Framework utilise l'API Commons Logging pour la sortie du journal, et la bibliothèque pour la sortie réelle du journal est dans le style choisi par le développeur. Il y a longtemps, "Commons Logging"+Bibliothèque de journaux(Log4j etc.)Était courant, mais récemment "SLF4J"+Log ligatory qui implémente l'API SLF4J(Par exemple Logback)Est de plus en plus courant (probablement):sweat_smile:)。 Par exemple ... "SLF4J+Dans le cas où "Logback" génère le journal de l'application, la sortie du journal par Spring Framework est une bibliothèque de pont qui implémente l '"API de journalisation Commons".(jcl-over-slf4j etc.) + SLF4J +Il doit être configuré comme "Logback".

Voyons comment résoudre les bibliothèques dépendantes pour la configuration ci-dessus.

Spring Framework 4.Dans 3, printemps-Puisque le noyau dépend de "Commons Logging of the head family", ressort lors de l'utilisation de la bibliothèque de bridge-J'ai dû exclure "Original Commons Logging" des bibliothèques dépendantes du noyau.

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 </ artifactId> <! - ★★★ Exclure la journalisation Commons originale->
        </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 </ artifactId> <! - ★★★ "Bridge library that implements Commons Logging API" is required->
  <version>1.7.25</version>
</dependency>

Spring Framework 5.À partir de 0, sortez la partie de "Bibliothèque de pont qui implémente l'API de journalisation Commons"-Parce que jcl est responsable, jcl-over-Utilisation de l'API Commons Logging, "Log4j 2" sans ajouter une bibliothèque de pont telle que slf4j.x」「SLF4J」「JUL(java.util.logging)Vous pouvez sortir le journal via. Cadre de printemps 5.À partir de 0, printemps-le noyau est le printemps au lieu de la "journalisation commune de la famille principale"-spring car cela dépend de jcl-Tout ce que vous avez à faire est d'ajouter la bibliothèque de journaux prise en charge par jcl aux bibliothèques dépendantes.

xml:pom.xml(5.0〜)


<!-- ... -->
<dependency>
  <groupId>ch.qos.logback</groupId>
 <artifactId> logback-classic </ artifactId> <! - ★★★ Il vous suffit d'ajouter Logback qui implémente l'API SLF4J->
  <version>1.2.3</version>
</dependency>

Note:

Si les bibliothèques utilisées pour construire l'application dépendent de "Original Commons Logging" ou de "Bridge library qui implémente l'API Commons Logging", vous devez exclure ces bibliothèques.

Quand j'essaye d'exécuter le code qui génère le conteneur DI de Spring ...

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

Le journal suivant a été généré correctement: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
 ... (Omis)
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

ResourcePeut être déterminé si se trouve sur le système de fichiers:thumbsup:

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

@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());
}

ResourceLes donnéesReadableByteChannelPeut être obtenu via:thumbsup:

[SPR-14698]:Interfacepourextrairelesressources(ResourcereadableChannel()Méthodeajoutée,ReadableByteChannel(NewI/O)経由でリソースのデータを読み込むことができるようÀなります。ちなみÀ・・・RC1WritableResourceÀwritableChannelMéthode ajoutée,WritableByteChannel経由でデータを書き込めるようÀなっていたので、あわせて実装サンプルを紹介しておきます。

@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());
}

#Résumé

Cette fois, nous avons introduit les principaux changements dans les fonctions de base. Il y a également eu une introduction de changements de classe qui ne sont pas utilisés directement lors du développement d'applications qui utilisent le framework, mais des améliorations ont été apportées pour améliorer la «convivialité» et «l'efficacité» du framework. C'est une impression. la prochaine fois,"Changements majeurs liés au conteneur DISera présenté.

Recommended Posts

Changements majeurs dans la fonctionnalité de base de Spring Framework 5.0
Changements majeurs dans Spring Boot 1.5
Spring Framework 5.0 Résumé des principaux changements
Modifications majeures liées au test Spring Framework 5.0
Modifications majeures liées à Spring Framework 5.0 Web MVC
Modifications majeures liées au conteneur Spring Framework 5.0 DI
Modifications de l'emplacement principal dans iOS 14
Changements dans Mockito 2
Changements dans mybatis-spring-boot-starter 2.0
Changements dans mybatis-spring-boot-starter 2.1
Changements dans mybatis-spring-boot-starter 1.3
Changements dans Java 11
Exemple multi-module d'API RESTful avec IntelliJ + Jersey + Spring Framework
Inject Logger au printemps
[Spring Framework] Division de la configuration
Prise en charge multilingue de Spring Framework
1. Démarrez Spring Framework à partir de 1
Résumé de Spring Framework - À propos de DI
Utilisez Interceptor au printemps
Changements dans JUnit5M4-> M5
Obtenez des cookies au printemps
Créer Pivotal tc Server Developer Edition dans Spring Framework (STS)
Exemple de configuration d'API RESTful minimum avec Jersey + Spring Framework
Changements dans mybatis-spring-boot-starter 2.1
Changements dans mybatis-spring-boot-starter 1.3
Changements dans mybatis-spring-boot-starter 1.2
Changements dans Mockito 2
Changements dans Java 11
Changements dans JUnit5M4-> M5
Modifications de l'emplacement principal dans iOS 14
Changements majeurs dans Spring Boot 1.5
Notation de version Java qui change dans Java 10
Changements majeurs dans la fonctionnalité de base de Spring Framework 5.0