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.
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 7StandardCharsets Sera 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-jcl Des 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(Resource )ÀisFile() メソッドが追加され、リソースがファイルシステム上À存在するか判定できるようÀなります。[Vers les détails:arrow_right:] |
7 | Interface pour extraire les ressources(Resource )ÀreadableChannel() Méthode ajoutée,ReadableByteChannel (New I/O)経由でリソースのデータを読み込むことができるようÀなります。[Vers les détails:arrow_right:] |
[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
surMethodParameter
. (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 beansBeanPostProcessor
Sur 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"Charset
Lorsdelaspécification,"Charset#forName(String)
Pris en charge par JDK 7 au lieu deStandardCharsets
Sera 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éfautIOException
Pré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 compilateurIOException
Il 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#newInstance
Lorsque vous créez une instance en utilisantInstantiationException
QuandIllegalAccessException
のハンドリングを行うように求めてきます。 この状態でテストケースを実行する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 ...InstantiationException
QuandIllegalAccessException
Se produit dans le constructeur même après la manipulationIOException
N'est pas gérée et une exception est lancée involontairement à l'appelant.
PuisClass#newInstance
ne pasConstructor#newInstance()
Voyons ce qui se passe lorsque vous utilisez. Si vous exécutez le test ci-dessous,InvocationTargetException
Se produit,InvocationTargetException
Exception 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());
}
}
[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
Resource
Peut être déterminé si se trouve sur le système de fichiers:thumbsup:[SPR-14484]:Interfacepourextrairelesressources(Resource
)ÀisFile()
メソッドが追加され、リソースがファイルシステム上À存在するか判定できるようÀなります。
@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());
}
Resource
Les donnéesReadableByteChannel
Peut être obtenu via:thumbsup:[SPR-14698]:Interfacepourextrairelesressources(Resource
)ÀreadableChannel()
Méthodeajoutée,ReadableByteChannel
(NewI/O)経由でリソースのデータを読み込むことができるようÀなります。ちなみÀ・・・RC1WritableResource
ÀwritableChannel
Mé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