阅读 322

SpringBoot 自定义starter实现

SpringBoot的基础介绍

SpringBoot是由Pivotal团队提出的全新框架,其出现的目的主要是为了简化Spring应用的搭建;

在使用SpringBoot之前,我们在使用Spring搭建项目时通常需要写一些xml对项目进行配置,通常还需要手动添加maven配置,框架搭建难度大,时间成本高;在对项目进行本地调试或者线上部署的时候,通常需要将项目编译成war包,然后部署到tomcat中;

SpringBoot的出现,改变了这种状况,SpringBoot具有自动装配的特性,另外SpringBoot内嵌容器,使我们免于安装Tomcat,直接通过运行main方法进行启动,非常方便;

提起SpringBoot的自动装配,我们不免想起在pom.xml中引用的spring-boot-starter-data-redis,spring-boot-starter-data-mongodb等,对于之前项目一直使用Spring的我来说,感觉非常的神奇,一个简单的配置竟然让spring整合redis,mongodb如此简单,所以我今天花了一些时间去学习了starter相关的知识,学习如何自定义starter;

自定义starter

首先我们需要创建一个Configuration类,这个类主要是为了配置参数,以及自动配置对外提供的功能;

@Configuration
@EnableConfigurationProperties(CourseProperties.class)
public class CourseAutoConfigure {

    @Bean
    public CourseClient courseClient(CourseProperties courseProperties){
        return new CourseClient(courseProperties);
    }

}
复制代码

上面代码表示我们对外提供一个CourseClient的对象,然后我们可以通过这个对象去实现一些功能,因为我们这里只是为了学习自定义starter的创建方式,所以我们肯定使用一些简单的demo去理解这个过程。


public class CourseClient {

    private CourseProperties courseProperties;

    public CourseClient(){

    }

    public CourseClient(CourseProperties courseProperties){
        this.courseProperties = courseProperties;
    }

    public String getName(){
        return courseProperties.getName();
    }

}

复制代码

可以看到,我们对外暴露的功能就是我们CourseProperties对象的课程名称;

@Data
@ConfigurationProperties(prefix = "spring.course")
public class CourseProperties {

    private String name;
    
}
复制代码

细心的小伙伴平时在使用SpringBoot的时候肯定注意到我们在yaml中或则properties写一些配置的时候idea总是会给我们一些提示,其实这个实现很简单啦,我们只需要在pom.xml中引入

 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
复制代码

它的作用就是会解析我们的Properties类,然后根据属性,注释,返回值等,自动解析生成一个spring-configuration-metadata.json,帮助我们理解我们要做的功能,配置的意义;

starter集成应用

我们的功能写好了,那么我们怎么集成到我们的项目当中呢?这里分为两种方式,一种是主动生效,一种是被动生效;

主动生效是指:需要我们主动声明启用该starter才能生效,这里主要是用到@Import注解,将该注解标记到自定义的@Enable注解上;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({CourseAutoConfigure.class})
public @interface EnableCourseClient {
}
复制代码

被动生效是指:将starter组件集成到SpringBoot应用时就已经被应用可用。实现方面类似java的spi机制,新建META-INF/spring.factories写入

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.learn.springboot.starter.autoconfigure.CourseAutoConfigure
复制代码

集成应用测试

我们新建一个SpringBoot web应用,首先我们使用主动生效的方式进行测试的

@EnableCourseClient
@SpringBootApplication
public class Application {

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

}
复制代码
@RestController
public class CourseController {

    @Autowired
    private CourseClient courseClient;

    @GetMapping("courseName")
    public String getCourseName(){
        return courseClient.getName();
    }

}
复制代码

在application.properties中配置

spring.course.name=python
复制代码

浏览器访问:localhost:8080/courseName 可以正常返回python

如果我们去掉@EnableCourseClient注解,启动的时候会报错

2021-05-08 09:51:53.924  INFO 58146 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-05-08 09:51:53.944 ERROR 58146 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field courseClient in com.learning.springboot.controller.CourseController required a bean of type 'com.learn.springboot.starter.autoconfigure.CourseClient' that could not be found.

The injection point has the following annotations:
	- @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.learn.springboot.starter.autoconfigure.CourseClient' in your configuration.

复制代码

下面我们使用被动生效的方式进行测试,去掉@EnableCourseClient注解,同时在META-INF/spring.factories写入

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.learn.springboot.starter.autoconfigure.CourseAutoConfigure
复制代码

浏览器访问:localhost:8080/courseName 可以正常返回python

其他补充

有时候我们想通过配置,动态控制starter是否可用,这个时候我们可以通过@Condition注解实现,比如

@Bean
@ConditionalOnProperty(prefix = "spring.course", value = "enabled", havingValue = "true")
public CourseClient courseClient(CourseProperties courseProperties){
    return new CourseClient(courseProperties);
}
复制代码

配置如下:

spring.course.name=python
spring.course.enabled=true
复制代码

如果将true改为false,发现启动同样也会出现报错

学习过程中遇见的问题

在学习过程中,发现既不使用主动生效也不使用被动生效,starter都可以正常使用,主要因为starter项目的包名取得和应用包名一样,更改包名后恢复正常。

文章分类
后端
文章标签