“This is the 26th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

This article, in the Spring1.x to Spring 5.x iterations, takes a look at Spring’s annotation-driven evolution from its present perspective, which will help us better understand annotation design in Spring.

Spring Framework 1.x

In the era of Spring framework1.x, 1.2.0 was the watershed of this era. At that time, Java5 was just released, the technology trend of using annotations was emerging, and the SpringFramework also provided support. Annotations such as @Transactional were already supported, but at this point XML configuration was the only option.

  • Add a Bean declaration to the XML

    <bean name="testService" class="com.gupaoedu.controller.TestService"/>
    Copy the code
  • 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); }}Copy the code

Spring Framework 2.x

Spring Framework2.x version 2.0 adds @required, @Repository, and aOP-related @aspect annotations to annotations, and also improves XML configuration capabilities, i.e. extensible XML. Open source frameworks such as Dubbo are based on Spring XML extensions that perfectly integrate Spring, lowering the barriers to use for Dubbo.

In the 2.x era, version 2.5 was also a watershed for this era, introducing some very core annotations

  • Autowired dependency injection
  • Qualifier depends on lookup
  • @Component, @service Component declarations
  • Spring MVC annotations for @Controller, @requestMappring, etc

Although Spring 2.x provides a number of annotations, it is still not free from XML configuration drivers, such as Context :annotation-config Context: Componet-Scan, which is responsible for registering annotation processors, The latter is responsible for scanning the classes marked by Spring schema annotations in the specified package path of the classpath and registering them as Spring beans

  • Define context in applicationContext.xml: Componet-scan

    <context:component-scan base-package="com.gupaoedu.controller"/>
    Copy the code
  • Add an annotation statement

    @Service
    public class TestService {}Copy the code
  • The 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); }}Copy the code

Spring Framework 3.x

Spring Framework3.0 was a landmark era, with very large extensions to its features, such as a full embrace of Java5 and Spring annotations. Most importantly, it provides the @configuration class annotation, which appears primarily to replace the XML Configuration method. Unfortunately, Spring Framework3.0 does not introduce an annotation to replace the XML element context:componet-scan. Instead, a transition was chosen, @importResource.

@importResource allows you to import legacy XML configuration files, such as

@ImportResource("classpath:/META-INF/spring/other.xml")
@Configuration
public class SpringConfiguration{}Copy the code

And in the Spring Frameworkd provides AnnotationConfigApplicationContext register to register @ the Configuration Class, by parsing the Configuration Class for assembly.

In version 3.1, @ComponentScan was introduced, replacing the XML element Context: Component-scan. This annotation is a minor upgrade, but it’s a big step forward for Spring in the annotation-driven world. This also demonstrates Spring’s configuration-free support.

Configuration Configuration Demo

  • Configuration is an annotation used by the JavaConfig Configuration class based on the Spring IOC container. Since SpringBoot is essentially a Spring application, it is normal to load the IOC container configuration through this annotation. So the @Configuration tag in the startup class means that it is also a Configuration class for the IoC container.

    Let’s take a very simple example

  • The test code

    ConfigurationDemo
    @Configuration
    public class ConfigurationDemo {
        @Bean
        public DemoClass demoClass(a){
            return new DemoClass();
        }
    }
    DemoClass
    public class DemoClass {
    
        public void say(a){
            System.out.println("say: Hello Mic");
        }
    }
    ConfigurationMain
    public class ConfigurationMain {
    
        public static void main(String[] args) {
            ApplicationContext applicationContext=
                    newAnnotationConfigApplicationContext (ConfigurationDemo.class); DemoClass demoClass=applicationContext.getBean(DemoClass.class); demoClass.say(); }}Copy the code

Component-scan

ComponentScan is the most common annotation, equivalent to context: Component-scan in an XML configuration file. Its main function is to scan the designated path for classes that need to be assembled automatically into Spring’s Ioc container.

Classes that need to be assembled are identified in the form of @Component, @repository, @Service, and @Controller annotations.

  • In the spring-MVC project, create a separate package path and create an OtherServcie.

    @Service
    public class OtherService {}Copy the code
  • In the Controller, you inject an instance of OtherService, and when you access this interface, you get an error saying there is no instance of OtherService.

    @RestController
    public class HelloController {
    
        @Autowired
        OtherService otherService;
    
        @GetMapping("/hello")
        public String hello(a){
            System.out.println(otherService);
            return "Hello Gupaoedu"; }}Copy the code
  • Add conpoment-scan annotation, access again, error resolved.

    @ComponentScan("com.gupaoedu")
    Copy the code

By default, ComponentScan scans all annotated classes in the current package into the IoC container;

The Import annotations

What does an import annotation mean? Think of an annotation in XML form in the form of
. Import is the merging of multiple container configurations into one configuration. The meaning expressed in JavaConfig is the same.

  • Create a package and add a separate Configuration to it

    public class DefaultBean {}@Configuration
    public class SpringConfig {
    
        @Bean
        public DefaultBean defaultBean(a){
            return newDefaultBean(); }}Copy the code
  • Run the test method at this point,

    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); }}}Copy the code
  • Create a configuration class in a different package path. When you run the previous test method again and print an instance of OtherBean, an error is reported indicating that the instance is not present

    public class OtherBean {}@Configuration
    public class OtherConfig {
    
        @Bean
        public OtherBean otherBean(a){
            return newOtherBean(); }}Copy the code
  • Modify springConfig to import another configuration

    @Import(OtherConfig.class)
    @Configuration
    public class SpringConfig {
    
        @Bean
        public DefaultBean defaultBean(a){
            return newDefaultBean(); }}Copy the code
  • Run the test method again and see the output of the object instance.

So far, we’ve seen the Spring Framework’s solution to completely replace XML in the annotation-driven era. Is this the end of the Spring team? You guys are too simple. Although configuration-free can reduce the hassle of configuration maintenance, there are still many basic configuration declarations for third-party components. It was also tedious, so Spring retired the @enable module driver. This feature further simplifies the configuration of Spring beans by assembling functional components with the same responsibilities in a modular manner.

Enable Module driver

We use the scheduled task mechanism provided by Spring to implement a scheduled task function. We demonstrate the difference between using the Enable annotation and not using the Enable annotation. Let’s get a feel for some Enable annotations.

Before using EnableScheduing

  • Add the scheduled scheduling configuration to applicationContext.xml

    
            
    <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"/> <! -- Timer switch -->
        <task:scheduler id="scheduler" pool-size="5"/>
    </beans>
    Copy the code
  • Write a task handling class

    @Service
    public class TaskService {
    
        @Scheduled(fixedRate = 5000) // The @scheduled method declares that the method is a Scheduled task, executed at fixed intervals using the fixedRate attribute
        public void reportCurrentTime(a){
            System.out.println("Every five seconds."+newDate()); }}Copy the code
  • Writing test classes

    public class TestTask {
    
        public static void main(String[] args) {
            ApplicationContext applicationContext=new FileSystemXmlApplicationContext("classpath:applicationContext.xml"); }}Copy the code

After using EnableScheding

  • Create a configuration class

    @Configuration
    @ComponentScan("com.gupaoedu.controller")
    @EnableScheduling
    public class SpringConfig {}Copy the code
  • Create a Service

    @Service
    public class TaskService {
        @Scheduled(fixedRate = 5000) // The @scheduled method declares that the method is a Scheduled task, executed at fixed intervals using the fixedRate attribute
        public void reportCurrentTime(a){
            System.out.println("Every five seconds."+newDate()); }}Copy the code
  • Create a main method

    public class TaskMain {
        public static void main(String[] args) {
            ApplicationContext context=newAnnotationConfigApplicationContext(SpringConfig.class); }}Copy the code
  • To implement the scheduled scheduling function, start the service.

Which step is omitted by thinking about using Enable?

First let’s look at the code that doesn’t use Enable

<task:annotation-driven scheduler="scheduler"/>
Copy the code

The scheduler is an annotation drive, will be AnnotationDrivenBeanDefinitionParser the parser for parsing.

In the Parse method, the following code is defined

 builder = BeanDefinitionBuilder.genericBeanDefinition("org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor");
            builder.getRawBeanDefinition().setSource(source);
Copy the code

This class is used to parse @Scheduled annotations.

Ok, we’ll have a look at EnableScheduling annotations, we can see, it will automatically register a ScheduledAnnotationBeanPostProcessor bean. So, in this example, we want to illustrate the use of the Enable annotation, which can help us omit some of the declared configuration of the bean of the third party module.

public class SchedulingConfiguration {
    public SchedulingConfiguration(a) {}@Bean( name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"} )
    @Role(2)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor(a) {
        return newScheduledAnnotationBeanPostProcessor(); }}Copy the code

Spring Framework 4.x

Spring 4.x version is a perfect era of annotation. It mainly improves Conditional assembly ability by introducing @Conditional annotation, which makes up for the shortcomings of Conditional configuration in previous versions by customizing Condition to realize coordination.

To put it simply, Conditional provides a judgment on the loading condition of a Bean. That is, if the condition is not met, an object declared via @bean will not be automatically loaded. First, let’s briefly take you to understand its basic use.

An overview of Conditional

@Conditional is an annotation, and if we look at the annotation declaration, it can receive an array of conditions.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

Copy the code
@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
Copy the code

Condition is a functional interface that provides a matchers method. In short, it provides a matching rule that returns true to indicate that beans can be injected and false to indicate that beans cannot be injected.

Conditional ‘

  • If the current operating system is Windows, return true, otherwise return false

    public class GpCondition implements Condition{
        @Override
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
            If true is returned, the configuration class or Bean needs to be loaded
            // Otherwise, the load is not loaded
            String os=conditionContext.getEnvironment().getProperty("os.name");
            if(os.contains("Windows")) {return true;
            }
            	return false; }}Copy the code
  • Create a configuration class that loads a BeanClass

    @Configuration
    public class ConditionConfig {
        @Bean
        @Conditional(GpCondition.class)
        public BeanClass beanClass(a){
       	 	return newBeanClass(); }}Copy the code
  • Add @Conditional(gpCondition.class) to BeanClass’s bean declaration method, where the specific condition is our custom GpCondition class. What the above code means is that if Matchs in the GpCondition class returns true, the BeanClass is loaded into the Spring IoC container

  • Run test method

    public class ConditionMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context=newAnnotationConfigApplicationContext(ConditionConfig.class); BeanClass beanClass=context.getBean(BeanClass.class); System.out.println(beanClass); }}Copy the code

conclusion

After an overall analysis of Spring’s annotation drive, it is not difficult to find that the Spring team’s efforts to address user pain points are the reason why we can easily implement a large number of Spring features based on annotations. Spring Boot’s autowiring mechanism is also an evolution of Spring’s annotation driver, and I’ll focus on the autowiring mechanism of Spring Boot in a future article.

Copyright Notice: All articles on this blog are subject to a CC BY-NC-SA 4.0 license unless otherwise stated. Reprint please specify from Mic to take you to learn structure! If this article is helpful to you, please also help to point attention and like, your persistence is the power of my continuous creation. Welcome to pay attention to the same wechat public account for more technical dry goods!