Good day, Habr.
Today I decided to present you a translation of a series of articles in preparation for Spring Professional Certification .
This translation is only the first article, if it goes to the audience, I will continue to issue translations.
So, let's begin.
Dependency injection is a special pattern that reduces communication between Spring components. Thus, when applying DI, your code becomes cleaner, simpler, it becomes easier to understand and test.
According to the DI pattern, creating objects for dependencies is transferred to the factory or given to a third party. This means that we can focus on using these objects instead of creating them.
In the Spring Framework, the org.springframework.factory.BeanFactory
interface provides a org.springframework.factory.BeanFactory
factory, which at the same time is an IoC container for the application. Bean management is configuration based (java or xml).
The org.springframework.context.ApplicationContext
interface is a wrapper over a bean factory that provides some additional features, such as AOP, transactions, security, i18n, etc.
The basis of the Spring Framework is a container, and our objects "live" in this container.
A container typically creates many objects based on their configurations and manages their life cycle from creating an object to destruction.
A container is an object that implements the ApplicationContext interface.
Spring provides several variations of context.
There are several basic implementations of the ApplicationContext interface:
Examples of creating a context:
ApplicationContext ctx = new FileSystemXmlApplicationContext( "c:/bean_properties.xml"); ApplicationContext ctx = new AnnotationConfigApplicationContext( "com.springdemoapp.JavaConfig.class");
BeanFactoryPostProcessors
setBeanName()
setBeanFactory()
setApplicationContext()
BeanPostProcessor
postProcessBeforeInitialization()
@PostConstruct
initMethod
@Bean
ApplicationContext#getBean()
close()
@PreDestroy
destroy()
@Bean
destroyMethod
If you are using JUnit 5, then you need to specify 2 annotations:
You can use the @SpringJUnitConfig
annotation, which combines both of these annotations.
You can use the @SpringJUnitWebConfig
annotation to test the web layer.
If this is not a web application, then there are 2 ways:
registerShutdownHook()
close()
In the Spring Boot application:
To create a class with configuration based on Java code, you need to annotate it with
@Configuration
.
This class will contain factory methods for creating beans in the container.
These methods should be annotated with the @Bean
annotation.
Example:
@Configuration public class DSConfig { @Bean public DataSource dataSource() { return DataSourceBuilder .create() .username("") .password("") .url("") .driverClassName("") .build(); } }
This class will place an instance of the DataSource class in the container. Later it can be used when accessing the database.
Component scanning - Spring automatically detects the beans that will be in the container. These are bins with annotations-stereotypes.
However, component scanning is not enabled by default.
To enable scanning, annotate the @ Configuration class with the annotation @ComponentScanning
. Spring will automatically scan the package that contains this class and all its subpackages.
You can specify other packages for scanning, and even classes:
// 2 @Configuration(<i>basePackages</i> = {"soundsystem", "video"})
// @Configuration(<i>basePackageClasses</i> = "MyClass.class")
Autowiring - Spring will automatically inject dependencies when scanning or placing a bin in a container.
Dependency injection uses the @Autowire
annotation.
Stereotypes are annotations denoting special functionality.
All stereotypes include @Component
annotation.
Component | The root annotation that marks a class as a candidate for auto-implementation |
Controller | Indicates that the class is the controller for sending data to the front.
|
@RestController | Indicates that the class is the controller for REST.
Contains Controller and @ResponseBody annotations |
Service | Indicates that the class is a service for executing business logic. |
Repository | Indicates that the class is a repository for working with the database. |
@Configuration | Indicates that the class contains a Java configuration (@ Bean methods) |
Scope - scope. There are 2 default scopes.
Singleton
| The default scope. There is only 1 bean instance in the container
|
Prototype
| Any number of bin instances can be in the container
|
And 4 scopes in a web application.
Request
| Scope - 1 HTTP request. A new bean is created for each request.
|
Session
| Scope - 1 session. A new bean is created for each session.
|
Application
| Scope - ServletContext Life Cycle
|
Web socket
| Scope - WebSocket Life Cycle
|
Singleton beans are usually created immediately upon scanning.
Prototype beans are usually created only upon request.
You can use the @Lazy
annotation to indicate how to initialize.
It is placed on @ Bean methods, on @ Configuration classes, or on @ Component classes.
Depending on the parameter (true or false) that the annotation accepts, initialization will either be lazy or will happen immediately. By default (i.e., without specifying a parameter), true is used.
BeanFactoryPostProcessor
BeanFactoryPostProcessor
BeanFactoryPostProcessor
In order to use custom BFPP. You can override the mechanism for obtaining data from metafiles.
@Bean public static PropertySourcesPlaceholderConfigurer pspc() { //, pspc }
destroyMethod
initMethod
name
value
Spring uses several BeanPostProcessors.
For example, CommonAnnotationPostProcessor
or AutowiredAnnotationBeanPostProcessor
.
BPP works with bean instances, i.e. the container creates the bin, and then BPP starts.
There are 3 options for creating such methods:
@PreDestroy
@PostConstruct
initMethod
destroyMethod
destroyMethod
InitializingBean#afterPropertiesSet()
DisposableBean#destroy()
The following are the types of DI that can be used in your application:
DI through the constructor is considered the best way, because for him there is no need to use reflection, and also he does not have the disadvantages of DI through the setter.
DI through the field is not recommended, because For this, reflection is used that reduces productivity.
DI through the constructor can lead to circular dependencies . To avoid this, you can use lazy initialization of beans or DI through the setter.
@Primary
@Autowire
Qualifier
@Qualifier
The container handles the DI using the AutowiredAnnotationBeanPostProcessor . In this regard, the annotation cannot be used in any BeanFactoryPP or BeanPP.
If the injected object is an array, collection, or map with a generic, then Spring will embed all the beans that are of type in this array (or another data structure). In the case of map, the key will be the bin name.
// , DI @Authowired(required = true/false)
Spring provides Qualifier annotation to overcome the DI ambiguity problem.
@Bean @Qualifier("SomeClass1") public SomeClass getField() {...} //… @Autowire @Qualifier("SomeField1") public SomeClass someField;
If there are several bins of the same type in the container (SomeClass), then the container will implement the bean with the corresponding qualifier above the @ Bean method. You can also not put a qualifier on a method, but use the bean name as a qualifier parameter.
The bean name can be specified through the Bean annotation parameter, and by default this is the name of the factory method.
A proxy is a special object that has the same public methods as the bean, but which has additional functionality.
Two types of proxies:
Pros of proxy objects:
If there is no bean instance in the container, the @ Bean method is called. If there is a bean instance, then the already created bean is returned.
When using the Java configuration, you can use the @Profile
annotation.
It allows you to use different settings for Spring, depending on the specified profile.
It can be placed on the @Configuration and Component classes, as well as on Bean methods.
Profile("!test") // ,
@Bean("dataSource") @Profile("production") public DataSource jndiDataSource() {...} @Bean("dataSource") @Profile("development") public DataSource standaloneDataSource() {...}
You can use the @Value
annotation for @Value
.
Such values can be obtained from property files, from bins, etc.
@Value("$some.key") public String stringWithDefaultValue;
A string will be embedded in this variable, for example, from property or from view.
As usual, please send corrections or errors found in PM.