This article, in the iteration from Spring 1.x to Spring 5.x, considers the development process of Spring annotation-driven from the current perspective, which will help us better understand the annotation design in Spring.
Spring Framework 1.x
In the SpringFramework1.x era, 1.2.0 was the watershed of this era. At that time, Java5 was just released. The industry is emerging with the technical style of using Annotation, and Spring Framework naturally provides support. For example, @Transactional and other annotations were already supported at that time. But at this time, the XML configuration method is still the only option.
Add Bean declaration in xml
<bean name="testService" class="com.gupaoedu.controller.TestService"/>
test
public class XmlMain { public static void main(String[] args) { ApplicationContext context=new FileSystemXmlApplicationContext("classpath:applicationContext.xml"); TestService testService=(TestService)context.getBean("testService"); System.out.println(testService); } }
Spring Framework 2.x
In the Spring Framework 2.x era, version 2.0 added @Required, @Repository, and AOP-related @Aspect annotations to Annotation, and at the same time improved XML configuration capabilities, that is, extensible XML. For example, open source frameworks such as Dubbo are based on Spring XML is extended to perfectly integrate Spring, thus lowering the threshold for Dubbo.
In the 2.x era, the 2.5 version is also a watershed in this era, it introduced some very core Annotations
- Autowired dependency injection
- @Qualifier dependency lookup
- @Component, @Service component declaration
- @Controller, @RequestMappring and other spring mvc annotations
Although the Spring 2.x era provided a lot of annotations, it still did not break away from the XML configuration driver, such as <context:annotation-config> <context:componet-scan>, the former is responsible for registering the Annotation processor, and the latter is responsible Scan the classes marked by Spring mode annotations under the specified package path under the classpath, and register them as Spring Beans
Define <context:componet-scan> in applicationContext.xml
<context:component-scan base-package="com.gupaoedu.controller"/>
Add comment statement
@Service public class TestService { }
Test class
public class XmlMain { public static void main(String[] args) { ApplicationContext context=new FileSystemXmlApplicationContext("classpath:applicationContext.xml"); TestService testService=(TestService)context.getBean("testService"); System.out.println(testService); } }
Spring Framework 3.x
Spring Framework 3.0 is a milestone era, and its features have begun to have very large extensions, such as the full embrace of Java 5 and Spring Annotation. More importantly, it provides the configuration class annotation @Configuration. His first task is to replace the XML configuration method, but unfortunately, Spring Framework 3.0 has not introduced the annotation to replace the XML element <context:componet-scan> , But chose a transition method @ImportResource.
@ImportResource allows to import legacy XML configuration files, such as
@ImportResource("classpath:/META-INF/spring/other.xml")
@Configuration
public class SpringConfiguration{
}
And the AnnotationConfigApplicationContext registration is provided in Spring Frameworkd, which is used to register @Configuration Class and assemble by parsing the Configuration class.
In version 3.1, @ComponentScan was introduced, replacing the XML element <Context:component-scan>. Although this annotation is a small upgrade, it is a big improvement in the field of annotation-driven for spring. It embodies Spring's configurationless support.
Configuration configuration demo
You should have used the Configuration annotation. It is an annotation used by the Spring IOC container-based configuration class in the form of JavaConfig. Because SpringBoot is essentially a spring application, it is normal to load the configuration of the IOC container through this annotation. Therefore, @Configuration is marked in the startup class, which means that it is actually a configuration class of an IoC container.
Give a very simple example
- Test code
ConfigurationDemo
@Configuration
public class ConfigurationDemo {
@Bean
public DemoClass demoClass(){
return new DemoClass();
}
}
DemoClass
public class DemoClass {
public void say(){
System.out.println("say: Hello Mic");
}
}
ConfigurationMain
public class ConfigurationMain {
public static void main(String[] args) {
ApplicationContext applicationContext=
new AnnotationConfigApplicationContext
(ConfigurationDemo.class);
DemoClass demoClass=applicationContext.getBean(DemoClass.class);
demoClass.say();
}
}
Component-scan
The annotation of ComponentScan is the most popular one, which is equivalent to <context:component-scan> in the xml configuration file. Its main function is to scan the identified classes that need to be assembled under the specified path, and automatically assemble them into the spring Ioc container.
The main forms of identifying the classes to be assembled are: @Component, @Repository, @Service, @Controller and other annotation-identified classes.
In the spring-mvc project, create a separate package path and create a OtherServcie.
@Service public class OtherService { }
In the Controller, inject an instance of OtherService. When accessing this interface at this time, an error will be reported, indicating that there is no instance of otherService.
@RestController public class HelloController { @Autowired OtherService otherService; @GetMapping("/hello") public String hello(){ System.out.println(otherService); return "Hello Gupaoedu"; } }
Add the comment of conpoment-scan, visit again, and solve the error.
@ComponentScan("com.gupaoedu")
By default, ComponentScan will scan all the classes under the current package with the relevant solution flags to the IoC container;
Import annotation
What does the import annotation mean? <import resource/>
of an annotation in the form of 061a7167272050 in the xml form, and you understand its role. Import is to merge multiple divided container configurations into one configuration. The meaning expressed in JavaConfig is the same.
Create a package and add a separate configuration inside
public class DefaultBean { } @Configuration public class SpringConfig { @Bean public DefaultBean defaultBean(){ return new DefaultBean(); } }
Now run the test method,
public class MainDemo { public static void main(String[] args) { ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfig.class); String[] defNames=ac.getBeanDefinitionNames(); for(String name:defNames){ System.out.println(name); } } }
Create a configuration class in another package path. At this time, run the previous test method again and print the OtherBean instance, this time an error will be reported, indicating that there is no such instance
public class OtherBean { } @Configuration public class OtherConfig { @Bean public OtherBean otherBean(){ return new OtherBean(); } }
Modify springConfig to import another configuration
@Import(OtherConfig.class) @Configuration public class SpringConfig { @Bean public DefaultBean defaultBean(){ return new DefaultBean(); } }
- Run the test method again to see the output of the object instance.
So far, we have learned that Spring Framework is a solution that completely replaces XML in the annotation-driven era. So far, did the Spring team stop there? You are too simple. Although non-configuration can reduce the trouble caused by configuration maintenance, there will still be a lot of basic configuration statements for third-party components. It is also very cumbersome, so Spring withdrew the @Enable module driver. The function of this feature is to assemble functional components with the same responsibilities in a modular manner, which further simplifies the configuration of Spring Beans.
Enable module driver
We implement the function of a timed task through the timed task mechanism provided by spring, and demonstrate the difference between using Enable annotation and not using Enable. Let everyone feel the role of some Enable annotations.
using EnableScheduing
Add the configuration of timing scheduling in applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.gupaoedu.controller"/> <!--AnnotationDrivenBeanDefinitionParser--> <task:annotation-driven scheduler="scheduler"/> <!-- 定时器开关--> <task:scheduler id="scheduler" pool-size="5"/> </beans>
Write task processing class
@Service public class TaskService { @Scheduled(fixedRate = 5000) //通过@Scheduled声明该方法是计划任务,使用fixedRate属性每隔固定时间执行 public void reportCurrentTime(){ System.out.println("每隔5秒执行一次 "+new Date()); } }
Write test class
public class TestTask { public static void main(String[] args) { ApplicationContext applicationContext=new FileSystemXmlApplicationContext("classpath:applicationContext.xml"); } }
using EnableScheding
Create a configuration class
@Configuration @ComponentScan("com.gupaoedu.controller") @EnableScheduling public class SpringConfig { }
Create a service
@Service public class TaskService { @Scheduled(fixedRate = 5000) //通过@Scheduled声明该方法是计划任务,使用fixedRate属性每隔固定时间执行 public void reportCurrentTime(){ System.out.println("每隔5秒执行一次 "+new Date()); } }
Create a main method
public class TaskMain { public static void main(String[] args) { ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class); } }
- The function of timing scheduling can be realized by starting the service.
Consider which step is omitted when using Enable?
First of all, we look at the code that does not use Enable, there will be a
<task:annotation-driven scheduler="scheduler"/>
This scheduler is an annotation driver and will be parsed by the AnnotationDrivenBeanDefinitionParser.
In the parse method, there will be the definition of the following code
builder = BeanDefinitionBuilder.genericBeanDefinition("org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor");
builder.getRawBeanDefinition().setSource(source);
This class is used to parse the @Scheduled annotation.
Ok, let's look at the EnableScheduling annotation again, we can see that it will automatically register a ScheduledAnnotationBeanPostProcessor bean. Therefore, through this example, I want to express the role of the Enable annotation, which can help us omit the configuration of the bean declaration of some third-party modules.
public class SchedulingConfiguration {
public SchedulingConfiguration() {
}
@Bean(
name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
)
@Role(2)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
Spring Framework 4.x
Spring 4.x version is the perfect era of annotations. It is mainly to improve conditional assembly capabilities. It introduces @Conditional annotations and realizes cooperation through custom Conditions, which makes up for the shortcomings of the previous version of conditional configuration.
Simply put, Conditional provides a Bean loading condition judgment, that is, if this condition is not met, then the object declared by @Bean will not be automatically loaded. How is it used? , Let’s take a brief look at the basic usage of it.
Overview of Conditional
@Conditional is an annotation. Let's observe the declaration of this annotation. It can receive an array of Conditions.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
This Condition is a functional interface that provides a matchers method. Simply put, it provides a matching judgment rule. Returning true means that the bean can be injected, and returning false means that it cannot be injected.
Conditional's actual combat
Customize a Condition, the logic is relatively simple, if the current operating system is Windows, it returns true, otherwise it returns false
public class GpCondition implements Condition{ @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { //此处进行条件判断,如果返回 true,表示需要加载该配置类或者 Bean //否则,表示不加载 String os=conditionContext.getEnvironment().getProperty("os.name"); if(os.contains("Windows")){ return true; } return false; } }
Create a configuration class, load a BeanClass
@Configuration public class ConditionConfig { @Bean @Conditional(GpCondition.class) public BeanClass beanClass(){ return new BeanClass(); } }
- Add @Conditional(GpCondition.class) to the bean declaration method of BeanClass, where the specific condition is our custom GpCondition class. The meaning of the above code is that if the matches in the GpCondition class return true, load the BeanClass into the Spring IoC container
Run the test method
public class ConditionMain { public static void main(String[] args) { AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(ConditionConfig.class); BeanClass beanClass=context.getBean(BeanClass.class); System.out.println(beanClass); } }
Summarize
After the overall analysis of Spring annotation-driven, it is not difficult to find that the reason why we are now able to complete a large number of functions in Spring very conveniently based on annotations is due to the various efforts made by the Spring team to continuously solve user pain points.
The automatic assembly mechanism of Spring Boot is also evolved on the basis of Spring annotation-driven. In the follow-up content, I will specifically analyze the automatic assembly mechanism of Spring Boot.
Copyright statement: All articles in this blog, except for special statements, adopt the CC BY-NC-SA 4.0 license agreement. Please indicate the reprint from Mic takes you to learn architecture!
If this article is helpful to you, please help me to follow and like. Your persistence is the motivation for my continuous creation. Welcome to follow the WeChat public account of the same name for more technical dry goods!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。