starter作用
springBoot starter基于约定大于配置思维,应用spi机制及主动拆卸原理,能够将一些通用的性能可能封装成一个独立组件并很不便的集成到不同的我的项目外面,简化开发,晋升代码复用能力。
springBoot在配置上相比spring要简略许多, 其外围在于starter的设计, 在应用springBoot来搭建一个我的项目时, 只须要引入官网提供的starter, 就能够间接应用, 免去了各种配置。starter简略来讲就是引入了一些相干依赖和一些初始化的配置。
约定大于配置:
- 对于大部分罕用状况,极简短的配置即可应用
- 对于少部分的略非凡状况,大量的配置即可应用
- 对于极为非凡的定制化需要,能够通过各选项手动配置实现
约定能够缩小很多配置,比如说在maven的构造中:
/src/main/java目录用来寄存java源文件
src/main/resources目录用来寄存资源文件,如application.yml文件
/src/test/java目录用来寄存java测试文件
/src/test/resources目录用来寄存测试资源文件
/target目录为我的项目的输入地位
- springBoot的理念就是约定大于配置,这一点在各种starter外面尤其体现的酣畅淋漓。在springBoot中提供了一套默认配置,不须要手动去写xml配置文件,只有默认配置不能满足咱们的需要时,才会去批改配置。相比于晚期的spring须要编写各种xml配置文件,starter极大的缩小了各种简单的配置
starter命名
spring官网提供了很多starter,第三方也能够定义starter。为了加以辨别,starter从名称上进行了如下标准:
- spring官网starter通常命名为 spring-boot-starter-{name},如spring-boot-starter-web
- spring官网倡议非官方starter命名应遵循 {name}-spring-boot-starter的格局,例如由mybatis提供的mybatis-spring-boot-starter
starter原理
mybatis-spring-boot-starter来阐明主动配置的实现过程
- 依赖
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis-spring-boot.version}</version> </dependency>
- 主动配置类
@Configuration
注解的类能够看作是能生产让Spring IoC容器治理的Bean实例的工厂。
@Configuration和@Bean
这两个注解一起应用就能够创立一个基于java代码的配置类,能够用来代替传统的xml配置文件。
@Bean
注解的办法返回的对象能够被注册到spring容器中。上面的MybatisAutoConfiguration这个类,主动帮咱们生成了SqlSessionFactory和SqlSessionTemplate这些Mybatis的重要实例并交给spring容器治理。
@EnableConfigurationProperties(MybatisProperties.class)
能够将mybatis的配置信息注入到MybatisProperties对应的bean实例外面。
@ConditionalOnClass,@ConditionalOnBean代表主动拆卸的条件
要实现Mybatis的主动配置,须要在类门路中存在SqlSessionFactory.class、SqlSessionFactoryBean.class这两个类,同时须要存在DataSource这个bean且这个bean实现主动注册。
@org.springframework.context.annotation.Configuration@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })@ConditionalOnBean(DataSource.class)@EnableConfigurationProperties(MybatisProperties.class)@AutoConfigureAfter(DataSourceAutoConfiguration.class)public class MybatisAutoConfiguration { private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class); private final MybatisProperties properties; private final Interceptor[] interceptors; private final ResourceLoader resourceLoader; @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setVfs(SpringBootVFS.class); if (StringUtils.hasText(this.properties.getConfigLocation())) { factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); } ... if (!ObjectUtils.isEmpty(this.interceptors)) { factory.setPlugins(this.interceptors); } if (this.databaseIdProvider != null) { factory.setDatabaseIdProvider(this.databaseIdProvider); } if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) { factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); } if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); } if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { factory.setMapperLocations(this.properties.resolveMapperLocations()); } return factory.getObject(); }
- 配置属性
@ConfigurationProperties把yml或者properties配置文件中的配置参数信息封装到ConfigurationProperties注解标注的bean里,个别联合@EnableConfigurationProperties注解应用
/** * Configuration properties for MyBatis. */@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)public class MybatisProperties { public static final String MYBATIS_PREFIX = "mybatis"; /** * Location of MyBatis xml config file. */ private String configLocation; /** * Locations of MyBatis mapper files. */ private String[] mapperLocations; /** * Packages to search type aliases. (Package delimiters are ",; \t\n") */ private String typeAliasesPackage; /** * Packages to search for type handlers. (Package delimiters are ",; \t\n") */ private String typeHandlersPackage; ... /** * A Configuration object for customize default settings. If {@link #configLocation} * is specified, this property is not used. */ @NestedConfigurationProperty private Configuration configuration; ... }
- starter里Bean的发现与注册
META-INF目录下的spring.factories文件
# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
spring boot默认扫描启动类所在的包下的主类与子类的所有组件,但并没有包含依赖包中的类,那么依赖包中的bean是如何被发现和加载的?
咱们须要从Spring Boot我的项目的启动类开始跟踪,在启动类上咱们个别会退出SpringBootApplication注解,此注解的源码如下:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication { /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ @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 {}; @AliasFor(annotation = Configuration.class) boolean proxyBeanMethods() default true;}
SpringBootConfiguration:
作用就相当于Configuration注解,被注解的类将成为一个bean配置类
ComponentScan:
作用就是主动扫描并加载符合条件的组件,最终将这些bean加载到spring容器中
EnableAutoConfiguration:
这个注解是要害,会引入@Import注解,借助@Import的反对,收集和注册依赖包中相干的bean定义
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ Class<?>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ String[] excludeName() default {};}
@Import:导入须要主动配置的组件,此处为AutoConfigurationImportSelector这个类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { ... /** * Return the auto-configuration class names that should be considered. By default * this method will load candidates using {@link SpringFactoriesLoader} with * {@link #getSpringFactoriesLoaderFactoryClass()}. * @param metadata the source metadata * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation * attributes} * @return a list of candidate configurations */ protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), 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; } ...}
getCandidateConfigurations里的SpringFactoriesLoader的loadFactoryNames静态方法能够从所有的jar包中读取META-INF/spring.factories文件,而主动配置的类就在这个文件中进行配置,从而加载在starter外面的主动配置类
在SpringBoot利用中要让一个一般类交给Spring容器治理,通常有以下办法:
1、应用 @Configuration与@Bean 注解
2、应用@Controller @Service @Repository @Component 注解标注该类并且启用@ComponentScan主动扫描
3、应用@Import办法
其中SpringBoot实现主动配置应用的是@Import注解这种形式,AutoConfigurationImportSelector类的selectImports办法返回一组从META-INF/spring.factories文件中读取的bean的全类名,这样SpringBoot就能够加载到这些Bean并实现实例的创立工作。
SPI机制
SPI(Service Provider Interface)是一种服务提供发现机制,能够用来启用框架扩大和替换组件,次要用于框架中开发,例如Dubbo、Spring、Common-Logging,JDBC等采纳采纳SPI机制,针对同一接口采纳不同的实现提供给不同的用户,从而进步了框架的扩展性。
Java内置的SPI通过java.util.ServiceLoader类应用load办法解析classPath和jar包的META-INF/services/目录 下的以接口全限定名命名的文件,并加载该文件中指定的接口实现类,以此实现调用。
Spring SPI沿用了Java SPI的设计思维,Spring采纳的是spring.factories形式实现SPI机制,能够在不批改Spring源码的前提下,提供Spring框架的扩展性。如主动拆卸外面获取主动配置的各种实现类,starter外面的spring.factories文件里的内容都是某某注解类型(接口)=对应的实现类。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
**Spring factories SPI是一个spring.factories配置文件寄存多个接口及对应的实现类,以接口全限定名作为key,实现类作为value来配置,多个实现类用逗号隔开,仅spring.factories一个配置文件。
Java SPI是一个服务提供接口对应一个配置文件,配置文件中寄存以后接口的所有实现类,多个服务提供接口对应多个配置文件,所有配置都在services目录下**
/** * Return the auto-configuration class names that should be considered. By default * this method will load candidates using {@link SpringFactoriesLoader} with * {@link #getSpringFactoriesLoaderFactoryClass()}. * @param metadata the source metadata * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation * attributes} * @return a list of candidate configurations */ protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), 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; } /** * Return the class used by {@link SpringFactoriesLoader} to load configuration * candidates. * @return the factory class */ protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
SpringFactoriesLoader.loadFactoryNames获取主动拆卸注解的全限定类名,须要传入的接口类型。这里传入的是EnableAutoConfiguration注解类型,注解实质上也是一种Interface。
总结
springBoot应用starter设计各种组件简化开发过程中很多简单的配置,把简单留给本人,简略不便给予别人,赠人玫瑰,手有余香,真不愧是java的开发框架之王,外面的设计之道值得咱们学习借鉴
参考文献:
https://zhuanlan.zhihu.com/p/...
https://mp.weixin.qq.com/s/GL...