SpringBoot編寫自定義的starter 專題


 

What’s in a name

All official starters follow a similar naming pattern; spring-boot-starter-*, where * is a particular type of application. This naming structure is intended to help when you need to find a starter. The Maven integration in many IDEs lets you search dependencies by name. For example, with the appropriate Eclipse or STS plugin installed, you can press ctrl-space in the POM editor and type “spring-boot-starter” for a complete list.

As explained in the “Creating Your Own Starter” section, third party starters should not start with spring-boot, as it is reserved for official Spring Boot artifacts. Rather, a third-party starter typically starts with the name of the project. For example, a third-party starter project called thirdpartyproject would typically be named thirdpartyproject-spring-boot-starter.

 

 

SpringBoot-創建自己的starter和autoconfiguration

(1)其實對於每個starter里面並沒有源碼,只是依賴了一個對應的autoconfiguration,這個autoconfiguration的jar包下有一個XxxxxxAutoConfiguration.class,里面配置了框架啟動需要自動注入的一些bean(默認優於配置)。
所以會自動啟動這個XxxxxxAutoConfiguration.class
(2)
在META-INF/spring.factories加上自動裝配配置;
META-INF/spring-configuration-metadata.json下配置application.properties的key-value的提示項。

Tips:這個xx-metadata.json文件生成在 target/classes/META-INF/spring-configurationmetadata.json
需要copy到xxx-Starter項目的 resources/META-INF/ 目錄下 

創建步驟
(1)實現XxxxxxAutoConfiguration.class類,並加上@Conditional限制條件.這需要實現需要自動裝載的bean
(2)在META-INF/spring.factories里面加上如下:

# 如果有多個XxxxxAutoConfiguration,用逗號分隔
org.springframework.boot.autoconfigure.EnableAutoConfiguration=xxx.xxx.xxx.xxx.XxxxxAutoConfiguration

(3)限制Auto-Configuration的順序
使用 @AutoConfigureAfter or @AutoConfigureBefore可以限制XxxxxxAutoConfiguration.class的加載順序
(4)創建metadata
(META-INF/spring-configuration-metadata.json) to make sure your keys are properly documented.
這里的配置是作用XxxxxxAutoConfiguration需要用到的一些properties的,這些properties我們可以寫在application.properties里面,然后通過這個文件的規約給出提示。
此外我們可以寫一個XxxxProperties.java, 然后通過 @ConfigurationProperties 注解,自動裝載application.properties的值到XxxxProperties.java中。
(5)編寫startermodule,引入Auto-Configuration這個module

https://blog.csdn.net/u010853261/article/details/77961716

spring.factories
簡單的說一下spring.factories這個主要是提供了一個功能,就是自動配置,不需要使用@EnableXXX來開啟,也就是說只要你用了springboot,並且依賴了一個jar包,這個jar包就會自動進行初始化
那么這個過程就是使用了spring.factories這個文件配置

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.start.config.AudoDemoConfig

下面的com.start.config.AudoDemoConfig就是自己定義的config,springboot啟動的時候就會自動掃描,具體使用可以查看下面這個項目中的com.start.config.AudoDemoConfig
http://git.oschina.net/wangkang_daydayup/SpringBoot-Learn/tree/master/springboot-11

spring-configuration-metadata.json
如果仔細看target/classes/META-INF中,你就會發現有那么一個文件的存在spring-configuration-metadata.json,這個就是整個application.yaml提示的配置,他是自動生成的

{
  "hints": [],
  "groups": [
    {
      "sourceType": "com.start.config.DemoProperties",
      "name": "springboot.demo",
      "type": "com.start.config.DemoProperties"
    }
  ],
  "properties": [
    {
      "sourceType": "com.start.config.DemoProperties",
      "name": "springboot.demo.name",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.start.config.DemoProperties",
      "name": "springboot.demo.select",
      "type": "java.lang.String"
    }
  ]
}

具體的英文文檔可以查看http://docs.spring.io/spring-boot/docs/current/reference/html/configuration-metadata.html#configuration-metadata-annotation-processor
這個json文件主要分為三個部分,首先是group,group代表一個類,比如DemoProperties,一個項目中存在多個group,而propertis代表是類里面的屬性通過sourceType來確定是那一個類的
然后是hints,語意上來說就是提示

hints的使用
具體項目中的使用,可以通過在resources/META-INF 中放入spring-configuration-metadata.json文件,文件內容如下

{
  "hints": [
    {
      "name": "springboot.demo.select",
      "values": [
        {
          "value": "1",
          "description": "1的描述"
        },
        {
          "value": "2",
          "description": "2的描述"
        }
      ]
    }
  ],
  "groups": [
    {
      "sourceType": "com.start.config.DemoProperties",
      "name": "springboot.demo",
      "type": "com.start.config.DemoProperties"
    }
  ],
  "properties": [
    {
      "sourceType": "com.start.config.DemoProperties",
      "name": "springboot.demo.name",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.start.config.DemoProperties",
      "name": "springboot.demo.select",
      "type": "java.lang.String"
    }
  ]
}

https://blog.csdn.net/king_is_everyone/article/details/75308620

如果想讓上述的配置在application.yaml進行自動提示,需要進行兩步,第一步是確保maven中有processor

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

第二步需要對idea 
編輯器進行配置Preferences->Build, Execution, Deployment->Compile->Annotation Processors->Enable annonation processing 
鈎上這個選項,這樣的話就會自動在target/classes/META-INF中生成spring-configuration-metadata.json文件 

一個完整的demo。
todo:設置默認值,及如果有多個可能選擇的值,有智能提示

   QiniuUploadManagerAutoConfiguration matched:
      - @ConditionalOnClass found required class 'com.qiniu.storage.UploadManager'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
      - @ConditionalOnProperty (qiniu.oss.enabled) matched (OnPropertyCondition)

   QiniuUploadManagerAutoConfiguration#qiniuUploadManager matched:
      - @ConditionalOnMissingBean (types: com.tangcheng.qiniu.oss.autoconfigure.QiniuUploadManager; SearchStrategy: all) did not find any beans (OnBeanCondition)

 



一個坑:
在pom文件中引入新創建的starter依賴后,Class import不了,提示 Cannot resolve symbol 'packageName'   

原因及解決辦法:
生成starter的jar,是一個可執行的jar

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>



    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

解決辦法:
去掉

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


Tips:
common jar的目錄結構 與 可執行jar目錄結構的差別:
common jar的目錄結構:

可執行jar的目錄結構【classes文件在BOOT-INF目錄下,不在根目錄】:

F:.
├─BOOT-INF │ ├─classes │ │ └─com
│  │      └─tangcheng
│  │          └─qiniu
│  │              └─oss
│  │                  │  QiniuOssSpringBootStarterApplication.class
│  │                  │
│  │                  └─autoconfigure
│  │                      │  QiniuUploadManager.class
│  │                      │  QiniuUploadManagerAutoConfiguration.class
│  │                      │
│  │                      └─properties
│  │                              QiniuOssProperties$Bucket$ZoneEnum.class
│  │                              QiniuOssProperties$Bucket.class
│  │                              QiniuOssProperties.class
│  │
│  └─lib
│          gson-2.8.2.jar
│          happy-dns-java-0.1.6.jar
│          javax.annotation-api-1.3.2.jar
│          jul-to-slf4j-1.7.25.jar
│          log4j-api-2.10.0.jar
│          log4j-to-slf4j-2.10.0.jar
│          logback-classic-1.2.3.jar
│          logback-core-1.2.3.jar
│          okhttp-3.9.1.jar
│          okio-1.13.0.jar
│          qiniu-java-sdk-7.2.11.jar
│          slf4j-api-1.7.25.jar
│          snakeyaml-1.19.jar
│          spring-aop-5.0.5.RELEASE.jar
│          spring-beans-5.0.5.RELEASE.jar
│          spring-boot-2.0.1.RELEASE.jar
│          spring-boot-autoconfigure-2.0.1.RELEASE.jar
│          spring-boot-configuration-processor-2.0.1.RELEASE.jar
│          spring-boot-starter-2.0.1.RELEASE.jar
│          spring-boot-starter-logging-2.0.1.RELEASE.jar
│          spring-context-5.0.5.RELEASE.jar
│          spring-core-5.0.5.RELEASE.jar
│          spring-expression-5.0.5.RELEASE.jar
│          spring-jcl-5.0.5.RELEASE.jar
│
├─META-INF
│  │  MANIFEST.MF
│  │  spring-configuration-metadata.json
│  │  spring.factories
│  │
│  └─maven
│      └─com.tangcheng.qiniu.oss
│          └─qiniu-oss-spring-boot-starter
│                  pom.properties
│                  pom.xml
│
└─org
    └─springframework
        └─boot
            └─loader
                │  ExecutableArchiveLauncher.class
                │  JarLauncher.class
                │  LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class
                │  LaunchedURLClassLoader.class
                │  Launcher.class
                │  MainMethodRunner.class
                │  PropertiesLauncher$1.class
                │  PropertiesLauncher$ArchiveEntryFilter.class
                │  PropertiesLauncher$PrefixMatchingArchiveFilter.class
                │  PropertiesLauncher.class
                │  WarLauncher.class
                │
                ├─archive
                │      Archive$Entry.class
                │      Archive$EntryFilter.class
                │      Archive.class
                │      ExplodedArchive$1.class
                │      ExplodedArchive$FileEntry.class
                │      ExplodedArchive$FileEntryIterator$EntryComparator.class
                │      ExplodedArchive$FileEntryIterator.class
                │      ExplodedArchive.class
                │      JarFileArchive$EntryIterator.class
                │      JarFileArchive$JarFileEntry.class
                │      JarFileArchive.class
                │
                ├─data
                │      RandomAccessData.class
                │      RandomAccessDataFile$1.class
                │      RandomAccessDataFile$DataInputStream.class
                │      RandomAccessDataFile$FileAccess.class
                │      RandomAccessDataFile.class
                │
                ├─jar
                │      AsciiBytes.class
                │      Bytes.class
                │      CentralDirectoryEndRecord.class
                │      CentralDirectoryFileHeader.class
                │      CentralDirectoryParser.class
                │      CentralDirectoryVisitor.class
                │      FileHeader.class
                │      Handler.class
                │      JarEntry.class
                │      JarEntryFilter.class
                │      JarFile$1.class
                │      JarFile$2.class
                │      JarFile$JarFileType.class
                │      JarFile.class
                │      JarFileEntries$1.class
                │      JarFileEntries$EntryIterator.class
                │      JarFileEntries.class
                │      JarURLConnection$1.class
                │      JarURLConnection$JarEntryName.class
                │      JarURLConnection.class
                │      StringSequence.class
                │      ZipInflaterInputStream.class
                │
                └─util
                        SystemPropertyUtils.class

 

 

 






在之前的文章中,我們分析過SpringBoot內部的自動化配置原理自動化配置注解開關原理

我們先簡單分析一下mybatis starter的編寫,然后再編寫自定義的starter。

mybatis中的autoconfigure模塊中使用了一個叫做MybatisAutoConfiguration的自動化配置類。

這個MybatisAutoConfiguration需要在這些Condition條件下才會執行:

  1. @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })。需要SqlSessionFactory和SqlSessionFactoryBean在classpath中都存在
  2. @ConditionalOnBean(DataSource.class)。 spring factory中需要存在一個DataSource的bean
  3. @AutoConfigureAfter(DataSourceAutoConfiguration.class)。需要在DataSourceAutoConfiguration自動化配置之后進行配置,因為mybatis需要數據源的支持

同時在META-INF目錄下有個spring.factories這個properties文件,而且它的key為org.springframework.boot.autoconfigure.EnableAutoConfiguration,這樣才會被springboot加載:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

有了這些東西之后,mybatis相關的配置會被自動加入到spring container中,只要在maven中加入starter即可:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.1.1</version>
</dependency>

編寫自定義的starter

接下來,我們來編寫自定義的starter:log-starter。

這個starter內部定義了一個注解,使用這個注解修飾方法之后,該方法的調用會在日志中被打印並且還會打印出方法的耗時。starter支持exclude配置,在exclude中出現的方法不會進行計算。

pom文件:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starters</artifactId>
    <version>1.3.5.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
</dependencies>

定義修飾方法的注解@Log:

package me.format.springboot.log.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;    

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log { }

然后是配置類:

package me.format.springboot.log.autoconfigure;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;

@ConfigurationProperties(prefix = "mylog")
public class LogProperties {

    private String exclude;

    private String[] excludeArr;

    @PostConstruct
    public void init() {
        this.excludeArr = StringUtils.split(exclude, ",");
    }

    public String getExclude() {
        return exclude;
    }

    public void setExclude(String exclude) {
        this.exclude = exclude;
    }

    public String[] getExcludeArr() {
        return excludeArr;
    }
}

接下來是AutoConfiguration:

package me.format.springboot.log.autoconfigure;

import me.format.springboot.log.annotation.Log;
import me.format.springboot.log.aop.LogMethodInterceptor;
import org.aopalliance.aop.Advice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration
@EnableConfigurationProperties(LogProperties.class)
public class LogAutoConfiguration extends AbstractPointcutAdvisor {

    private Logger logger = LoggerFactory.getLogger(LogAutoConfiguration.class);

    private Pointcut pointcut;

    private Advice advice;

    @Autowired
    private LogProperties logProperties;

    @PostConstruct
    public void init() {
        logger.info("init LogAutoConfiguration start");
        this.pointcut = new AnnotationMatchingPointcut(null, Log.class);
        this.advice = new LogMethodInterceptor(logProperties.getExcludeArr());
        logger.info("init LogAutoConfiguration end");
    }

    @Override
    public Pointcut getPointcut() {
        return this.pointcut;
    }

    @Override
    public Advice getAdvice() {
        return this.advice;
    }

}

由於計算方法調用的時候需要使用aop相關的lib,所以我們的AutoConfiguration繼承了AbstractPointcutAdvisor。這樣就有了Pointcut和Advice。Pointcut是一個支持注解的修飾方法的Pointcut,Advice則自己實現:

package me.format.springboot.log.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.List;

public class LogMethodInterceptor implements MethodInterceptor {
    private Logger logger = LoggerFactory.getLogger(LogMethodInterceptor.class);
    private List<String> exclude;
    public LogMethodInterceptor(String[] exclude) {
        this.exclude = Arrays.asList(exclude);
    }
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();
        if(exclude.contains(methodName)) {
            return invocation.proceed();
        }
        long start = System.currentTimeMillis();
        Object result = invocation.proceed();
        long end = System.currentTimeMillis();
        logger.info("====method({}), cost({}) ", methodName, (end - start));
        return result;
    }
}

最后resources/META-INF/spring.factories中加入這個AutoConfiguration:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
me.format.springboot.log.autoconfigure.LogAutoConfiguration

我們在項目中使用這個log-starter:

<dependency>
    <groupId>me.format.springboot</groupId>
    <artifactId>log-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

使用配置:

mylog.exclude=core,log

然后編寫一個簡單的Service:

@Service
public class SimpleService {

    @Log
    public void test(int num) {
        System.out.println("----test---- " + num);
    }

    @Log
    public void core(int num) {
        System.out.println("----core---- " + num);
    }

    public void work(int num) {
        System.out.println("----work---- " + num);
    }

}

使用單元測試分別調用這3個方法,由於work方法沒有加上@Log注解,core方法雖然加上了@Log注解,但是在配置中被exclude了,只有test方法可以正常計算耗時:

----test---- 666
2016-11-16 01:29:32.255  INFO 41010 --- [           main] m.f.s.log.aop.LogMethodInterceptor       : ====method(test),     cost(36) 
----work---- 666
----core---- 666

總結:

自定義springboot的starter,注意這兩點。

  1. 如果自動化配置類需要在程序啟動的時候就加載,可以在META-INF/spring.factories文件中定義。如果本次加載還需要其他一些lib的話,可以使用ConditionalOnClass注解協助
  2. 如果自動化配置類要在使用自定義注解后才加載,可以使用自定義注解+@Import注解或@ImportSelector注解完成

參考:

http://www.jianshu.com/p/85460c1d835a

http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html

 

http://fangjian0423.github.io/2016/11/16/springboot-custom-starter/

 

Appendix B. Configuration Metadata

Spring Boot jars include metadata files that provide details of all supported configuration properties. The files are designed to let IDE developers offer contextual help and “code completion” as users are working with application.properties or application.yml files.

The majority of the metadata file is generated automatically at compile time by processing all items annotated with @ConfigurationProperties. However, it is possible to write part of the metadata manually for corner cases or more advanced use cases.

B.1 Metadata Format

Configuration metadata files are located inside jars under META-INF/spring-configuration-metadata.json They use a simple JSON format with items categorized under either “groups” or “properties” and additional values hints categorized under "hints", as shown in the following example:

{"groups": [ { "name": "server", "type": "org.springframework.boot.autoconfigure.web.ServerProperties", "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties" }, { "name": "spring.jpa.hibernate", "type": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties$Hibernate", "sourceType": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties", "sourceMethod": "getHibernate()" } ... ],"properties": [ { "name": "server.port", "type": "java.lang.Integer", "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties" }, { "name": "server.servlet.path", "type": "java.lang.String", "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties", "defaultValue": "/" }, { "name": "spring.jpa.hibernate.ddl-auto", "type": "java.lang.String", "description": "DDL mode. This is actually a shortcut for the \"hibernate.hbm2ddl.auto\" property.", "sourceType": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties$Hibernate" } ... ],"hints": [ { "name": "spring.jpa.hibernate.ddl-auto", "values": [ { "value": "none", "description": "Disable DDL handling." }, { "value": "validate", "description": "Validate the schema, make no changes to the database." }, { "value": "update", "description": "Update the schema if necessary." }, { "value": "create", "description": "Create the schema and destroy previous data." }, { "value": "create-drop", "description": "Create and then destroy the schema at the end of the session." } ] } ]}

Each “property” is a configuration item that the user specifies with a given value. For example, server.port and server.servlet.path might be specified inapplication.properties, as follows:

server.port=9090
server.servlet.path=/home

The “groups” are higher level items that do not themselves specify a value but instead provide a contextual grouping for properties. For example, the server.port andserver.servlet.path properties are part of the server group.

[Note]

It is not required that every “property” has a “group”. Some properties might exist in their own right.

Finally, “hints” are additional information used to assist the user in configuring a given property. For example, when a developer is configuring thespring.jpa.hibernate.ddl-auto property, a tool can use the hints to offer some auto-completion help for the nonevalidateupdatecreate, and create-drop values.

B.1.1 Group Attributes

The JSON object contained in the groups array can contain the attributes shown in the following table:

Name Type Purpose

name

String

The full name of the group. This attribute is mandatory.

type

String

The class name of the data type of the group. For example, if the group were based on a class annotated with @ConfigurationProperties, the attribute would contain the fully qualified name of that class. If it were based on a @Bean method, it would be the return type of that method. If the type is not known, the attribute may be omitted.

description

String

A short description of the group that can be displayed to users. If not description is available, it may be omitted. It is recommended that descriptions be short paragraphs, with the first line providing a concise summary. The last line in the description should end with a period (.).

sourceType

String

The class name of the source that contributed this group. For example, if the group were based on a @Bean method annotated with @ConfigurationProperties, this attribute would contain the fully qualified name of the @Configuration class that contains the method. If the source type is not known, the attribute may be omitted.

sourceMethod

String

The full name of the method (include parenthesis and argument types) that contributed this group (for example, the name of a @ConfigurationProperties annotated @Bean method). If the source method is not known, it may be omitted.

B.1.2 Property Attributes

The JSON object contained in the properties array can contain the attributes described in the following table:

Name Type Purpose

name

String

The full name of the property. Names are in lower-case period-separated form (for example, server.servlet.path). This attribute is mandatory.

type

String

The full signature of the data type of the property (for example, java.lang.String) but also a full generic type (such as java.util.Map<java.util.String,acme.MyEnum>). You can use this attribute to guide the user as to the types of values that they can enter. For consistency, the type of a primitive is specified by using its wrapper counterpart (for example, booleanbecomes java.lang.Boolean). Note that this class may be a complex type that gets converted from a String as values are bound. If the type is not known, it may be omitted.

description

String

A short description of the group that can be displayed to users. If no description is available, it may be omitted. It is recommended that descriptions be short paragraphs, with the first line providing a concise summary. The last line in the description should end with a period (.).

sourceType

String

The class name of the source that contributed this property. For example, if the property were from a class annotated with @ConfigurationProperties, this attribute would contain the fully qualified name of that class. If the source type is unknown, it may be omitted.

defaultValue

Object

The default value, which is used if the property is not specified. If the type of the property is an array, it can be an array of value(s). If the default value is unknown, it may be omitted.

deprecation

Deprecation

Specify whether the property is deprecated. If the field is not deprecated or if that information is not known, it may be omitted. The next table offers more detail about the deprecation attribute.

The JSON object contained in the deprecation attribute of each properties element can contain the following attributes:

Name Type Purpose

level

String

The level of deprecation, which can be either warning (the default) or error. When a property has a warning deprecation level, it should still be bound in the environment. However, when it has an error deprecation level, the property is no longer managed and is not bound.

reason

String

A short description of the reason why the property was deprecated. If no reason is available, it may be omitted. It is recommended that descriptions be short paragraphs, with the first line providing a concise summary. The last line in the description should end with a period (.).

replacement

String

The full name of the property that replaces this deprecated property. If there is no replacement for this property, it may be omitted.

[Note]

Prior to Spring Boot 1.3, a single deprecated boolean attribute can be used instead of the deprecation element. This is still supported in a deprecated fashion and should no longer be used. If no reason and replacement are available, an empty deprecation object should be set.

Deprecation can also be specified declaratively in code by adding the @DeprecatedConfigurationProperty annotation to the getter exposing the deprecated property. For instance, assume that the app.acme.target property was confusing and was renamed to app.acme.name. The following example shows how to handle that situation:

@ConfigurationProperties("app.acme")
public class AcmeProperties { private String name; public String getName() { ... } public void setName(String name) { ... } @DeprecatedConfigurationProperty(replacement = "app.acme.name") @Deprecated public String getTarget() { return getName(); } @Deprecated public void setTarget(String target) { setName(target); } }
[Note]

There is no way to set a levelwarning is always assumed, since code is still handling the property.

The preceding code makes sure that the deprecated property still works (delegating to the name property behind the scenes). Once the getTarget and setTargetmethods can be removed from your public API, the automatic deprecation hint in the metadata goes away as well. If you want to keep a hint, adding manual metadata with an error deprecation level ensures that users are still informed about that property. Doing so is particularly useful when a replacement is provided.

B.1.3 Hint Attributes

The JSON object contained in the hints array can contain the attributes shown in the following table:

Name Type Purpose

name

String

The full name of the property to which this hint refers. Names are in lower-case period-separated form (such as server.servlet.path). If the property refers to a map (such as system.contexts), the hint either applies to the keys of the map (system.context.keys) or the values (system.context.values) of the map. This attribute is mandatory.

values

ValueHint[]

A list of valid values as defined by the ValueHint object (described in the next table). Each entry defines the value and may have a description.

providers

ValueProvider[]

A list of providers as defined by the ValueProvider object (described later in this document). Each entry defines the name of the provider and its parameters, if any.

The JSON object contained in the values attribute of each hint element can contain the attributes described in the following table:

Name Type Purpose

value

Object

A valid value for the element to which the hint refers. If the type of the property is an array, it can also be an array of value(s). This attribute is mandatory.

description

String

A short description of the value that can be displayed to users. If no description is available, it may be omitted . It is recommended that descriptions be short paragraphs, with the first line providing a concise summary. The last line in the description should end with a period (.).

The JSON object contained in the providers attribute of each hint element can contain the attributes described in the following table:

Name Type Purpose

name

String

The name of the provider to use to offer additional content assistance for the element to which the hint refers.

parameters

JSON object

Any additional parameter that the provider supports (check the documentation of the provider for more details).

B.1.4 Repeated Metadata Items

Objects with the same “property” and “group” name can appear multiple times within a metadata file. For example, you could bind two separate classes to the same prefix, with each having potentially overlapping property names. While the same names appearing in the metadata multiple times should not be common, consumers of metadata should take care to ensure that they support it.

B.2 Providing Manual Hints

To improve the user experience and further assist the user in configuring a given property, you can provide additional metadata that:

  • Describes the list of potential values for a property.
  • Associates a provider, to attach a well defined semantic to a property, so that a tool can discover the list of potential values based on the project’s context.

B.2.1 Value Hint

The name attribute of each hint refers to the name of a property. In the initial example shown earlier, we provide five values for the spring.jpa.hibernate.ddl-autoproperty: nonevalidateupdatecreate, and create-drop. Each value may have a description as well.

If your property is of type Map, you can provide hints for both the keys and the values (but not for the map itself). The special .keys and .values suffixes must refer to the keys and the values, respectively.

Assume a sample.contexts maps magic String values to an integer, as shown in the following example:

@ConfigurationProperties("sample")
public class SampleProperties { private Map<String,Integer> contexts; // getters and setters }

The magic values are (in this example) are sample1 and sample2. In order to offer additional content assistance for the keys, you could add the following JSON to the manual metadata of the module:

{"hints": [ { "name": "sample.contexts.keys", "values": [ { "value": "sample1" }, { "value": "sample2" } ] } ]}
[Tip]

We recommend that you use an Enum for those two values instead. If your IDE supports it, this is by far the most effective approach to auto-completion.

B.2.2 Value Providers

Providers are a powerful way to attach semantics to a property. In this section, we define the official providers that you can use for your own hints. However, your favorite IDE may implement some of these or none of them. Also, it could eventually provide its own.

[Note]

As this is a new feature, IDE vendors must catch up with how it works. Adoption times naturally vary.

The following table summarizes the list of supported providers:

Name Description

any

Permits any additional value to be provided.

class-reference

Auto-completes the classes available in the project. Usually constrained by a base class that is specified by the target parameter.

handle-as

Handles the property as if it were defined by the type defined by the mandatory target parameter.

logger-name

Auto-completes valid logger names. Typically, package and class names available in the current project can be auto-completed.

spring-bean-reference

Auto-completes the available bean names in the current project. Usually constrained by a base class that is specified by the targetparameter.

spring-profile-name

Auto-completes the available Spring profile names in the project.

[Tip]

Only one provider can be active for a given property, but you can specify several providers if they can all manage the property in some way. Make sure to place the most powerful provider first, as the IDE must use the first one in the JSON section that it can handle. If no provider for a given property is supported, no special content assistance is provided, either.

Any

The special any provider value permits any additional values to be provided. Regular value validation based on the property type should be applied if this is supported.

This provider is typically used if you have a list of values and any extra values should still be considered as valid.

The following example offers on and off as auto-completion values for system.state:

{"hints": [ { "name": "system.state", "values": [ { "value": "on" }, { "value": "off" } ], "providers": [ { "name": "any" } ] } ]}

Note that, in the preceding example, any other value is also allowed.

Class Reference

The class-reference provider auto-completes classes available in the project. This provider supports the following parameters:

Parameter Type Default value Description

target

String(Class)

none

The fully qualified name of the class that should be assignable to the chosen value. Typically used to filter out-non candidate classes. Note that this information can be provided by the type itself by exposing a class with the appropriate upper bound.

concrete

boolean

true

Specify whether only concrete classes are to be considered as valid candidates.

The following metadata snippet corresponds to the standard server.servlet.jsp.class-name property that defines the JspServlet class name to use:

{"hints": [ { "name": "server.servlet.jsp.class-name", "providers": [ { "name": "class-reference", "parameters": { "target": "javax.servlet.http.HttpServlet" } } ] } ]}

Handle As

The handle-as provider lets you substitute the type of the property to a more high-level type. This typically happens when the property has a java.lang.String type, because you do not want your configuration classes to rely on classes that may not be on the classpath. This provider supports the following parameters:

Parameter Type Default value Description

target

String (Class)

none

The fully qualified name of the type to consider for the property. This parameter is mandatory.

The following types can be used:

  • Any java.lang.Enum: Lists the possible values for the property. (We recommend defining the property with the Enum type, as no further hint should be required for the IDE to auto-complete the values.)
  • java.nio.charset.Charset: Supports auto-completion of charset/encoding values (such as UTF-8)
  • java.util.Locale: auto-completion of locales (such as en_US)
  • org.springframework.util.MimeType: Supports auto-completion of content type values (such as text/plain)
  • org.springframework.core.io.Resource: Supports auto-completion of Spring’s Resource abstraction to refer to a file on the filesystem or on the classpath. (such as classpath:/sample.properties)
[Tip]

If multiple values can be provided, use a Collection or Array type to teach the IDE about it.

The following metadata snippet corresponds to the standard spring.liquibase.change-log property that defines the path to the changelog to use. It is actually used internally as a org.springframework.core.io.Resource but cannot be exposed as such, because we need to keep the original String value to pass it to the Liquibase API.

{"hints": [ { "name": "spring.liquibase.change-log", "providers": [ { "name": "handle-as", "parameters": { "target": "org.springframework.core.io.Resource" } } ] } ]}

Logger Name

The logger-name provider auto-completes valid logger names. Typically, package and class names available in the current project can be auto-completed. Specific frameworks may have extra magic logger names that can be supported as well.

Since a logger name can be any arbitrary name, this provider should allow any value but could highlight valid package and class names that are not available in the project’s classpath.

The following metadata snippet corresponds to the standard logging.level property. Keys are logger names, and values correspond to the standard log levels or any custom level.

{"hints": [ { "name": "logging.level.keys", "values": [ { "value": "root", "description": "Root logger used to assign the default logging level." } ], "providers": [ { "name": "logger-name" } ] }, { "name": "logging.level.values", "values": [ { "value": "trace" }, { "value": "debug" }, { "value": "info" }, { "value": "warn" }, { "value": "error" }, { "value": "fatal" }, { "value": "off" } ], "providers": [ { "name": "any" } ] } ]}

Spring Bean Reference

The spring-bean-reference provider auto-completes the beans that are defined in the configuration of the current project. This provider supports the following parameters:

Parameter Type Default value Description

target

String(Class)

none

The fully qualified name of the bean class that should be assignable to the candidate. Typically used to filter out non-candidate beans.

The following metadata snippet corresponds to the standard spring.jmx.server property that defines the name of the MBeanServer bean to use:

{"hints": [ { "name": "spring.jmx.server", "providers": [ { "name": "spring-bean-reference", "parameters": { "target": "javax.management.MBeanServer" } } ] } ]}
[Note]

The binder is not aware of the metadata. If you provide that hint, you still need to transform the bean name into an actual Bean reference using by the ApplicationContext.

Spring Profile Name

The spring-profile-name provider auto-completes the Spring profiles that are defined in the configuration of the current project.

The following metadata snippet corresponds to the standard spring.profiles.active property that defines the name of the Spring profile(s) to enable:

{"hints": [ { "name": "spring.profiles.active", "providers": [ { "name": "spring-profile-name" } ] } ]}

B.3 Generating Your Own Metadata by Using the Annotation Processor

You can easily generate your own configuration metadata file from items annotated with @ConfigurationProperties by using the spring-boot-configuration-processor jar. The jar includes a Java annotation processor which is invoked as your project is compiled. To use the processor, include a dependency on spring-boot-configuration-processor.

With Maven the dependency should be declared as optional, as shown in the following example:

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

With Gradle 4.5 and earlier, the dependency should be declared in the compileOnly configuration, as shown in the following example:

dependencies {
	compileOnly "org.springframework.boot:spring-boot-configuration-processor"
}

With Gradle 4.6 and later, the dependency should be declared in the annotationProcessor configuration, as shown in the following example:

dependencies {
	annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
}

If you are using an additional-spring-configuration-metadata.json file, the compileJava task should be configured to depend on the processResourcestask, as shown in the following example:

compileJava.dependsOn(processResources)

This dependency ensures that the additional metadata is available when the annotation processor runs during compilation.

The processor picks up both classes and methods that are annotated with @ConfigurationProperties. The Javadoc for field values within configuration classes is used to populate the description attribute.

[Note]

You should only use simple text with @ConfigurationProperties field Javadoc, since they are not processed before being added to the JSON.

Properties are discovered through the presence of standard getters and setters with special handling for collection types (that is detected even if only a getter is present). The annotation processor also supports the use of the @Data@Getter, and @Setter lombok annotations.

[Note]

If you are using AspectJ in your project, you need to make sure that the annotation processor runs only once. There are several ways to do this. With Maven, you can configure the maven-apt-plugin explicitly and add the dependency to the annotation processor only there. You could also let the AspectJ plugin run all the processing and disable annotation processing in the maven-compiler-plugin configuration, as follows:

<plugin>
	<groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <proc>none</proc> </configuration> </plugin>

B.3.1 Nested Properties

The annotation processor automatically considers inner classes as nested properties. Consider the following class:

@ConfigurationProperties(prefix="server")
public class ServerProperties {

    private String name;

    private Host host; // ... getter and setters

    private static class Host {

        private String ip;

        private int port;

        // ... getter and setters

    }

}
 
          

The preceding example produces metadata information for server.nameserver.host.ip, and server.host.port properties. You can use the @NestedConfigurationProperty annotation on a field to indicate that a regular (non-inner) class should be treated as if it were nested.

[Tip]

This has no effect on collections and maps, as those types are automatically identified, and a single metadata property is generated for each of them.

B.3.2 Adding Additional Metadata

Spring Boot’s configuration file handling is quite flexible, and it is often the case that properties may exist that are not bound to a @ConfigurationProperties bean. You may also need to tune some attributes of an existing key. To support such cases and let you provide custom "hints", the annotation processor automatically merges items from META-INF/additional-spring-configuration-metadata.json into the main metadata file.

If you refer to a property that has been detected automatically, the description, default value, and deprecation information are overridden, if specified. If the manual property declaration is not identified in the current module, it is added as a new property.

The format of the additional-spring-configuration-metadata.json file is exactly the same as the regular spring-configuration-metadata.json. The additional properties file is optional. If you do not have any additional properties, do not add the file.

https://docs.spring.io/spring-boot/docs/current/reference/html/configuration-metadata.html#configuration-metadata-annotation-processor

https://www.jianshu.com/p/547fe62365f8

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM