0%

springboot(二)——starter组件

Spring配置方式

第一阶段:xml配置

    在spring 1.x时代,使用spring开发满眼都是xml配置的bean,随着项目的扩大,
我们需要把xml配置文件分放到不同的配置文件中,那时候需要频繁地在开发的类和配置文件间切换。

第二阶段:注解配置

在spring 2.x时代,随着JDK1.5带来的注解支持,spring提供了声明bean的注解,
大大减少了配置量。这时spring圈子存在一种争论:注解配置和xml配置究竟哪个更好?我们最终的选择是应用
的基本配置用xml,业务配置用户注解。

第三阶段:Java配置(java config)

从spring 3.x到现在,spring提供了Java配置的能力,使用Java配置更好的理解
配置的bean。spring 4.x和spring boot都推荐使用Java配置。

Spring IOC有一个非常核心的概念——Bean。由Spring容器来负责对Bean的实例化,装配和管理。XML是用来描述Bean最为流行的配置方式。但随着Spring的日益发展,越来越多的人对Spring提出了批评。“Spring项目大量的烂用XML”就是最为严励的一个批评。由于Spring会把几乎所有的业务类都以Bean的形式配置在XML文件中,造成了大量的XML文件。使用XML来配置Bean失去了编译时的类型安全检查。大量的XML配置使得整个项目变得更加复杂。

随着JAVA EE 5.0的发布,其中引入了一个非常重要的特性——Annotations(注释)。注释是源代码的标签,这些标签可以在源代码层进行处理或通过编译器把它熔入到class文件中。在JAVA EE5以后的版本中,注释成为了一个主要的配置选项。Spring使用注释来描述Bean的配置与采用XML相比,因类注释是在一个类源代码中,可以获得类型安全检查的好处。可以良好的支持重构。

SpringBoot 配置方式

当我们构建完Spring Boot项目后,会在resources目录下给我们一个默认的全局配置文件 application.properties,这是一个空文件,因为Spring Boot在底层已经把配置都给我们自动配置好了,当在配置文件进行配置时,会修改SpringBoot自动配置的默认值。

配置文件名是固定的

​ application.properties

但我们可以修改为

​ application.yml

这两个文件本质是一样的,区别只是其中的语法略微不同。

自定义配置

1
@ConfigurationProperties(prefix = "person")

@ConfigurationProperties 注解向Spring Boot声明该类中的所有属性和配置文件中相关的配置进行绑定。

  • prefix = “person”:声明配置前戳,将该前戳下的所有属性进行映射。

@Component 或者@Configuration:将该组件加入Spring Boot容器,只有这个组件是容器中的组件,配置才生效。

配置文件加载位置

springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件

  • –file:./config/
  • –file:./
  • –classpath:/config/
  • –classpath:/

优先级由高到底,高优先级的配置会覆盖低优先级的配置;SpringBoot会从这四个位置全部加载主配置文件。、

​ 项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;

自动装配

自动装配主要由@EnableAutoConfiguration实现,添加了@EnableAutoConfiguration注解,会导入AutoConfigurationImportSelector类,里面的selectImports方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有含有META-INF/spring.factoriesjar包,将对应key@EnableAutoConfiguration注解全名对应的value类全部装配到IOC容器中。

相比较于传统的 Spring 应用,搭建一个 SpringBoot 应用,我们只需要引入一个注解 @SpringBootApplication,就可以成功运行。

核心注解 SpringBootApplication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
<1.>@SpringBootConfiguration
<2.>@ComponentScan
<3.>@EnableAutoConfiguration
public @interface SpringBootApplication {

}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //实际上它也是一个配置类
public @interface SpringBootConfiguration {
}

大概可以把 @SpringBootApplication看作是 @Configuration@EnableAutoConfiguration@ComponentScan 注解的集合。根据 SpringBoot 官网,这三个注解的作用分别是:

  • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
  • @Configuration:允许在上下文中注册额外的 bean 或导入其他配置类
  • @ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。

@SpringBootConfiguration 注解

这个注解我们点进去就可以发现,它实际上就是一个 @Configuration 注解,这个注解大家应该很熟悉了,加上这个注解就是为了让当前类作为一个配置类交由 Spring 的 IOC 容器进行管理,因为前面我们说了,SpringBoot 本质上还是 Spring,所以原属于 Spring 的注解 @Configuration 在 SpringBoot 中也可以直接应用。

@ComponentScan 注解

这个注解也很熟悉,用于定义 Spring 的扫描路径,等价于在 xml 文件中配置 context:component-scan,假如不配置扫描路径,那么 Spring 就会默认扫描当前类所在的包及其子包中的所有标注了 @Component,@Service,@Controller 等注解的类。

@EnableAutoConfiguration:实现自动装配的核心注解

EnableAutoConfiguration 只是一个简单地注解,自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector类。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //作用:将main包下的所欲组件注册到容器中
@Import({AutoConfigurationImportSelector.class}) //加载自动装配类 xxxAutoconfiguration
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

Class<?>[] exclude() default {};

String[] excludeName() default {};
}

我们现在重点分析下AutoConfigurationImportSelector 类到底做了什么?

AutoConfigurationImportSelector:加载自动装配类

AutoConfigurationImportSelector类的继承体系如下:

1
2
3
4
5
6
7
8
9
10
11
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

}

public interface DeferredImportSelector extends ImportSelector {

}

public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
}

可以看出,AutoConfigurationImportSelector 类实现了 ImportSelector接口,也就实现了这个接口中的 selectImports方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中

1
2
3
4
5
6
7
8
9
10
11
12
13
private static final String[] NO_IMPORTS = new String[0];

public String[] selectImports(AnnotationMetadata annotationMetadata) {
// <1>.判断自动装配开关是否打开
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
//<2>.获取所有需要装配的bean
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}

这里我们需要重点关注一下getAutoConfigurationEntry()方法,这个方法主要负责加载自动配置类的。

原理流程汇总

从上面查看的源码,可以知道Spring Boot自动配置主要是@EnableAutoConfiguration实现的,@EnableAutoConfiguration注解导入AutoConfigurationImportSelector类,通过selectImports方法调用SpringFactoriesLoader.loadFactoryNames()扫描所有含有META-INF/spring.factories文件的jar包,将spring.factories文件中@EnableAutoConfiguration对应的类注入到IOC容器中。

这些属性自动配置到IOC之后就无需自己手动配置bean了,Spring Boot中的约定大于配置理念,约定是将需要的配置以约定的方式添加到IOC容器中。

自动配置生效条件

那是不是spring.factories文件对应的配置都会加载到IOC容器中?比如下面的Kafka自动配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration
@ConditionalOnClass(KafkaTemplate.class)
@EnableConfigurationProperties(KafkaProperties.class)
@Import({ KafkaAnnotationDrivenConfiguration.class, KafkaStreamsAnnotationDrivenConfiguration.class })
public class KafkaAutoConfiguration {

private final KafkaProperties properties;

private final RecordMessageConverter messageConverter;

public KafkaAutoConfiguration(KafkaProperties properties, ObjectProvider<RecordMessageConverter> messageConverter) {
this.properties = properties;
this.messageConverter = messageConverter.getIfUnique();
}

@Bean
@ConditionalOnMissingBean(KafkaTemplate.class)
public KafkaTemplate<?, ?> kafkaTemplate(ProducerFactory<Object, Object> kafkaProducerFactory,
ProducerListener<Object, Object> kafkaProducerListener) {
....
}

其中有几个注解:

1
2
@ConditionalOnClass
@ConditionalOnMissingBean
  • @ConditionalOnClass表示在类路径中存在类才会配置该配置类。只有引入相关依赖才会自动配置该配置类。
  • @ConditionalOnMissingBean表示只有不存在对应的类的bean才会自动配置该类。

所以spring.factories里面并不是所有的bean都会装配到IOC容器中,只会按需配置对应的bean

总结

Spring Boot 自动装配原理

  1. @EnableAutoConfiguration注解导入AutoConfigurationImportSelector类,开启自动配置(@SpringBootApplication注解默认已包含)。

  2. 自动加载类路径下META-INF/spring.factories文件,读取以EnableAutoConfiguration的全限定类名对应的值,作为候选配置类。这里默认给出了很多组件的自动配置类。

    • 执行selectImports方法调用SpringFactoriesLoader.loadFactoryNames()扫描所有jar下面的对应的META-INF/spring.factories文件.
    • 限定为@EnableAutoConfiguration对应的value,将这些装配条件的装配到IOC容器中。
  3. 自动配置类可能会再导入一些依赖(比如@Import),或者给出一些配置条件,并且会通过@Bean注解把该组件所包含的组件注入到spring容器中以供使用。

  4. 自动配置类还可能会绑定xxxProperties配置文件类,该类又会和应用程序中的application.properties中的指定前缀绑定。第3步注入组件的时候,组件可能还会获取配置文件类中的内容,所以用户可以在application.properties修改指定配置,来制定自己的个性化服务。

自动装配简单来说就是自动将第三方的组件的bean装载到IOC容器内,不需要再去写bean相关的配置,符合约定大于配置理念。

Spring Boot基于约定大于配置的理念,配置如果没有额外的配置的话,就给按照默认的配置使用约定的默认值,按照约定配置到IOC容器中,无需开发人员手动添加配置,加快开发效率。

starter

starter会把所有用到的依赖包都包含进来,避免开发者自己去引入依赖所带来的麻烦。

虽然不同的starter实现起来各有差异,但是他们基本上都会使用到两个相同的内容:ConfigurationPropertiesAutoConfiguration

img

Starter是一组可以让你很方便的在应用增加的依赖关系描述符的集合。或者可以这样理解,平时我们开发的时候很多情况下都会有一个模块依赖另一个模块,这个时候我们一般都是采用maven的模块依赖,进行模块的依赖,但是这种情况下我们完全可以采用Starter的方式,将需要被依赖的模块用Starter的方式去开发,最后直接引入一个Starter也可以达到这样的效果。

命名规则

由于SpringBoot官方本身就提供了很多Starter,为了区别那些是官方的,哪些是第三方的,所以SpringBoot官方提出:

  • 官方定义的starter组件命名一般为spring-boot-starter-xxx
  • 自定义starter组件命名一般为xxx-spring-boot-starter

总结自定义starter的主要流程和注意事项:

  1. 规范是定义一个starter模块和autoconfigure模块,starter模块依赖autoconfigure模块,并提供启动所需的依赖。starter模块不提供服务。
  2. 在命名上,一般是前缀-spring-boot-starter前缀-spring-boot-autoconfigure,比如mybatis-spring-boot-starter
  3. 可以编写xxxProperties配置文件,按照自己的需求,提供可自定义的配置选项。
  4. autoconfigure模块中编写自己的服务类,可以导入配置文件并使用其中的配置。
  5. 自动配置类要标注@Configuration注解,用@EnableConfigurationProperties绑定配置文件,可按照自己的需求,给出自动配置的条件、时机、顺序等。用@Bean把自己所要暴露的服务类提供出来,让其注入到Spring容器中。
  6. 新建META-INF/spring.factories文件,把自己的配置类全类名绑定到Spring Boot自动配置类的全类名上。如下所示:
1
2
3
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.novalue.starter.MyServiceAutoConfiguration
  1. 打包发布两个模块。