Mode annotation
Stereotype Annotation mode known as annotations, Spring notes have in common mode @Service
, @Repository
, @Controller
and so on, they are "derived" from @Component
comments. We all know that all @Component
annotated classes will be scanned by Spring and included in the IOC container, and @Component
the classes annotated by the derived annotations will also be scanned into the IOC container. Below we mainly come to understand @Component
the "derivation" and "layering" through custom mode annotations .
@Component "Derivative"
Create a new Spring Boot project, the Spring Boot version is 2.1.0.RELEASE, artifactId
autoconfig, and spring-boot-starter-web
dependencies are introduced . The project structure is as follows:
In com.example.demo
case the new annotation
package, and then create a FirstLevelService
comment:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface FirstLevelService {
String value() default "";
}
This annotation is defined by @Service
annotations, @Service
and you will find that it is @Component
annotated when you view the source code , so their hierarchical relationship is:
└─@Component
└─@Service
└─@FirstLevelService
That @FirstLevelService
is @Component
derived from the annotation mode, we have to test whether it marked a class can be scanned into the IOC container:
In com.example.demo
New Under service
the package, and then create a TestService
class:
@SecondLevelService
public class TestService {
}
In com.example.demo
the new bootstrap
package, and then create a ServiceBootStrap
class for the test register TestService
and obtain it from the IOC vessel:
@ComponentScan("com.example.demo.service")
public class ServiceBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(ServiceBootstrap.class)
.web(WebApplicationType.NONE)
.run(args);
TestService testService = context.getBean("testService", TestService.class);
System.out.println("TestService Bean: " + testService);
context.close();
}
}
Run the main method of this class, the console output is as follows:
@Component "hierarchical"
We com.example.demo.annotation
create another SecondLevelService
annotation definition under the path , the annotation is @FirstLevelService
marked by the above :
At this time, the hierarchical relationship is:
└─@Component
└─@Service
└─@FirstLevelService
└─@SecondLevelService
We will replace the TestService
above annotation with the main method, @SecondLevelService
and then run it again ServiceBootStrap
, the output is as follows:
It can be seen that the result is also successful.
One thing to note here is that the
@Component
annotation only contains one value attribute definition, so its "derived" annotation can only contain one value attribute definition.
@Enable module driver
@Enable
Module drivers are supported after Spring Framework 3.1. In general, the module here is a collection of components to achieve a certain function. Through the @Enable
module driver, we can turn on the corresponding module function.
@Enable
Module drive can be divided into two implementation modes: "annotation drive" and "interface programming". The following demonstrates one by one:
Annotation Driven
In Spring, an annotation-driven example can be viewed from the @EnableWebMvc
source code:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
This annotation is done by @Import
importing a configuration class DelegatingWebMvcConfiguration
:
This configuration class is inherited from it WebMvcConfigurationSupport
, which defines some Bean declarations.
Therefore, the annotation-driven
@Enable
module driver actually@Import
imports a configuration class to realize the component registration of the corresponding module. When these components are registered in the IOC container, the corresponding function of this module can be used.
Let's define a @Enable
module driver based on annotation- driven.
In com.example.demo
New Under configuration
the package, and then create a HelloWorldConfiguration
configuration class:
@Configuration
public class HelloWorldConfiguration {
@Bean
public String hello() {
return "hello world";
}
}
A named hello
Bean is defined in this configuration class with content hello world
.
In com.example.demo.annotation
creating a next EnableHelloWorld
annotation definition:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloWorldConfiguration.class)
public @interface EnableHelloWorld {
}
We @Import
imported the configuration class we just created on this annotation class.
Then in com.example.demo.bootstrap
creating a next TestEnableBootstap
startup class to test @EnableHelloWorld
annotation is in effect:
@EnableHelloWorld
public class TestEnableBootstap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(TestEnableBootstap.class)
.web(WebApplicationType.NONE)
.run(args);
String hello = context.getBean("hello", String.class);
System.out.println("hello Bean: " + hello);
context.close();
}
}
Run the main method of this class, the console output is as follows:
Explain that our custom annotation-driven @EnableHelloWorld
is feasible.
Interface programming
In addition to using the above method, we can also implement @Enable
module drive by way of interface programming . In Spring, there are @EnableCaching
annotations based on interface programming , check the source code:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({CachingConfigurationSelector.class})
public @interface EnableCaching {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default 2147483647;
}
EnableCaching
Annotations @Import
import a CachingConfigurationSelector
class, which indirectly implements the ImportSelector
interface. In the in- depth study of Spring component registration , we have introduced that ImportSelector
component registration can be achieved through .
Therefore
@Enable
, the essence of implementing module driving through interface programming is@Import
to import the interfaceImportSelector
implementation class, which can define the components that need to be registered in the IOC container, so as to realize the registration of the corresponding components of the corresponding module.
Next, we come to realize it again according to this idea:
In com.example.demo
the new selector
packet, then a new path in the HelloWorldImportSelector
implemented ImportSelector
interfaces:
public class HelloWorldImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{HelloWorldConfiguration.class.getName()};
}
}
If you don't understand the meaning of the above code, you can read the article on in-depth learning of Spring component registration .
Then we modify EnableHelloWorld
:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloWorldImportSelector.class)
public @interface EnableHelloWorld {
}
The import above is HelloWorldImportSelector
, not HelloWorldConfiguration
.
Run TestEnableBootstap
the main method again and you will find that the output is the same.
Automatic assembly
The bottom layer of automatic assembly technology in Spring Boot mainly uses the following technologies:
-
Spring mode annotation assembly
-
Spring @Enable module assembly
-
Spring conditional assembly (introduced in the in-depth study of Spring component registration )
-
Spring factory loading mechanism
The implementation class of Spring factory loading mechanism is SpringFactoriesLoader
, check its source code:
The method of this class will read the spring.factories configuration file under the META-INF directory. We check the file under spring-boot-autoconfigure-2.1.0.RELEASE.jar:
When the startup class is @EnableAutoConfiguration
marked, all the classes in the screenshot above will be scanned by Spring to see if they can be included in the IOC container for management.
For example org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
, the source code we viewed :
It can be seen that there are some annotations marked on this category, among which @Configuration
are the mode annotations, @EnableConfigurationProperties
the module assembly technology, and ConditionalOnClass
the conditional assembly technology. This is consistent with the main underlying technologies of Spring Boot auto-assembly listed above, so we can customize an auto-assembly implementation based on this idea.
Create a new configuration class HelloWorldAutoConfiguration
:
@Configuration
@EnableHelloWorld
@ConditionalOnProperty(name = "helloworld", havingValue = "true")
public class HelloWorldAutoConfiguration {
}
Then create a new META-INF directory under the resources directory and create a spring.factories file:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.demo.configuration.HelloWorldAutoConfiguration
Then add helloworld=true
configuration in the configuration file application.properties
helloworld=true
Finally create EnableAutoConfigurationBootstrap
, test HelloWorldAutoConfiguration
whether it works:
@EnableAutoConfiguration
public class EnableAutoConfigurationBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableAutoConfigurationBootstrap.class)
.web(WebApplicationType.NONE)
.run(args);
String hello = context.getBean("hello", String.class);
System.out.println("hello Bean: " + hello);
context.close();
}
}
Run the main method, the console output is as follows:
It shows that our customized automatic assembly has been successful.
Below is a brief analysis of the running logic of the code:
-
Spring's factory loading mechanism will automatically read the content of the spring.factories file in the META-INF directory;
- We defined in spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.demo.configuration.HelloWorldAutoConfiguration
We use
@EnableAutoConfiguration
annotations on the test class , thenHelloWorldAutoConfiguration
it will be scanned by Spring to see if it meets the requirements, and if it meets the requirements, it will be included in the IOC container;
-
HelloWorldAutoConfiguration
The function of the@ConditionalOnProperty
above annotation is: when the configuration file is configuredhelloworld=true
(we did add this configuration, so it meets the requirements), this class meets the scanning rules; the@EnableHelloWorld
annotation is a custom module-driven annotation in our previous example, which introduces the hello Bean, so the hello bean will exist in the IOC container; -
Through the above steps, we can get the hello bean through the context.