您现在的位置是:首页 > 文章详情

自定义SpringBoot Starter实战

日期:2019-02-19点击:495

直接干,我们先来自己制作一个SpringBoot Starter,拿又拍云的SDK来玩玩

创建一个Maven项目

这个不说了,创建完是这样一个结构

配置Pom.xml文件

<properties>         <spring-boot.version>2.0.0.RELEASE</spring-boot.version>         <upyun-sdk.version>3.16</upyun-sdk.version>     </properties>     <dependencies>         <!-- Spring Boot dependencies -->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-autoconfigure</artifactId>             <optional>true</optional>             <version>${spring-boot.version}</version>         </dependency>         <!-- @ConfigurationProperties annotation processing (metadata for IDEs) -->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-configuration-processor</artifactId>             <optional>true</optional>             <version>${spring-boot.version}</version>         </dependency>         <!-- Test Dependencies -->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-test</artifactId>             <scope>test</scope>             <version>${spring-boot.version}</version>         </dependency>         <!--upyun sdk-->         <dependency>             <groupId>com.upyun</groupId>             <artifactId>java-sdk</artifactId>             <version>${upyun-sdk.version}</version>         </dependency>    </dependencies> 

① spring-boot-configuration-processor 包的作用是编译时生成 spring-configuration-metadata.json ,此文件主要给IDE使用。如当配置此jar相关配置属性在 application.yml ,你可以用ctlr+鼠标左键点击属性名,IDE会跳转到你配置此属性的类中。
② spring-boot-autoconfigure 包包括了@ConditionalOn开头的各种注解,并且内置了一些组件的自动换配置,如freemarker,kafka等

新建Properties文件

/**  * upyun 配置文件  */ @ConfigurationProperties(prefix = "upyun") public class UpyunProperties {     /**      * 服务名称      */     private String bucketName;     /**      * 操作员账号      */     private String operId;     /**      * 操作员密码      */     private String operPass;    //....省略get,set } 

新建AutoConfiguration文件

/**  * Upyun Auto  */ @Configuration @ConditionalOnClass(UpYun.class) @EnableConfigurationProperties(UpyunProperties.class) public class UpyunAutoConfiguration {     @Resource     private UpyunProperties upyunProperties;     @Bean     @ConditionalOnMissingBean     public UpYun upYunAutoConfig(){         return  new UpYun(upyunProperties.getBucketName(),upyunProperties.getOperId(),upyunProperties.getOperPass());     } } 

@ConditionalOnClass 表示当前路径下存在指定的类,才会创建该Bean @EnableConfigurationProperties:这个注解可以提供一种方便的方式来将带有 @ConfigurationProperties 注解的类注入为 Spring 容器的 Bean。 @ConditionalOnMissingBean:当 Spring Context中不存在该Bean时,才创建Bean

创建spring.factories文件

创建resources/META-INF/spring.factories文件,Springboot将从该文件读取自动配置类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.upyun.autoconfigure.UpyunAutoConfiguration 

mvn clean install 本地打包安装 试一下
通过@Autowired 就可以引入Upyun对象

原理分析

实践完,我们来看看SpringBoot是如何加载自己的starter的
从SpringBoot Application的 run()进入源码,省略前面几个run代码,下面是主要方法

    public ConfigurableApplicationContext run(String... args) {         ...         //进入getRunListeners()方法         SpringApplicationRunListeners listeners = getRunListeners(args);         listeners.starting();         ...         listeners.running(context);         return context;     } 

在这里看到了一个getSpringFactoriesInstances()方法,看名字不就是spring.factories吗,我们在深入

    private SpringApplicationRunListeners getRunListeners(String[] args) {         Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };         return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(                 SpringApplicationRunListener.class, types, this, args));     } 

这里我们继续深入SpringFactoriesLoader.loadFactoryNames()方法

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,             Class<?>[] parameterTypes, Object... args) {         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();         // Use names and ensure unique to protect against duplicates         Set<String> names = new LinkedHashSet<>(                 SpringFactoriesLoader.loadFactoryNames(type, classLoader));         List<T> instances = createSpringFactoriesInstances(type, parameterTypes,                 classLoader, args, names);         AnnotationAwareOrderComparator.sort(instances);         return instances;     } 

这么深入了,还没到,继续深入loadSpringFactories

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {         String factoryClassName = factoryClass.getName();         return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());     } 

终于到了加载spring.factories的地方了,这里会先判断内存中是否已经存在,不存在在从META-INF/spring.factories 加载,知道路径后,SpringBoot就知道已经加载哪些类了

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {         MultiValueMap<String, String> result = cache.get(classLoader);         if (result != null)             return result;         try {             Enumeration<URL> urls = (classLoader != null ?                     classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));             result = new LinkedMultiValueMap<>();             while (urls.hasMoreElements()) {                 URL url = urls.nextElement();                 UrlResource resource = new UrlResource(url);                 Properties properties = PropertiesLoaderUtils.loadProperties(resource);                 for (Map.Entry<?, ?> entry : properties.entrySet()) {                     List<String> factoryClassNames = Arrays.asList(                             StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));                     result.addAll((String) entry.getKey(), factoryClassNames);                 }             }             cache.put(classLoader, result);             return result;         }         catch (IOException ex) {             throw new IllegalArgumentException("Unable to load factories from location [" +                     FACTORIES_RESOURCE_LOCATION + "]", ex);         }     } 

之前我们在spring.factories中已经有配置,SpringBoot就会根据配置加载我们的自定义starter了
到此,自己制作starter就实践完了,不知道你理解了没有,欢迎留言交流。

原文链接:https://my.oschina.net/itsaysay/blog/3011826
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章