自定义SpringBoot Starter实战

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

创建一个Maven项目

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

e6cca3bf0874f690f05ba5527bc3923c172.jpg

配置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就实践完了,不知道你理解了没有,欢迎留言交流。

464c1d4bbafe87d9e33c0e669a16af77a72.jpg

转载于:https://my.oschina.net/itsaysay/blog/3011826

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值