美文网首页
Spring-boot自动装配

Spring-boot自动装配

作者: Wu杰语 | 来源:发表于2021-02-07 22:12 被阅读0次

    Spring-boot是基于Spring的一个框架,是Spring Cloud生态的基础,学习Spring应该从这个基础学习,向上学习Spring Cloud生态,向下学习Spring基础。

    Spring-boot本身是基于SpringFramework,在SpringFramework上开发出Spring的自动装配框架。正所谓原理下面有原理,在学习Spring-boot之前,最好是把SpringFramework的基础学习一下。

    Spring boot自动装配

    Spring boot的底层本质还是Spring IOC,为了加载bean, Spring boot构造了一种机制,只要按照该积木方式,可以非常容易的把各种组件很容易的引入组装起来,是一种很精妙的构思。

    要理解Spring boot的自动装配,就要先理解SpringFramework手动装配。

    模式注解

    SpringFramework中定义了若干模式注解,Configuration,相关的模式注解有@Service @Repository @Conponent,@Configuration,@Controller。

    这些模式注解中@Configuration是一级元注解,其它四个注解继承于@Configuration,是二级元注解,相当于给予了特定场景下的语义,例如说@Repository用于数据库装载,@Service用于服务装载,@Controller即MVC中的Controller。

    例如说有下面这个HellowordConfiguration

    @Configuration
    class HellowordConfiguration {
        @Bean
        public String SayHello() {
            return "hello"
        }
    }
    
    模式注解的装载

    怎么加载模式注解呢,可以使用XML配置、API接口编程、注解方式。XML配置和API接口暂且不谈,不适合Spring boot自动装配的场景,使用注解方式有两种方式:

    • 加载制定目录下的所有模式注解,使用@ComponentScan注解,使用该注解可以加载指定目录下所有的模式注解。

    • 加载指定的模式注解,使用@Import注解
      Import注解定义如下

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Import {
        Class<?>[] value();
    }
    

    使用Import注解有两种方式,例如对于HellowordConfiguration,一种是直接Import

    @Import(HellowordConfiguration.class)
    

    一种是先定义selector,然后使用Import该selector

    public class HelloWorldImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{HelloWorldConfiguration.class.getName()};
        }
    }
    
    @Import(HelloWorldImportSelector.class)
    

    selector的方式更加灵活一点,对于要加载多个Configuration,一个类就搞定了,否则就要写多个注解。

    Enable注解

    Enable注解可以理解程Enable模式,意思就是一种开关,通过开关来控制模式注解加载。
    Eanble注解在Spring中使用广泛,例如:

    • SpringFramework中,@EnableWebMVC、@EnableTransactionManagement、@EnableCaching、@EnableAsync、@EnableWebFlux、@EnableAspectJAutoProxy
    • Spring boot中,@EnableAutoConfiguration、@EnableManagementContext、@EnableConfigurationProperties
    • Spring Cloud中,@EnbleEurekaServer、@EnableEurekaClient、@EnableConfigServer、@EnableFeignClients、@EnableZuulProxy、@EnableCircuitBreaker

    Spring-boot中使用的是@EnableAutoConfiguration,其定义为

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import({AutoConfigurationImportSelector.class})
    public @interface EnableAutoConfiguration {
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        Class<?>[] exclude() default {};
    
        String[] excludeName() default {};
    }
    

    其中@AutoConfigurationPackage注解,定义加载@Import({Registrar.class}),会记载主类以及主类目录子目录下的模式注解。

    @AutoConfigurationPackage@Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import({Registrar.class})
    public @interface AutoConfigurationPackage {
    }
    

    然后再看 @Import({AutoConfigurationImportSelector.class}),这个就是一个典型的selector的使用。

      public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            } else {
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                configurations = this.removeDuplicates(configurations);
                Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                this.checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = this.filter(configurations, autoConfigurationMetadata);
                this.fireAutoConfigurationImportEvents(configurations, exclusions);
                return StringUtils.toStringArray(configurations);
            }
        }
        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
            Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
            return configurations;
        }
    

    使用了@EnableAutoConfiguration注解后,看上面的代码,会按照预定扫描所有加载jar包下面的“META-INF/spring.factories”文件,读取该文件里面的自定义的AutoConfiguration配置,这就是一种约定编程。

    关于自定义的AutoConfiguration下面再说。

    条件装配

    条件装配有两种,一种是使用在Bean上面的@Profile注解,一种是@Conditional注解。
    @Profile注解不是Spring-boot场景选择(实际上@Profile也是从@Conditional继承的),这里先不讲,重点是Conditional类的注解。

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Conditional {
    
        /**
         * All {@link Condition}s that must {@linkplain Condition#matches match}
         * in order for the component to be registered.
         */
        Class<? extends Condition>[] value();
    
    }
    

    这是@Conditonal注解的定义,然后我们看

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Documented
    @Conditional(OnSystemPropertyCondition.class)
    public @interface ConditionalOnSystemProperty {
    
        /**
         * Java 系统属性名称
         * @return
         */
        String name();
    
        /**
         * Java 系统属性值
         * @return
         */
        String value();
    }
    
    

    @ConditionalOnSystemProperty注解,这个注解在系统属性中,如果存在某些参数,就允许条件通过。
    另外看下面这个注解

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional({OnClassCondition.class})
    public @interface ConditionalOnClass {
        Class<?>[] value() default {};
    
        String[] name() default {};
    }
    
    

    这个注解是如果存在某些类,就允许条件通过。当我们在pom文件中加载上某些类包后,starter中的条件才会通过,才会记载相应的bean。

    上面两个条件是各种starter中最常用的条件注解。

    springboot的自动装配

    有了上面模式注解手动装配、Eanble模式注解、条件注解后,Spring-boot的自动装配就呼之欲出了,实际上在上面已经讲过了。

    首先我们要写自己的自动装配类,包括

    • 自定义的模式注解,HellowordConfigration
    • 自定义的Enable模式注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    //@Import(HelloWorldConfiguration.class)
    @Import(HelloWorldImportSelector.class)
    public @interface EnableHelloWorld {
    }
    
    • 自定义的AutoConfiguration
    @Configuration // Spring 模式注解装配
    @EnableHelloWorld // Spring @Enable 模块装配
    @ConditionalOnSystemProperty(name = "user.name", value = "Mercy") // 条件装配
    public class HelloWorldAutoConfiguration {
    }
    

    注意,如上都是自定义部分,这些自定义的如何工作,需要首先在约定编程中META-INFO/spring.factory中增加配置

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.imooc.diveinspringboot.configuration.HelloWorldAutoConfiguration
    

    然后在主类上加上注解@SpringBootApplication

    public @interface SpringBootApplication {
        @AliasFor(
            annotation = EnableAutoConfiguration.class
        )
        Class<?>[] exclude() default {};
    
        @AliasFor(
            annotation = EnableAutoConfiguration.class
        )
        String[] excludeName() default {};
    
        @AliasFor(
            annotation = ComponentScan.class,
            attribute = "basePackages"
        )
        String[] scanBasePackages() default {};
    
        @AliasFor(
            annotation = ComponentScan.class,
            attribute = "basePackageClasses"
        )
        Class<?>[] scanBasePackageClasses() default {};
    }
    

    这个注解上就加载了@EnableAutoConfiguration,如上Enable注解中讲述的,加载了这个注解,spring-boot启动时会扫描所有jar包下的META-INFO/spring.factories, 把刚才的HelloWorldAutoConfiguration返回,这个AutoConfiguration的加载就完成了对自定义Configuration的加载。

    所以这里要区分一下,一个自动装配的完成分为:

    • 系统级注解,@SpringBootApplication, @EnableAutoConfiguration的注解,启动扫描META-INFO/spring.factories
    • 自定义注解,如上HelloWorldAutoConfiguration,配置在META-INFO/spring.factories中
      这里有点像自举型的编程语言一样,都是用了Eanble注解定义,但是EnableAutoConfiguration用来定义系统行为,自定义的AutoConfiguration被EnableAutoConfiguration加载。

    小结

    Spring-boot是基于Spring的框架,基于这个框架,开发人员可以自己二次开发starter,使用Spring-boot框架和starter,开发人员就可以非常容易的组装出服务,摆脱Spring的繁杂构造。
    Spring-Cloud基于Spring-boot,构造了大量的Starter,可以非常方便的开箱即用,形成了一片生态。
    Spring-boot框架本身让人感觉到优美,是量体裁衣良好设计,大大降低开发人员的开发难度。

    相关文章

      网友评论

          本文标题:Spring-boot自动装配

          本文链接:https://www.haomeiwen.com/subject/bfrtxltx.html