首先,我们要手写一个Spring-Boot-Starter,就必须了解与之相关的一些知识,而SpringBoot中对于这一块没有什么新技术,如果你的Spring学的足够好则学这里会非常轻松,相反则需要细心耐心的去学习。
SpringBootApplication本质上是由下面三个注解组成的复合注解,相信只要用过SpringBoot都见过它一面。至于了不了解就不一定咯,如果你不了解请跟随我一起去探究一下它的原理吧。
SpringBootConfiguration本质上又由Configuration组成,相信对于Configuration大家并不陌生,以前都是用applicationContext.xml来管理bean,如
<bean name="xxx" class="xxxxx"/>
在Spring3以后开始支持JavaConfig,可实现无配置化(即省去applicationContext.xml),写个栗子:
/***
* 在以前想要将这个类给Spring托管,则是这样的
*
*/
public class DemoClass {
public void say(){
System.out.println("say Hello");
}
}
@Configuration
public class ConfigurationDemo {
@Bean
public DemoClass demoClass(){
return new DemoClass();
}
}
DemoClass.java和ConfigurationDemo结合起来等价于在配置文件里配置了这一行
<bean name="demoClass" class="com.lele.springboot.firstDemo.DemoClass" />
ComponentScan大家可能更熟悉了,根据上面代码修改一下,自己对照即可
/***
* 在以前想要将这个类给Spring托管,则是这样的
*
*/
@Component
public class DemoClass {
public void say(){
System.out.println("say Hello");
}
}
@Configuration
@ComponentScan
public class ConfigurationDemo {
}
这样和上面代码效果一样
这个可能接触的比较少,我将会详细介绍这个
它主要是由@AutoConfigurationPackage、@Import(AutoConfigurationImportSelector.class)组成
Import简单写个栗子,若会则直接跳过这个栗子
1.SpringConfig.java
@Configuration
@Import(OtherConfiguration.class)
public class SpringConfig {
@Bean
public DefultBean defultBean(){
return new DefultBean();
}
}
2.OtherConfiguration
@Configuration
public class OtherConfiguration {
@Bean
public OtherBean otherBean(){
return new OtherBean();
}
}
简单的讲就是可以导入其他的配置类,使单例池内有导入配置类中配置的Bean;
栗子
写个栗子你们看一下自己对着我的代码敲一下
public class CacheImportSelector implements ImportSelector {
@Override
public String[] selectImports( AnnotationMetadata annotationMetadata) {
final Map<String,Object> attributes = annotationMetadata.getAnnotationAttributes(EnableDefineService.class.getName());
final Object exclude = attributes.get("exclude");
if (exclude == CacheService.class) {
return new String[]{
LoggerService.class.getName()
};
}else if (exclude == LoggerService.class){
return new String[]{
CacheService.class.getName()
};
}
return new String[]{
CacheService.class.getName(),LoggerService.class.getName()
};
}
}
public class CacheService {
}
public class LoggerService {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(CacheImportSelector.class)
public @interface EnableDefineService {
Class<?> exclude() ;
}
@EnableDefineService(exclude = CacheService.class)
@SpringBootApplication
public class EnableDemoMain {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(EnableDemoMain.class, args);
System.out.println(run.getBean(CacheService.class));
}
}
上面的整个代码主要是为了熟悉ImportSelector,如何去使用,懂得如何去根据条件去导入Bean,可以自己多去理解体会。
其本质由@Import(AutoConfigurationPackages.Registrar.class)组成
写个栗子带你了解其原理
public class LoggerDefinitonRegister implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Class beanClass = LoggerService.class;
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(beanClass);
final String beanName = StringUtils.uncapitalize(beanClass.getSimpleName());
registry.registerBeanDefinition(beanName,rootBeanDefinition);
}
}
无论是实现ImportBeanDefinitionRegistrar 或实现 ImportSelector 都可以完成对Bean的生成,所以到现在你是不是有点感觉了
二、SPI
上图可以自行去查看代码,讲解一下原理,主要是将约定路径下的文件下的key取出所有value来,通过筛选将其new出来
若想扩展自己的也可以在classpath下新建一个META-INF/spring.factories文件,将org.springframework.boot.autoconfigure.EnableAutoConfiguration其赋值为自己的配置文件,则可自动装配
================================================================================================
在spring-autoconfigure-metadata.properties中若配置了红色框圈出来的则表示当等号后面的类存在时才加载当前类
也可在classpath下的META-INF/spring-autoconfigure-metadata.properties添加需要的条件
本篇讲解完毕,下一篇将会手写一个Spring-Boot-Starter,请认真阅读本篇博客,对于下一篇的理解有一定的帮助