This is the third installment of the "Spring Framework 5.0 Major Changes" series, and the main changes (new features and improvements, etc.) related to DI containers. ) I would like to introduce.
In Spring Framework 5.0, the following changes have been made to DI containers.
Item number | Changes |
---|---|
1 | Ability to scan components from the classpath and register beans in the DI container when the application is executed(@ComponentScan )Alternative to(Purpose of shortening and stabilizing application startup time)As a result, a mechanism to create an index of component candidates to be registered as beans at compile time will be added.[To details:arrow_right:] |
2 | Injection by auto wiring is optional(option)Provided from your own or 3rd party library to indicate that@Nullable Will be able to be specified.[To details:arrow_right:]**Note:**Annotation name is "Nullable" It is judged by whether or not it is. |
3 | GenericApplicationContext WhenAnnotationConfigApplicationContext Functional interface(Supplier )Bean registration method using(registerBean )WhenBean定義をカスタマイズするためのインタフェース(BeanDefinitionCustomizer )Will be added.[To details:arrow_right:] |
4 | When applying AOP to a class that implements an interface using "CGLIB Proxy"(proxyTargetClass=true When specified)Annotation specified in the interface method(@Transactional , @Cacheable , @Sync Such)Will be read.[To details:arrow_right:] |
5 | Generation management of xsd file specified when defining a bean in XML(Providing xsd files for past versions)Will be abolished and only xsd files for that version will be stored in the JAR file.[To details:arrow_right:] **Note:**Specifying a versioned xsd file within an XML file is still supported, but the same xsd file is always used when parsing the XML file. |
[SPR-11890]: A function to scan the class under the package specified at the time of application execution and register the bean in the DI container (@ComponentScan
" ), But by default, the target class is searched from the classpath at runtime. With this mechanism, the time required to scan a class depends on the package configuration and the number of classes.
From Spring Framework 5.0, a mechanism to resolve scan candidate classes at compile time will be added as an alternative method of classpath scanning (for the purpose of shortening or stabilizing the application startup time).
Note:
JIRA's comments don't seem to make it dramatically faster, and it doesn't seem to make it faster, so it's best to actually look at the effects and decide whether or not to use index scans. I think. In addition, in the test case created for operation verification (the total number of scan candidate classes and classes is about 10 classes), the classpath scan was slightly faster. (It's a difference of about several tens of msec: sweat_smile :)
To briefly explain the mechanism ... Using the "JSR 269: Pluggable Annotation Processing API" added in JDK 1.6, "Index file ( / META-) for acquiring scan candidate classes at compile time. INF / spring.components
) ”is created and the file is read at runtime. (If you try to make a picture in vain ... it looks like the following)
The classes to be output from the index file at compile time are "composite annotations that specify @Indexed
and @Indexed
as meta annotations (such as @Component
) "and annotations that start with javax.
(such as javax.
". @Named
and @ManagedBean
) "" package-info class ".
At the time of execution, the following Map
(index) is generated based on the index file, and the class list of scan candidates is acquired without actually scanning the package to be scanned for components.
As with the classpath scan, "classes outside the package to be scanned", "classes that match the exclusion filter", and "classes that do not match the inclusion filter" are not registered in the DI container.
Enabling index scanning is easy, just add the spring-context-indexer
module to the dependent artifacts as follows:
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>5.0.0.RC1</version>
<optional>true</optional> <!--Avoid packaging in war etc. as it is only needed when compiling-->
</dependency>
Note:
If there is at least one valid index file (
/META-INF/spring.components
) on the classpath, index scan is used by default. For example, if your project does not usespring-context-indexer
, but its dependent libraries (such as your company's common library) contain index files, your project's components will not be scanned. To avoid this event, you have two choices: "Usespring-context-indexer
in your own project "or" Do not scan the index at runtime ". To "prevent index scanning at runtime", go to "Spring Properties File (spring.properties
directly under the classpath) "or" Java System Properties (-D
Option)" to "spring Please specify .index.ignore = true
".
@Nullable
can be specified for any injection point: thumbsup:[SPR-15028]: Homebrew or 3rd party library ([SPR-15028]: Autowiring injection is optional (optional) You will be able to specify the @Nullable
provided by JSR-305](https://jcp.org/en/jsr/detail?id=305).
Note:
Internally, it judges whether the annotation name is
" Nullable "
or not, so it also recognizes your own@ Nullable
.
For example ...
〜4.3
@Component
public class Foo {
private final Bar bar;
private final Baz baz;
public Foo(Bar bar, @Autowired(required = false) Baz baz) {
this.bar = bar;
this.baz = baz;
}
}
Can also be rewritten in a style that does not use Spring Framework annotations (example: JSR-330 annotation + JSR-305 @ Nullable
) as shown below.
pom.xml
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
</dependency>
@Use of Nullable(5.0〜)
import javax.annotation.Nullable;
import javax.inject.Named;
//...
@Named
public class Foo {
private final Bar bar;
private final Baz baz;
public Foo(Bar bar, @Nullable Baz baz) {
this.bar = bar;
this.baz = baz;
}
}
By the way ... JSR-330 annotations (@Named
and @Inject
) can also be used in Spring Framework 4.3. You can also use the java.util.Optional
added in JDK 8 as a way to indicate that it is an arbitrary injection point.
Use of Optional
import java.util.Optional;
// ...
@Named
public class Foo {
private final Bar bar;
private final Optional<Baz> baz;
public Foo(Bar bar, Optional<Baz> baz) {
this.bar = bar;
this.baz = baz;
}
}
Supplier
): thumbsup:[SPR-14832]: Bean registration method (Supplier
) using functional interface (Supplier
) for GenericApplicationContext
and ʻAnnotationConfigApplicationContext An interface (
BeanDefinitionCustomizer) for customizing the registerBean
) and bean definition is added.
For example, if you do the following, the object returned from the Supplier
specified in the lambda expression will be managed in the DI container as a singleton bean.
Bean generation and registration using Supplier
try (AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext()) {
applicationContext.registerBean(Bar.class);
applicationContext.registerBean(Foo.class, () -> new Foo(applicationContext.getBean(Bar.class))); //★★★ Bean registration using Supplier
applicationContext.refresh();
Foo foo = applicationContext.getBean(Foo.class);
// ...
}
Furthermore ... By using the BeanDefinitionCustomizer
interface, you can specify the meta information (scope, etc.) of the bean managed by the DI container. In the sample below, the scope of the bean is changed to prototype.
java:org.springframework.beans.factory.config.BeanDefinitionCustomizer
@FunctionalInterface
public interface BeanDefinitionCustomizer {
void customize(BeanDefinition bd);
}
Example of bean definition customization using BeanDefinitionCustomizer
try (AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext()) {
applicationContext.registerBean(Bar.class);
applicationContext.registerBean(Foo.class, () -> new Foo(applicationContext.getBean(Bar.class)),
bd -> bd.setScope(BeanDefinition.SCOPE_PROTOTYPE)); //★★★ Customize Bean Definitions with BeanDefinitionCustomizer
applicationContext.refresh();
Foo foo = applicationContext.getBean(Foo.class);
Assertions.assertFalse(foo == applicationContext.getBean(Foo.class)); //Since the scope is a prototype, different instances are returned the first time and the second time
}
Note:
By the way ... If you don't use any annotations provided by Spring (do you want to do annotation-driven development?), You can also use
GenericApplicationContext
.
[SPR-14322 etc.]: When applying AOP to a class that implements an interface using "CGLIB Proxy" ( When proxyTargetClass = true
is specified), the annotations specified in the interface method (@Transactional
, @Cacheable
, @Sync
, etc.) will be read.
For example, when using Spring's cache function as shown below, annotate the interface with cache control.
interface
@CacheConfig(cacheNames = "accounts")
public interface AccountService {
@Cacheable
Account getAccount(int id);
}
Implementation class
public class AccountServiceImpl implements AccountService {
@Override
public Account getAccount(int id) {
return new Account(id);
}
}
In the configuration class, set to use CGLIB Proxy instead of JDK Proxy when applying Spring cache function (when creating Proxy object).
configuration
@EnableCaching(proxyTargetClass = true) //★★★ Set to use CGLIB Proxy
@Configuration
public static class CacheConfiguration {
@Bean
CacheManager cacheManager() {
return new ConcurrentMapCacheManager("accounts");
}
@Bean
AccountService accountService() {
return new AccountServiceImpl();
}
}
In this state, if you call the AccountService method obtained from the application context twice with the same argument, the cached object will be returned the second time.
python
try (AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(
CacheConfiguration.class)) {
AccountService service = applicationContext.getBean(AccountService.class);
Account account = service.getAccount(1);
Assertions.assertTrue(account == service.getAccount(1)); //Spring's cache function is applied, and the cached object is returned the second time.
}
[SPR-13499]: Generation management of xsd files specified when defining beans in XML (providing xsd files for past versions) It will be deprecated and only xsd files for that version will be stored in the JAR file. Specifying a versioned xsd file within an XML file is still supported, but the same xsd file is always used when parsing the XML file.
On Spring Framework 4.3, it was possible to use the attributes supported in the past version as follows.
Bean definition XML file
<?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-3.2.xsd"> <!-- ★★★ 3.Specify XSD file for 2-->
<bean class="com.example.di.Foo">
<property name="bar">
<ref local="bar"/> <!-- ★★★ 3.x has local attribute-->
</property>
</bean>
<bean id="bar" class="com.example.di.Bar"/>
</beans>
If you use this file with Spring Framework 5.0 ... It will be a schema violation when parsing the XML file, and the following error will occur.
org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 9 in XML document from class path resource [com/example/di/applicationContext.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 9; columnNumber: 25; cvc-complex-type.3.2.2:element'ref'Attribute to'local'Cannot be included.
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:399)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
In order to use this file on Spring Framework 5.0, it is necessary to take measures such as "change to bean
attribute instead of local
attribute" or "use ref
attribute of <property>
element ". Become. In addition, " local
attribute of <ref>
element "is an example of the attribute that can not be used, and some other things can not be used.
Example of changing to bean attribute
<bean class="com.example.di.Foo">
<property name="bar">
<ref bean="bar"/>
</property>
</bean>
<property>Example of using the ref attribute of an element
<bean class="com.example.di.Foo">
<property name="bar" ref="bar"/>
</bean>
Also, there is no point in specifying the version when specifying the XSD file (rather confusing), so it is recommended to take this opportunity to change to specify the XSD file that does not include the version.
Example of specifying an unversioned XSD file
<?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.xsd"> <!--★★★ Change to unversioned file-->
<!-- ... -->
</beans>
This time, we introduced the main changes related to DI containers. Perhaps "index scan support" is the main change, but it doesn't seem to start up dramatically faster, so look forward to future improvements! !! Is it something like: wink: Next time, I will introduce "Major changes related to WebMVC".
Recommended Posts