Maskwang

Talk is nothing,Show me the code

  • 主页
  • 所有文章
  • 随笔
  • 关于我
所有文章 关于我 友链

Maskwang

Talk is nothing,Show me the code

  • 主页
  • 所有文章
  • 随笔
  • 关于我

Spring boot构建自定义的starter

2017-11-04

 在我们日常用springboot的开发过程中,经常会遇到使用如下的一个类来代表程序的入口类。即

1
2
3
4
5
6
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

  包括我自己,我平常在开发的过程中,并没有去重点关注spring boot的运行原理,大家都是约定俗成的这么去使用。接下的过程中,将会结合源码简单的分析下springboot运行原理。

一.Springboot 自动配置原理分析

   @SpringBootApplication注解@SpringBootApplication是一个复合注解,它包括@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解。其中最关键的莫过@EnableAutoConfiguration这个注解。在它的源码中加入了这样一个注解@Import({EnableAutoConfigurationImportSelector.class}),EnableAutoConfigurationImportSelector,它使用SpringFactoriesLoader. loadFactoryNames方法来扫描META-INF/spring.factories文件,此文件中声明了有哪些自动配置。源码如下(我挑选出重要的一部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}

  我随便查看spring-boot-autoconfigure-1.5.3.RELEASE.jar中的spring.factories,有如下的自动配置。

1
2
3
4
5
6
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\

上述spring.factories对应key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值即为启动时候需要自动配置的类。

二. 实现自定义的starter

  1. 首先定义一个基本的对象类,用来接收application.properties里面特定字段的值。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @ConfigurationProperties(prefix = "hello")
    public class HelloServiceProperties {
    private static final String MSG="world";
    private String msg=MSG;
    public String getMsg() {
    return msg;
    }
    public void setMsg(String msg) {
    this.msg = msg;
    }
    }
  • @ConfigurationProperties(prefix = "hello")是类型安全的属性获取。在application.properties 中通过hello.msg来设置,如果不设置默认就是“word”。
  1. 定义条件类(根据此类的存在与否来创建这个类的Bean,这个类可以是第三方类库的类)。
1
2
3
4
5
6
7
8
9
public class HelloService {
private String msg;
public String sayHello(){
return msg;
}
public void setMsg(String msg){
this.msg=msg;
}
}
  1. 自动配置类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Configuration //1
    @EnableConfigurationProperties(HelloServiceProperties.class)//2
    @ConditionalOnClass(HelloService.class) //3
    @ConditionalOnProperty(prefix = "hello",value = "enabled",matchIfMissing = true) //4
    public class HelloServiceAutoConfiguration {
    @Autowired
    private HelloServiceProperties helloServiceProperties;
    @Bean
    @ConditionalOnMissingBean(HelloService.class) //5
    public HelloService helloService(){
    HelloService helloService=new HelloService();
    helloService.setMsg(helloServiceProperties.getMsg());
    return helloService;
    }
    }
  • @Configuration 它告知 Spring 容器这个类是一个拥有 bean 定义和依赖项的配置类。
  • @EnableConfigurationProperties的bean可以以标准方式被注册(例如使用 @Bean 方法),即我定义HelloServiceProperties可以作为标准的Bean被容器管理。
  • @ConditionalOnClass表示该类在类路径下存在,自动配置该类下的Bean。
  • @ConditionalOnProperty当指定的属性等于指定的值的情况下加载当前配置类,在这里如果matchIfMissing如果为false,则在application.properties中必须存在hello.enable(且不能为false)
  • @ConditionalOnMissingBean()表示指定的bean不在容器中,则重新新建@Bean注解的类,并交给容器管理。

  配置好之后,我们还需要在src\main\resources下新建文件夹WEB-INF,再新建文件spring.factories里面的内容如下

1
2
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.springboot.mystartertool.HelloServiceAutoConfiguration

里面指定的类是上面自定义的那个配置类HelloServiceAutoConfiguration

  定义spring.factories的原因是因为@EnableAutoConfiguration会扫描jar包下所有spring.factories文件,从而构造自动配置类。我们使用的时候使用@Autowired注入就行。

在以上工作完成后,我们执行如下命令

1
mvn clean install

  就将项目打包到本地maven仓库中,有条件的可以安装的到私服中。

三. 应用自定义starter

  1. 首先引入自定义的starter的jar包
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!--引入我的start-->
    <dependency>
    <groupId>com.maskwang</groupId>
    <artifactId>Springboot-mystart</artifactId>
    <version>1.0-SNAPSHOT</version>
    </dependency>
    ```
    2. 当我们在`application.properties`配置如下
    ```Groovy
    hello.msg=maskwang

  我们就可以使用自定义的starter啦。

1
2
3
4
5
6
7
8
9
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@RequestMapping("/hello")
public String hello() {
return helloService.sayHello();
}
}

  由于我们没有自定义HelloService,所以会配置类会发挥作用,新建一个HelloService,并把里面的msg设置成”maskwang”。没有配置msg,则会采用默认的。结果如下

image.png

参考文献:

  1. 使用 Java 配置进行 Spring bean 管理
  2. Spring boot实战(汪云飞著)
    github地址
赏

谢谢你请我吃糖果

支付宝
微信
  • java
  • Springboot
  • Springboot

扫一扫,分享到微信

微信分享二维码
自定义注解实现Restful接口版本管理
HeadFirst设计模式4-工厂模式
© 2018 Maskwang
Hexo Theme Yilia by Litten
  • 所有文章
  • 关于我
  • 友链

tag:

  • Cookie&Session
  • HTTP/2
  • 设计模式
  • java
  • HashMap
  • LeetCode
  • Netty
  • netty
  • Springboot
  • Redis
  • websocket
  • springboot
  • web
  • Spring
  • flink
  • kafka
  • 集合
  • Java基础
  • Lambada表达式
  • HTTP
  • Zookeeper
  • 中间件比赛
  • 红黑树

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • 友情链接1
  • 友情链接2
  • 友情链接3
  • 友情链接4
  • 友情链接5
  • 友情链接6
很惭愧

只做了一点微小的工作
谢谢大家