This is the second installment of the "Spring Framework 5.0 Major Changes" series, and the main changes in core functions (new functions, improvements, etc.) I would like to introduce.
In Spring Framework 5.0, the following changes have been made to general-purpose functions (= core functions) that do not depend on the type of application.
Item number | Changes |
---|---|
1 | You will be able to efficiently access method parameters using the mechanisms supported by JDK 8.[To details:arrow_right:] **Note:**Since it is an internal implementation, the external specifications do not change. |
2 | Some interfaces will now implement the default methods supported by the JDK 8.[To details:arrow_right:] **Note:**Whencreatinganimplementationclass(extensionclass) for those interfaces, it is only necessary to implement the necessary methods, so there are measures such as "creating an Adapter class that provides an empty implementation" and "unnecessarily performing an empty implementation". It will be unnecessary. |
3 | Supported in JDK 7StandardCharsets Will be used.[To details:arrow_right:]**Note:**Since it is an internal implementation, the external specifications do not change. |
4 | Will be deprecated in JDK 9Class#newInstance() Instead ofConstructor#newInstance() Will be called to instantiate.[To details:arrow_right:]**Note:**Since it is an internal implementation, the external specifications do not change. |
5 | spring-jcl Modules have been added and Log4j 2 via the Commons Logging API.x, SLF4J, JUL(java.util.logging )You will be able to output the log via.[To details:arrow_right:]Note: Spring Framework 4.The library for log bridge, which was required up to 3, is no longer required. |
6 | Interface for abstracting resources(Resource )ToisFile() メソッドが追加され、リソースがファイルシステム上To存在するか判定できるようToなります。[To details:arrow_right:] |
7 | Interface for abstracting resources(Resource )ToreadableChannel() Method added,ReadableByteChannel (New I/O)経由でリソースのデータを読み込むことができるようToなります。[To details:arrow_right:] |
[SPR-14055]: Starting with JDK 8, the java.lang.reflect.Method
and java.lang.reflect.Constructor
classes are It now inherits the java.lang.reflect.Executable
class added from JDK 8, and Spring Framework 5.0 now uses the methods of the ʻExecutable` class to access method and constructor parameter information. became.
Since the class modified this time is basically a class used in the internal processing of the framework, I think that it is not often used directly by application developers, but it is used when creating AP infrastructure parts such as framework extensions. There may be a possibility.
For example ...
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;
}
}
Let's take a look at the code that accesses the constructor and method arguments of this class.
In Spring Framework 4.3, use the forMethodOrConstructor
method of the MethodParameter
class to get the parameter information. (Deprecated 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());
}
}
In Spring Framework 5.0, use the forExecutable
or forParameter
method of the MethodParameter
class to get the parameter information.
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:
By the way ... If you want to get the actual parameter name, you need to set
DefaultParameterNameDiscoverer
toMethodParameter
. (If you want to use this mechanism, you need to specify "-parameters
"or" -g` "as a compile option.)MethodParameter parameter0 = MethodParameter.forParameter(method.getParameters()[0]); parameter0.initParameterNameDiscovery(new DefaultParameterNameDiscoverer()); // ... Assertions.assertEquals("text", parameter0.getParameterName());
#Default method implemented in interface:thumbsup:
[SPR-14432]:SomeoftheinterfacesprovidedbytheSpringFrameworkwillnowimplementthedefaultmethodssupportedbytheJDK8.Duetothischange,whencreatinganimplementationclass(extensionclass) for those interfaces, it is only necessary to implement the necessary methods, so "Create an Adapter class that provides an empty implementation" or "Uselessly perform an empty implementation". There is no need to take measures such as ".
For example, spring-Provided by the beans moduleBeanPostProcessor
On the interfacepostProcessBeforeInitialization
(Method that is called back before performing the initial processing of the bean)WhenpostProcessAfterInitialization
(A method that is called back after performing the initial processing of Bnea)Whenいう2つのメソッドがありますが、そちらか一方だけ実装したいWhenいうケースもあります。
Spring Framework 4.In 3, I had to implement two methods without fail:
〜4.3
@Configuration
public class AppConfig {
@Bean
BeanPostProcessor myBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// Example) Implement the code that changes the bean state before the initialization process is performed.
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean; // Template implementation that returns bean as it is is required ...
}
};
}
}
Spring Framework 5.At 0, you only need to implement the required methods as shown below.
5.0〜
@Configuration
public class AppConfig {
@Bean
BeanPostProcessor myBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// Example) Implement the code that changes the bean state before the initialization process is performed.
return bean;
}
};
}
}
#Standard Charsets are used for processing within the framework:thumbsup:
[SPR-14492]:Intheinternalprocessingoftheframework,"UTF-Standardsuchas8"Charset
Whenspecifying,"Charset#forName(String)
Supported by JDK 7 instead ofStandardCharsets
Will be used. This has no effect on framework users,StandardCharsets
To "US-ASCII」「ISO-8859-1」「UTF-8」「UTF-16BE」「UTF-16LE」「UTF-There are constants for "16", so let's all actively use these constants!
Constructor#newInstance()
Is generated using:thumbsup:[SPR-14486]:IntheinternalprocessingoftheframeworkDeprecatedinJDK9WillbeClass#newInstance()
InsteadofConstructor#newInstance()
Willbecalledtoinstantiate.Apparently...Class#newInstance()
Isusedtohandlecheckedexceptionswhenacheckedexceptionwithadeclarationinthethrowsclauseoftheconstructoroccurs.(Addcatchorthrowsclause)Is not enforced, so it seems that the exception that occurred is thrown to the top unintentionally. This is also a story that does not affect the framework user at all, but it seems that there are cases where it is implemented to create an instance using reflection independently, so let's see what is the problem. ..
It's not really possible, but it's forced by the default constructorIOException
Prepare a class that generates.
public class Foo {
public Foo () throws IOException {
throw new IOException("error.");
}
// ...
}
When creating an instance of the class above,new Foor()
, The compiler willIOException
You will be asked to handle.
new
@Test
void newInstance() {
try {
new Foo();
Assertions.fail("does not occurred a IOException.");
} catch (IOException e) {// ★★★ The compiler forces the handling of IOException! !!
// NOP
}
}
nextClass#newInstance
When you create an instance usingInstantiationException
WhenIllegalAccessException
のハンドリングを行うように求めてきます。 この状態でテストケースを実行するWhen・・・
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)
And the test fails. In other words ...InstantiationException
WhenIllegalAccessException
Occurs in the constructor even after handlingIOException
Is not handled and an exception is thrown unintentionally to the caller.
ThenClass#newInstance
notConstructor#newInstance()
Let's see what happens when you use. If you run the test below,InvocationTargetException
Occurs,InvocationTargetException
Exception that occurred in the constructor inside(IOException
)Is wrapped.
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) {// ★★★ The compiler forces the handling of the exception that wraps the exception (IOException) that occurred in the constructor: sweat_smile! !!
Assertions.assertEquals(IOException.class, e.getTargetException().getClass());
}
}
[SPR-14512]:spring-Ajclmodulehasbeenadded,andLog4j2viatheCommonsLoggingAPIwithoutusing"OriginalCommonsLogging"or"BridgelibrarythatimplementstheCommonsLoggingAPI"..x,SLF4J,JUL(java.util.logging)You will be able to output the log via. Spring Framework uses the API of Commons Logging to output logs, and the library for actual log output is in the style selected by the developer. A long time ago, "Commons Logging"+Log library(Log4j etc.)Was common, but recently "SLF4J"+Log ligatory that implements SLF4J API(For example Logback)Is becoming more mainstream (probably):sweat_smile:)。 For example ... "SLF4J+In the case where the application log is output by "Logback", the log output by Spring Framework is a bridge library that implements the API of Commons Logging.(jcl-over-slf4j etc.) + SLF4J +It should be configured as "Logback".
Let's see how to resolve the dependent libraries for the above configuration.
Spring Framework 4.In 3, spring-Since the core depends on "Commons Logging of the head family", spring when using the bridge library-I had to exclude "Original Commons Logging" from the core's dependent libraries.
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> <!-★★★ Exclude Commons Logging of the head family->
</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 implementing Commons Logging API" is required->
<version>1.7.25</version>
</dependency>
Spring Framework 5.From 0, spring the part of "Bridge library that implements API of Commons Logging"-Because jcl is responsible, jcl-over-Using the Commons Logging API, "Log4j 2" without adding a bridge library such as slf4j.x」「SLF4J」「JUL(java.util.logging
)You can output the log via. Spring Framework 5.From 0, spring-core is spring instead of "commons logging of the head family"-spring because it depends on jcl-All you have to do is add the log library supported by jcl to the dependent libraries.
xml:pom.xml(5.0〜)
<!-- ... -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId> logback-classic </ artifactId> <!-★★★ You only need to add Logback that implements the SLF4J API->
<version>1.2.3</version>
</dependency>
Note:
If the libraries used to build the application depend on "the original Commons Logging" or "the bridge library that implements the Commons Logging API", you need to exclude those libraries.
When I try to execute the code that generates the DI container of Spring ...
@Test
void applicationContext() {
try (ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class)) {
// ...
}
}
The following log was output properly: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
... (Omitted)
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
Can be determined if is on the file system:thumbsup:[SPR-14484]:Interfaceforabstractingresources(Resource
)ToisFile()
メソッドが追加され、リソースがファイルシステム上To存在するか判定できるようToなります。
@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
DataReadableByteChannel
Can be obtained via:thumbsup:[SPR-14698]:Interfaceforabstractingresources(Resource
)ToreadableChannel()
Methodadded,ReadableByteChannel
(NewI/O)経由でリソースのデータを読み込むことができるようToなります。ちなみTo・・・RC1WritableResource
TowritableChannel
Method added,WritableByteChannel
経由でデータを書き込めるようToなっていたので、あわせて実装サンプルを紹介しておきます。
@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());
}
#Summary
This time, we have introduced the main changes in core functions. There was also an introduction of class changes that are not used directly when developing applications that use the framework, but improvements have been made to improve the "usability" and "efficiency" of the framework. It is an impression. next time,"Major changes related to DI containerWill be introduced.
Recommended Posts