3

图怪兽_40b0b39fdbb3d29e40b4b52282058329_23747

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!

跟着Mic学架构
810 声望1.1k 粉丝

《Spring Cloud Alibaba 微服务原理与实战》、《Java并发编程深度理解及实战》作者。 咕泡教育联合创始人,12年开发架构经验,对分布式微服务、高并发领域有非常丰富的实战经验。