一步步实现spring-boot-starter

导言

本文通过一步一步的实现一个自定义的spring-boot-starter来了解spring-boot的内部机制,读者最好把示例项目learnstepbystep-spring-boot-starter 克隆下来,跟随文章进行实践,希望这篇文章会让你有所收获。
下面进入正题。

实现一个自己的Spring boot starter

使用过spring boot的都知道,想要应用spring boot中的功能非常简单,你只需要导入对应的starter依赖,标注一个@Enable注解,在配置文件中加入对应的配置项,大多数至多三步就可以让这个功能生效。这一节我们将带领大家一步一步的实现一个starter来掌握其内部机制。

预备知识:三个模块

一个starter主要由三个模块构成。

1.base function模块

这个模块包要包含基本的功能。

2.autoconfigure模块

这个模块的主要作用是对我们的基本功能进行自动配置并且指定生效条件,将我们的功能整合入spring boot。这里我们可以从一个注解讲起,这个注解就是@Conditional

@Conditional注解

这个注解可以按照一定的条件注册bean,其子注解有
@ConditionalOnClass:存在某class时生效
@ConditionalOnBean:容器中存在某个bean时生效
@ConditionalOnMissingBean:容器中不存在某个bean时生效
@ConditionalOnProperty:存在某个配置项时生效
等等。

3.starter模块

这个模块主要是提供给使用者一个友好的依赖,隐藏配置的复杂性。

实践

1.base function实现

我的示例项目:learnbystep-spring-boot-starter 该项目按照学习的步骤打了标签,每个标签都是编译通过可运行的,可以按顺序检出,就可以看到整个项目的变化过程。
下面我们使用 spring aop 中的 cglib 来做一个简单的动态代理功能。
其中最为关键的实现如下

public class AopCreater implements BeanPostProcessor{
    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        Class<?> aClass = bean.getClass();
        WithAspect annotation = aClass.getAnnotation(WithAspect.class);
        if(null != annotation){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(aClass);
            enhancer.setCallback(new InvocationHandler() {
                @Override
                public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                    System.out.println("aop before");
                    try{
                        Object invoke = method.invoke(bean, objects);
                        System.out.println("aop after return");
                        return invoke;
                    }catch (Exception e){
                        System.out.println("aop after throw");
                        throw e;
                    }
                }
            });
            return enhancer.create();
        }
        return bean;
    }
}

很简单,在spring容器初始化时,使用cglib对注解WithAspect标注的bean的所有方法进行代理,在before,after return和after throw时分别打印一句话。
克隆示例项目,检出标签 step1-base-function,运行MyaopApplicationTests的测试方法可看到效果。

2.创建模块

检出标签 step2-modules
这一步把我们的基本功能模块抽离出来放到一个module里边,把测试用例单独放到专门的test module中,同时创建好autoconfigure和starter的空module,一共四个module。
对module的命名spring官方推荐使用*-spring-boot-autoconfigure这种方式,不建议以spring-boot开头,因为以spring-boot开头可能会和spring官方的命名产生冲突。
(把这个挪到后边)指定aop-base-function中的依赖为可选依赖(Optional Dependencies),即这个依赖不会被继承,这样在autoconfigure中使用@ConditionalOnClass这种注解时才有意义,如果不使用可选依赖我们在base-function中的所有功能都会生效。

3.autoconfigure实现

自动配置模块包括两种配置:1.property配置,2.bean配置,3.使我们的自动配置类生效
property配置
@ConfigurationProperties标注的类可以绑定外部配置项(如properties,yml等),配置项为:prefix.属性名

@ConfigurationProperties(prefix = MyAopProperties.PREFIX)
public class MyAopProperties {
    public static final String PREFIX = "myaop";
    private boolean enable;

    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }
}

bean配置
在@Configuration中定制配置规则(bean的生效前提,注入顺序等)使用@EnableConfigurationProperties来使你的绑定生效

@Configuration
@ConditionalOnClass({Enhancer.class})//当依赖中存在Enhancer时,这个配置类才生效
@EnableConfigurationProperties(MyAopProperties.class)//使绑定的外部配置生效
public class MyAopAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
        //在没有这个bean时才注入,比如,你想要对AopCreater里的行为做修改,你可以自己注入一个AopCreater,这样这个bean就不会被注入
    AopCreater aopCreater(){
        AopCreater aopCreater = new AopCreater();
        return aopCreater;
    }
}

到这里其实我们的自动配置已经做完了,但是有个问题,在我们在编写一个springboot应用时,会指定扫描注解的包(默认@SpringBootApplication所在包和其子包下),使用注解配置的bean只有来这个包下才会被加入上下文,按照这一做法如果我们想应用别的jar包里的@Configuration,就一定要配置扫描它所在的包。
检出标签step3-validate-autoconfigure-by-specifying-package,这个版本采用了这种做法,启动类的代码扫描了autoconfigre包,代码如下

@SpringBootApplication(scanBasePackages = {"com.fxp.myaop.spring.boot.autoconfigure","com.fxp.myaop.test"})
public class MyaopApplication {

	public static void main(String[] args) {
		SpringApplication.run(MyaopApplication.class, args);
	}
}

如果真的这样做,设想假如我们依赖了10个自动配置的jar包,那岂不是要配置10个不通的包进来,这样的坏处可真是说的说不尽。
幸好spring的开发人员也考虑到了这个问题,在这里引入了
一个配置文件META-INF/spring.factories:定义了要注入到上下文的configuration类,格式如下;

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.fxp.myaop.spring.boot.autoconfigure.MyAopAutoConfiguration

一个注解@EnableAutoConfiguration:从AutoConfigurationMetadata中读取定义好的类进行实际的注入。
这样就实现了,只要导入jar包就可以自动注入相应的bean。
检出标签step4-define-autoconfiguration-metadata查看实现。
最后关于autoconfigure模块还有个小知识点,在这个模块的pom文件中可以发现这样一个配置

        <!-- @ConfigurationProperties annotation processing (metadata for IDEs) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

这个是用来自动向jar里写一些数据(metadata),这些数据会被IDE读取,用来在properties中支持自动补全提示功能(赞赞的)。

4.starter实现

这个模块是最简单的模块了,引用spring boot官网文档的一句话:The starter is really an empty jar.
我们前面说过starter模块是用来专门向外部提供依赖的,我们只需要保留这个module中的pom文件即可。
其中的内容至少要有
1.spring-boot-starter
2.autoconfigure模块
3.base function模块
4.autoconfigure,base function这两个模块的依赖,如果这些依赖有也直接或间接继承了spring-boot-starter,则不必再声明spring-boot-starter的依赖(即可以没有1)。
标签step5-starter-ok

这一篇到这里就告一段落了,如果有什么问题或文章有什么不好的地方请在评论中提出,大家互相学习,感激不尽。

最后欢迎来关注我的微信小专栏
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值