Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
8f693a02
Commit
8f693a02
authored
Mar 20, 2019
by
Madhura Bhave
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for configuration properties scanning
See gh-12602
parent
711169aa
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
563 additions
and
132 deletions
+563
-132
SpringBootApplication.java
...ngframework/boot/autoconfigure/SpringBootApplication.java
+7
-3
spring-boot-features.adoc
...ing-boot-docs/src/main/asciidoc/spring-boot-features.adoc
+27
-28
using-spring-boot.adoc
...spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc
+12
-10
ConfigurationPropertiesBeanDefinitionRegistrar.java
...rties/ConfigurationPropertiesBeanDefinitionRegistrar.java
+120
-0
ConfigurationPropertiesScan.java
.../boot/context/properties/ConfigurationPropertiesScan.java
+75
-0
ConfigurationPropertiesScanRegistrar.java
...text/properties/ConfigurationPropertiesScanRegistrar.java
+91
-0
EnableConfigurationPropertiesImportSelector.java
...operties/EnableConfigurationPropertiesImportSelector.java
+4
-91
ConfigurationPropertiesScanRegistrarTests.java
...properties/ConfigurationPropertiesScanRegistrarTests.java
+97
-0
ConfigurationPropertiesScanConfiguration.java
...erties/scan/ConfigurationPropertiesScanConfiguration.java
+70
-0
AScanConfiguration.java
...rk/boot/context/properties/scan/a/AScanConfiguration.java
+30
-0
BScanConfiguration.java
...rk/boot/context/properties/scan/b/BScanConfiguration.java
+30
-0
No files found.
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SpringBootApplication.java
View file @
8f693a02
...
@@ -25,6 +25,7 @@ import java.lang.annotation.Target;
...
@@ -25,6 +25,7 @@ import java.lang.annotation.Target;
import
org.springframework.boot.SpringBootConfiguration
;
import
org.springframework.boot.SpringBootConfiguration
;
import
org.springframework.boot.context.TypeExcludeFilter
;
import
org.springframework.boot.context.TypeExcludeFilter
;
import
org.springframework.boot.context.properties.ConfigurationPropertiesScan
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.ComponentScan
;
import
org.springframework.context.annotation.ComponentScan
;
import
org.springframework.context.annotation.ComponentScan.Filter
;
import
org.springframework.context.annotation.ComponentScan.Filter
;
...
@@ -35,9 +36,11 @@ import org.springframework.core.annotation.AliasFor;
...
@@ -35,9 +36,11 @@ import org.springframework.core.annotation.AliasFor;
/**
/**
* Indicates a {@link Configuration configuration} class that declares one or more
* Indicates a {@link Configuration configuration} class that declares one or more
* {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
* {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
* auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
* auto-configuration}, {@link ComponentScan component scanning}, and
* annotation that is equivalent to declaring {@code @Configuration},
* {@link ConfigurationPropertiesScan configuration properties scanning}. This is a
* {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
* convenience annotation that is equivalent to declaring {@code @Configuration},
* {@code @EnableAutoConfiguration}, {@code @ComponentScan}, and
* {@code @ConfigurationPropertiesScan}.
*
*
* @author Phillip Webb
* @author Phillip Webb
* @author Stephane Nicoll
* @author Stephane Nicoll
...
@@ -50,6 +53,7 @@ import org.springframework.core.annotation.AliasFor;
...
@@ -50,6 +53,7 @@ import org.springframework.core.annotation.AliasFor;
@Inherited
@Inherited
@SpringBootConfiguration
@SpringBootConfiguration
@EnableAutoConfiguration
@EnableAutoConfiguration
@ConfigurationPropertiesScan
@ComponentScan
(
excludeFilters
=
{
@ComponentScan
(
excludeFilters
=
{
@Filter
(
type
=
FilterType
.
CUSTOM
,
classes
=
TypeExcludeFilter
.
class
),
@Filter
(
type
=
FilterType
.
CUSTOM
,
classes
=
TypeExcludeFilter
.
class
),
@Filter
(
type
=
FilterType
.
CUSTOM
,
classes
=
AutoConfigurationExcludeFilter
.
class
)
})
@Filter
(
type
=
FilterType
.
CUSTOM
,
classes
=
AutoConfigurationExcludeFilter
.
class
)
})
...
...
spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
View file @
8f693a02
...
@@ -1070,7 +1070,23 @@ missing property.
...
@@ -1070,7 +1070,23 @@ missing property.
[[boot-features-external-config-enabling]]
[[boot-features-external-config-enabling]]
==== Enabling `@ConfigurationProperties`-annotated types
==== Enabling `@ConfigurationProperties`-annotated types
Spring Boot provides an infrastructure to bind such types and register them as beans
Spring Boot provides an infrastructure to bind such types and register them as beans
automatically. Any `@Configuration` class can specify the list of types to process as
automatically. If your application uses `@SpringBootApplication`, classes annotated with
`@ConfigurationProperties` will automatically be scanned and registered as beans. By default,
scanning will occur from the package of the class that declares this annotation. If you want
to define specific packages to scan, you can do so using an explicit `@ConfigurationPropertiesScan`
directive on your `@SpringBootApplication`-annotated class as shown in the following example:
[source,java,indent=0]
----
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "org.acme.another" })
public class MyApplication {
}
----
Sometimes, classes annotated with `@ConfigurationProperties` might not be suitable
for scanning, for example, if you're developing your own auto-configuration. In these
cases, you can specify the list of types to process on any `@Configuration` class as
shown in the following example:
shown in the following example:
[source,java,indent=0]
[source,java,indent=0]
...
@@ -1083,13 +1099,13 @@ shown in the following example:
...
@@ -1083,13 +1099,13 @@ shown in the following example:
[NOTE]
[NOTE]
====
====
When the `@ConfigurationProperties` bean is registered
that way, the bean has
a
When the `@ConfigurationProperties` bean is registered
using scanning or vi
a
conventional name: `<prefix>-<fqn>`, where `<prefix>` is the environment key prefix
`@EnableConfigurationProperties`, the bean has a conventional name: `<prefix>-<fqn>`,
specified in the `@ConfigurationProperties` annotation and `<fqn>` is the fully qualified
where `<prefix>` is the environment key prefix specified in the `@ConfigurationProperties`
name of the bean. If the annotation does not provide any prefix, only the fully qualified
annotation and `<fqn>` is the fully qualified name of the bean. If the annotation does not
name of the bean is used.
provide any prefix, only the fully qualified
name of the bean is used.
The bean name in the example
s
above is `acme-com.example.AcmeProperties`.
The bean name in the example above is `acme-com.example.AcmeProperties`.
====
====
We recommend that `@ConfigurationProperties` only deal with the environment and, in
We recommend that `@ConfigurationProperties` only deal with the environment and, in
...
@@ -1100,27 +1116,10 @@ binder that only deals with the environment.
...
@@ -1100,27 +1116,10 @@ binder that only deals with the environment.
For corner cases, setter injection can be used or any of the `*Aware` interfaces provided
For corner cases, setter injection can be used or any of the `*Aware` interfaces provided
by the framework (such as `EnvironmentAware` if you need access to the `Environment`).
by the framework (such as `EnvironmentAware` if you need access to the `Environment`).
If you find using `@EnableConfigurationProperties` tedious, you can also declare a bean
NOTE: Annotating a `@ConfigurationProperties` type with `@Component` will result in two
yourself. For instance, instead of annotating `MyConfiguration` with
beans of the same type if the type is also scanned as part of classpath scanning. If you want
`@EnableConfigurationProperties(AcmeProperties.class)`, you could make `AcmeProperties`
to register the bean yourself using `@Component`, consider disabling scanning of
a bean, as shown in the following example:
`@ConfigurationProperties`.
[source,java,indent=0]
----
@Component
@ConfigurationProperties(prefix="acme")
public class AcmeProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
// ... see the preceding JavaBean properties binding example
}
----
...
...
spring-boot-project/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc
View file @
8f693a02
...
@@ -336,8 +336,8 @@ best practices that help.
...
@@ -336,8 +336,8 @@ best practices that help.
When a class does not include a `package` declaration, it is considered to be in the
When a class does not include a `package` declaration, it is considered to be in the
"`default package`". The use of the "`default package`" is generally discouraged and
"`default package`". The use of the "`default package`" is generally discouraged and
should be avoided. It can cause particular problems for Spring Boot applications that use
should be avoided. It can cause particular problems for Spring Boot applications that use
the `@ComponentScan`, `@
EntityScan`, or `@SpringBootApplication` annotations, since every
the `@ComponentScan`, `@
ConfigurationPropertiesScan`, `@EntityScan`, or `@SpringBootApplication`
class from every jar is read.
annotations, since every
class from every jar is read.
TIP: We recommend that you follow Java's recommended package naming conventions and use a
TIP: We recommend that you follow Java's recommended package naming conventions and use a
reversed domain name (for example, `com.example.project`).
reversed domain name (for example, `com.example.project`).
...
@@ -355,8 +355,8 @@ is used to search for `@Entity` items. Using a root package also allows componen
...
@@ -355,8 +355,8 @@ is used to search for `@Entity` items. Using a root package also allows componen
scan to apply only on your project.
scan to apply only on your project.
TIP: If you don't want to use `@SpringBootApplication`, the `@EnableAutoConfiguration`
TIP: If you don't want to use `@SpringBootApplication`, the `@EnableAutoConfiguration`
and `@ComponentScan` annotations that it imports defines that behaviour so you can also
`@ComponentScan`, and `@ConfigurationPropertiesScan` annotations that it imports defines
use that
instead.
that behaviour so you can also use those
instead.
The following listing shows a typical layout:
The following listing shows a typical layout:
...
@@ -556,12 +556,14 @@ be able to define extra configuration on their "application class". A single
...
@@ -556,12 +556,14 @@ be able to define extra configuration on their "application class". A single
auto-configuration mechanism>>
auto-configuration mechanism>>
* `@ComponentScan`: enable `@Component` scan on the package where the application is
* `@ComponentScan`: enable `@Component` scan on the package where the application is
located (see <<using-boot-structuring-your-code,the best practices>>)
located (see <<using-boot-structuring-your-code,the best practices>>)
* `@ConfigurationPropertiesScan`: enable `@ConfigurationProperties` scan on the package
where the application is located (see <<using-boot-structuring-your-code,the best practices>>)
* `@Configuration`: allow to register extra beans in the context or import additional
* `@Configuration`: allow to register extra beans in the context or import additional
configuration classes
configuration classes
The `@SpringBootApplication` annotation is equivalent to using `@Configuration`,
The `@SpringBootApplication` annotation is equivalent to using `@Configuration`,
`@EnableAutoConfiguration`,
and `@ComponentScan` with their default attributes, as shown
`@EnableAutoConfiguration`,
@ComponentScan`, and `@ConfigurationPropertiesScan` with their default
in the following example:
attributes, as shown
in the following example:
[source,java,indent=0]
[source,java,indent=0]
...
@@ -571,7 +573,7 @@ in the following example:
...
@@ -571,7 +573,7 @@ in the following example:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan
@SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan
@ConfigurationPropertiesScan
public class Application {
public class Application {
public static void main(String[] args) {
public static void main(String[] args) {
...
@@ -588,7 +590,7 @@ NOTE: `@SpringBootApplication` also provides aliases to customize the attributes
...
@@ -588,7 +590,7 @@ NOTE: `@SpringBootApplication` also provides aliases to customize the attributes
====
====
None of these features are mandatory and you may choose to replace this single annotation
None of these features are mandatory and you may choose to replace this single annotation
by any of the features that it enables. For instance, you may not want to use component
by any of the features that it enables. For instance, you may not want to use component
scan in your application:
scan
or configuration properties scan
in your application:
[source,java,indent=0]
[source,java,indent=0]
----
----
...
@@ -612,8 +614,8 @@ scan in your application:
...
@@ -612,8 +614,8 @@ scan in your application:
----
----
In this example, `Application` is just like any other Spring Boot application except that
In this example, `Application` is just like any other Spring Boot application except that
`@Component`-annotated classes a
re not detected automatically and the user-defined beans
`@Component`-annotated classes a
nd `@ConfigurationProperties`-annotated classes are not detected
are imported explicitly (see `@Import`).
a
utomatically and the user-defined beans a
re imported explicitly (see `@Import`).
====
====
...
...
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanDefinitionRegistrar.java
0 → 100644
View file @
8f693a02
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
context
.
properties
;
import
java.lang.reflect.Constructor
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.List
;
import
org.springframework.beans.BeanUtils
;
import
org.springframework.beans.factory.BeanFactory
;
import
org.springframework.beans.factory.config.BeanDefinition
;
import
org.springframework.beans.factory.config.ConfigurableListableBeanFactory
;
import
org.springframework.beans.factory.support.BeanDefinitionRegistry
;
import
org.springframework.beans.factory.support.GenericBeanDefinition
;
import
org.springframework.core.KotlinDetector
;
import
org.springframework.core.annotation.AnnotationUtils
;
import
org.springframework.util.Assert
;
import
org.springframework.util.StringUtils
;
/**
* Registers a bean definition for a type annotated with {@link ConfigurationProperties}
* using the prefix of the annotation in the bean name.
*
* @author Madhura Bhave
*/
final
class
ConfigurationPropertiesBeanDefinitionRegistrar
{
private
ConfigurationPropertiesBeanDefinitionRegistrar
()
{
}
private
static
final
boolean
KOTLIN_PRESENT
=
KotlinDetector
.
isKotlinPresent
();
public
static
void
register
(
BeanDefinitionRegistry
registry
,
ConfigurableListableBeanFactory
beanFactory
,
Class
<?>
type
)
{
String
name
=
getName
(
type
);
if
(!
containsBeanDefinition
(
beanFactory
,
name
))
{
registerBeanDefinition
(
registry
,
beanFactory
,
name
,
type
);
}
}
private
static
String
getName
(
Class
<?>
type
)
{
ConfigurationProperties
annotation
=
AnnotationUtils
.
findAnnotation
(
type
,
ConfigurationProperties
.
class
);
String
prefix
=
(
annotation
!=
null
)
?
annotation
.
prefix
()
:
""
;
return
(
StringUtils
.
hasText
(
prefix
)
?
prefix
+
"-"
+
type
.
getName
()
:
type
.
getName
());
}
private
static
boolean
containsBeanDefinition
(
ConfigurableListableBeanFactory
beanFactory
,
String
name
)
{
if
(
beanFactory
.
containsBeanDefinition
(
name
))
{
return
true
;
}
BeanFactory
parent
=
beanFactory
.
getParentBeanFactory
();
if
(
parent
instanceof
ConfigurableListableBeanFactory
)
{
return
containsBeanDefinition
((
ConfigurableListableBeanFactory
)
parent
,
name
);
}
return
false
;
}
private
static
void
registerBeanDefinition
(
BeanDefinitionRegistry
registry
,
ConfigurableListableBeanFactory
beanFactory
,
String
name
,
Class
<?>
type
)
{
assertHasAnnotation
(
type
);
registry
.
registerBeanDefinition
(
name
,
createBeanDefinition
(
beanFactory
,
name
,
type
));
}
private
static
void
assertHasAnnotation
(
Class
<?>
type
)
{
Assert
.
notNull
(
AnnotationUtils
.
findAnnotation
(
type
,
ConfigurationProperties
.
class
),
()
->
"No "
+
ConfigurationProperties
.
class
.
getSimpleName
()
+
" annotation found on '"
+
type
.
getName
()
+
"'."
);
}
private
static
BeanDefinition
createBeanDefinition
(
ConfigurableListableBeanFactory
beanFactory
,
String
name
,
Class
<?>
type
)
{
if
(
canBindAtCreationTime
(
type
))
{
return
ConfigurationPropertiesBeanDefinition
.
from
(
beanFactory
,
name
,
type
);
}
else
{
GenericBeanDefinition
definition
=
new
GenericBeanDefinition
();
definition
.
setBeanClass
(
type
);
return
definition
;
}
}
private
static
boolean
canBindAtCreationTime
(
Class
<?>
type
)
{
List
<
Constructor
<?>>
constructors
=
determineConstructors
(
type
);
return
(
constructors
.
size
()
==
1
&&
constructors
.
get
(
0
).
getParameterCount
()
>
0
);
}
private
static
List
<
Constructor
<?>>
determineConstructors
(
Class
<?>
type
)
{
List
<
Constructor
<?>>
constructors
=
new
ArrayList
<>();
if
(
KOTLIN_PRESENT
&&
KotlinDetector
.
isKotlinType
(
type
))
{
Constructor
<?>
primaryConstructor
=
BeanUtils
.
findPrimaryConstructor
(
type
);
if
(
primaryConstructor
!=
null
)
{
constructors
.
add
(
primaryConstructor
);
}
}
else
{
constructors
.
addAll
(
Arrays
.
asList
(
type
.
getDeclaredConstructors
()));
}
return
constructors
;
}
}
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesScan.java
0 → 100644
View file @
8f693a02
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
context
.
properties
;
import
java.lang.annotation.Documented
;
import
java.lang.annotation.ElementType
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
import
org.springframework.context.annotation.Import
;
import
org.springframework.core.annotation.AliasFor
;
/**
* Configures the base packages used when scanning for {@link ConfigurationProperties}
* classes. One of {@link #basePackageClasses()}, {@link #basePackages()} or its alias
* {@link #value()} may be specified to define specific packages to scan. If specific
* packages are not defined scanning will occur from the package of the class with this
* annotation.
*
* @author Madhura Bhave
* @since 2.2.0
* @see ConfigurationPropertiesScanRegistrar
*/
@Target
(
ElementType
.
TYPE
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Documented
@Import
(
ConfigurationPropertiesScanRegistrar
.
class
)
public
@interface
ConfigurationPropertiesScan
{
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
* declarations e.g.: {@code @ConfigurationPropertiesScan("org.my.pkg")} instead of
* {@code @ConfigurationPropertiesScan(basePackages="org.my.pkg")}.
* @return the base packages to scan
*/
@AliasFor
(
"basePackages"
)
String
[]
value
()
default
{};
/**
* Base packages to scan for configuration properties. {@link #value()} is an alias
* for (and mutually exclusive with) this attribute.
* <p>
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
* package names.
* @return the base packages to scan
*/
@AliasFor
(
"value"
)
String
[]
basePackages
()
default
{};
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages to
* scan for configuration properties. The package of each class specified will be
* scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return classes from the base packages to scan
*/
Class
<?>[]
basePackageClasses
()
default
{};
}
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesScanRegistrar.java
0 → 100644
View file @
8f693a02
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
context
.
properties
;
import
java.util.Arrays
;
import
java.util.LinkedHashSet
;
import
java.util.Set
;
import
org.springframework.beans.factory.config.BeanDefinition
;
import
org.springframework.beans.factory.config.ConfigurableListableBeanFactory
;
import
org.springframework.beans.factory.support.BeanDefinitionRegistry
;
import
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
;
import
org.springframework.context.annotation.ImportBeanDefinitionRegistrar
;
import
org.springframework.core.annotation.AnnotationAttributes
;
import
org.springframework.core.type.AnnotationMetadata
;
import
org.springframework.core.type.filter.AnnotationTypeFilter
;
import
org.springframework.util.ClassUtils
;
import
org.springframework.util.StringUtils
;
/**
* {@link ImportBeanDefinitionRegistrar} for registering {@link ConfigurationProperties}
* bean definitions via scanning.
*
* @author Madhura Bhave
*/
class
ConfigurationPropertiesScanRegistrar
implements
ImportBeanDefinitionRegistrar
{
@Override
public
void
registerBeanDefinitions
(
AnnotationMetadata
importingClassMetadata
,
BeanDefinitionRegistry
registry
)
{
Set
<
String
>
packagesToScan
=
getPackagesToScan
(
importingClassMetadata
);
register
(
registry
,
(
ConfigurableListableBeanFactory
)
registry
,
packagesToScan
);
}
private
Set
<
String
>
getPackagesToScan
(
AnnotationMetadata
metadata
)
{
AnnotationAttributes
attributes
=
AnnotationAttributes
.
fromMap
(
metadata
.
getAnnotationAttributes
(
ConfigurationPropertiesScan
.
class
.
getName
()));
String
[]
basePackages
=
attributes
.
getStringArray
(
"basePackages"
);
Class
<?>[]
basePackageClasses
=
attributes
.
getClassArray
(
"basePackageClasses"
);
Set
<
String
>
packagesToScan
=
new
LinkedHashSet
<>(
Arrays
.
asList
(
basePackages
));
for
(
Class
<?>
basePackageClass
:
basePackageClasses
)
{
packagesToScan
.
add
(
ClassUtils
.
getPackageName
(
basePackageClass
));
}
if
(
packagesToScan
.
isEmpty
())
{
packagesToScan
.
add
(
ClassUtils
.
getPackageName
(
metadata
.
getClassName
()));
}
return
packagesToScan
;
}
protected
void
register
(
BeanDefinitionRegistry
registry
,
ConfigurableListableBeanFactory
beanFactory
,
Set
<
String
>
packagesToScan
)
{
scan
(
packagesToScan
,
beanFactory
,
registry
);
}
protected
void
scan
(
Set
<
String
>
packages
,
ConfigurableListableBeanFactory
beanFactory
,
BeanDefinitionRegistry
registry
)
{
ClassPathScanningCandidateComponentProvider
scanner
=
new
ClassPathScanningCandidateComponentProvider
(
false
);
scanner
.
addIncludeFilter
(
new
AnnotationTypeFilter
(
ConfigurationProperties
.
class
));
for
(
String
basePackage
:
packages
)
{
if
(
StringUtils
.
hasText
(
basePackage
))
{
for
(
BeanDefinition
candidate
:
scanner
.
findCandidateComponents
(
basePackage
))
{
String
beanClassName
=
candidate
.
getBeanClassName
();
try
{
Class
<?>
type
=
ClassUtils
.
forName
(
beanClassName
,
null
);
ConfigurationPropertiesBeanDefinitionRegistrar
.
register
(
registry
,
beanFactory
,
type
);
}
catch
(
ClassNotFoundException
ex
)
{
// Ignore
}
}
}
}
}
}
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/EnableConfigurationPropertiesImportSelector.java
View file @
8f693a02
...
@@ -16,27 +16,17 @@
...
@@ -16,27 +16,17 @@
package
org
.
springframework
.
boot
.
context
.
properties
;
package
org
.
springframework
.
boot
.
context
.
properties
;
import
java.lang.reflect.Constructor
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.List
;
import
java.util.stream.Collectors
;
import
java.util.stream.Collectors
;
import
org.springframework.beans.BeanUtils
;
import
org.springframework.beans.factory.BeanFactory
;
import
org.springframework.beans.factory.config.BeanDefinition
;
import
org.springframework.beans.factory.config.ConfigurableListableBeanFactory
;
import
org.springframework.beans.factory.config.ConfigurableListableBeanFactory
;
import
org.springframework.beans.factory.support.BeanDefinitionRegistry
;
import
org.springframework.beans.factory.support.BeanDefinitionRegistry
;
import
org.springframework.beans.factory.support.GenericBeanDefinition
;
import
org.springframework.context.annotation.ImportBeanDefinitionRegistrar
;
import
org.springframework.context.annotation.ImportBeanDefinitionRegistrar
;
import
org.springframework.context.annotation.ImportSelector
;
import
org.springframework.context.annotation.ImportSelector
;
import
org.springframework.core.KotlinDetector
;
import
org.springframework.core.annotation.AnnotationUtils
;
import
org.springframework.core.type.AnnotationMetadata
;
import
org.springframework.core.type.AnnotationMetadata
;
import
org.springframework.util.Assert
;
import
org.springframework.util.MultiValueMap
;
import
org.springframework.util.MultiValueMap
;
import
org.springframework.util.StringUtils
;
/**
/**
* Import selector that sets up binding of external properties to configuration classes
* Import selector that sets up binding of external properties to configuration classes
...
@@ -54,8 +44,6 @@ import org.springframework.util.StringUtils;
...
@@ -54,8 +44,6 @@ import org.springframework.util.StringUtils;
*/
*/
class
EnableConfigurationPropertiesImportSelector
implements
ImportSelector
{
class
EnableConfigurationPropertiesImportSelector
implements
ImportSelector
{
private
static
boolean
KOTLIN_PRESENT
=
KotlinDetector
.
isKotlinPresent
();
private
static
final
String
[]
IMPORTS
=
{
private
static
final
String
[]
IMPORTS
=
{
ConfigurationPropertiesBeanRegistrar
.
class
.
getName
(),
ConfigurationPropertiesBeanRegistrar
.
class
.
getName
(),
ConfigurationPropertiesBindingPostProcessorRegistrar
.
class
.
getName
()
};
ConfigurationPropertiesBindingPostProcessorRegistrar
.
class
.
getName
()
};
...
@@ -74,8 +62,10 @@ class EnableConfigurationPropertiesImportSelector implements ImportSelector {
...
@@ -74,8 +62,10 @@ class EnableConfigurationPropertiesImportSelector implements ImportSelector {
@Override
@Override
public
void
registerBeanDefinitions
(
AnnotationMetadata
metadata
,
public
void
registerBeanDefinitions
(
AnnotationMetadata
metadata
,
BeanDefinitionRegistry
registry
)
{
BeanDefinitionRegistry
registry
)
{
getTypes
(
metadata
).
forEach
((
type
)
->
register
(
registry
,
ConfigurableListableBeanFactory
beanFactory
=
(
ConfigurableListableBeanFactory
)
registry
;
(
ConfigurableListableBeanFactory
)
registry
,
type
));
getTypes
(
metadata
)
.
forEach
((
type
)
->
ConfigurationPropertiesBeanDefinitionRegistrar
.
register
(
registry
,
beanFactory
,
type
));
}
}
private
List
<
Class
<?>>
getTypes
(
AnnotationMetadata
metadata
)
{
private
List
<
Class
<?>>
getTypes
(
AnnotationMetadata
metadata
)
{
...
@@ -92,83 +82,6 @@ class EnableConfigurationPropertiesImportSelector implements ImportSelector {
...
@@ -92,83 +82,6 @@ class EnableConfigurationPropertiesImportSelector implements ImportSelector {
.
collect
(
Collectors
.
toList
());
.
collect
(
Collectors
.
toList
());
}
}
private
void
register
(
BeanDefinitionRegistry
registry
,
ConfigurableListableBeanFactory
beanFactory
,
Class
<?>
type
)
{
String
name
=
getName
(
type
);
if
(!
containsBeanDefinition
(
beanFactory
,
name
))
{
registerBeanDefinition
(
registry
,
beanFactory
,
name
,
type
);
}
}
private
String
getName
(
Class
<?>
type
)
{
ConfigurationProperties
annotation
=
AnnotationUtils
.
findAnnotation
(
type
,
ConfigurationProperties
.
class
);
String
prefix
=
(
annotation
!=
null
)
?
annotation
.
prefix
()
:
""
;
return
(
StringUtils
.
hasText
(
prefix
)
?
prefix
+
"-"
+
type
.
getName
()
:
type
.
getName
());
}
private
boolean
containsBeanDefinition
(
ConfigurableListableBeanFactory
beanFactory
,
String
name
)
{
if
(
beanFactory
.
containsBeanDefinition
(
name
))
{
return
true
;
}
BeanFactory
parent
=
beanFactory
.
getParentBeanFactory
();
if
(
parent
instanceof
ConfigurableListableBeanFactory
)
{
return
containsBeanDefinition
((
ConfigurableListableBeanFactory
)
parent
,
name
);
}
return
false
;
}
private
void
registerBeanDefinition
(
BeanDefinitionRegistry
registry
,
ConfigurableListableBeanFactory
beanFactory
,
String
name
,
Class
<?>
type
)
{
assertHasAnnotation
(
type
);
registry
.
registerBeanDefinition
(
name
,
createBeanDefinition
(
beanFactory
,
name
,
type
));
}
private
void
assertHasAnnotation
(
Class
<?>
type
)
{
Assert
.
notNull
(
AnnotationUtils
.
findAnnotation
(
type
,
ConfigurationProperties
.
class
),
()
->
"No "
+
ConfigurationProperties
.
class
.
getSimpleName
()
+
" annotation found on '"
+
type
.
getName
()
+
"'."
);
}
private
BeanDefinition
createBeanDefinition
(
ConfigurableListableBeanFactory
beanFactory
,
String
name
,
Class
<?>
type
)
{
if
(
canBindAtCreationTime
(
type
))
{
return
ConfigurationPropertiesBeanDefinition
.
from
(
beanFactory
,
name
,
type
);
}
else
{
GenericBeanDefinition
definition
=
new
GenericBeanDefinition
();
definition
.
setBeanClass
(
type
);
return
definition
;
}
}
private
boolean
canBindAtCreationTime
(
Class
<?>
type
)
{
List
<
Constructor
<?>>
constructors
=
determineConstructors
(
type
);
return
(
constructors
.
size
()
==
1
&&
constructors
.
get
(
0
).
getParameterCount
()
>
0
);
}
private
List
<
Constructor
<?>>
determineConstructors
(
Class
<?>
type
)
{
List
<
Constructor
<?>>
constructors
=
new
ArrayList
<>();
if
(
KOTLIN_PRESENT
&&
KotlinDetector
.
isKotlinType
(
type
))
{
Constructor
<?>
primaryConstructor
=
BeanUtils
.
findPrimaryConstructor
(
type
);
if
(
primaryConstructor
!=
null
)
{
constructors
.
add
(
primaryConstructor
);
}
}
else
{
constructors
.
addAll
(
Arrays
.
asList
(
type
.
getDeclaredConstructors
()));
}
return
constructors
;
}
}
}
}
}
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesScanRegistrarTests.java
0 → 100644
View file @
8f693a02
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
context
.
properties
;
import
java.io.IOException
;
import
org.junit.Test
;
import
org.springframework.beans.factory.config.BeanDefinition
;
import
org.springframework.beans.factory.support.DefaultListableBeanFactory
;
import
org.springframework.beans.factory.support.GenericBeanDefinition
;
import
org.springframework.boot.context.properties.scan.ConfigurationPropertiesScanConfiguration
;
import
org.springframework.core.type.AnnotationMetadata
;
import
org.springframework.core.type.classreading.SimpleMetadataReaderFactory
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
/**
* Tests for {@link ConfigurationPropertiesScanRegistrar}.
*
* @author Madhura Bhave
*/
public
class
ConfigurationPropertiesScanRegistrarTests
{
private
final
ConfigurationPropertiesScanRegistrar
registrar
=
new
ConfigurationPropertiesScanRegistrar
();
private
final
DefaultListableBeanFactory
beanFactory
=
new
DefaultListableBeanFactory
();
@Test
public
void
registerBeanDefintionsShouldScanForConfigurationProperties
()
throws
IOException
{
this
.
registrar
.
registerBeanDefinitions
(
getAnnotationMetadata
(
ConfigurationPropertiesScanConfiguration
.
class
),
this
.
beanFactory
);
BeanDefinition
bingDefinition
=
this
.
beanFactory
.
getBeanDefinition
(
"bing-org.springframework.boot.context.properties.scan.ConfigurationPropertiesScanConfiguration$BingProperties"
);
BeanDefinition
fooDefinition
=
this
.
beanFactory
.
getBeanDefinition
(
"foo-org.springframework.boot.context.properties.scan.ConfigurationPropertiesScanConfiguration$FooProperties"
);
BeanDefinition
barDefinition
=
this
.
beanFactory
.
getBeanDefinition
(
"bar-org.springframework.boot.context.properties.scan.ConfigurationPropertiesScanConfiguration$BarProperties"
);
assertThat
(
bingDefinition
).
isExactlyInstanceOf
(
GenericBeanDefinition
.
class
);
assertThat
(
fooDefinition
).
isExactlyInstanceOf
(
GenericBeanDefinition
.
class
);
assertThat
(
barDefinition
)
.
isExactlyInstanceOf
(
ConfigurationPropertiesBeanDefinition
.
class
);
}
@Test
public
void
scanWhenBeanDefinitionExistsShouldSkip
()
throws
IOException
{
DefaultListableBeanFactory
beanFactory
=
new
DefaultListableBeanFactory
();
beanFactory
.
setAllowBeanDefinitionOverriding
(
false
);
this
.
registrar
.
registerBeanDefinitions
(
getAnnotationMetadata
(
ConfigurationPropertiesScanConfiguration
.
TestConfiguration
.
class
),
beanFactory
);
BeanDefinition
fooDefinition
=
beanFactory
.
getBeanDefinition
(
"foo-org.springframework.boot.context.properties.scan.ConfigurationPropertiesScanConfiguration$FooProperties"
);
assertThat
(
fooDefinition
).
isExactlyInstanceOf
(
GenericBeanDefinition
.
class
);
}
@Test
public
void
scanWhenBasePackagesAndBasePackcageClassesProvidedShouldUseThat
()
throws
IOException
{
DefaultListableBeanFactory
beanFactory
=
new
DefaultListableBeanFactory
();
beanFactory
.
setAllowBeanDefinitionOverriding
(
false
);
this
.
registrar
.
registerBeanDefinitions
(
getAnnotationMetadata
(
ConfigurationPropertiesScanConfiguration
.
DifferentPackageConfiguration
.
class
),
beanFactory
);
assertThat
(
beanFactory
.
containsBeanDefinition
(
"foo-org.springframework.boot.context.properties.scan.ConfigurationPropertiesScanConfiguration$FooProperties"
))
.
isFalse
();
BeanDefinition
aDefinition
=
beanFactory
.
getBeanDefinition
(
"a-org.springframework.boot.context.properties.scan.a.AScanConfiguration$AProperties"
);
BeanDefinition
bDefinition
=
beanFactory
.
getBeanDefinition
(
"b-org.springframework.boot.context.properties.scan.b.BScanConfiguration$BProperties"
);
assertThat
(
aDefinition
).
isExactlyInstanceOf
(
GenericBeanDefinition
.
class
);
assertThat
(
bDefinition
).
isExactlyInstanceOf
(
GenericBeanDefinition
.
class
);
}
private
AnnotationMetadata
getAnnotationMetadata
(
Class
<?>
source
)
throws
IOException
{
return
new
SimpleMetadataReaderFactory
().
getMetadataReader
(
source
.
getName
())
.
getAnnotationMetadata
();
}
}
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/scan/ConfigurationPropertiesScanConfiguration.java
0 → 100644
View file @
8f693a02
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
context
.
properties
.
scan
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
import
org.springframework.boot.context.properties.ConfigurationPropertiesScan
;
import
org.springframework.boot.context.properties.EnableConfigurationProperties
;
import
org.springframework.boot.context.properties.scan.b.BScanConfiguration
;
/**
* Used for testing {@link ConfigurationProperties} scanning.
*
* @author Madhura Bhave
*/
@ConfigurationPropertiesScan
public
class
ConfigurationPropertiesScanConfiguration
{
@ConfigurationPropertiesScan
@EnableConfigurationProperties
({
ConfigurationPropertiesScanConfiguration
.
FooProperties
.
class
})
public
static
class
TestConfiguration
{
}
@ConfigurationPropertiesScan
(
basePackages
=
"org.springframework.boot.context.properties.scan.a"
,
basePackageClasses
=
BScanConfiguration
.
class
)
public
static
class
DifferentPackageConfiguration
{
}
@ConfigurationProperties
(
prefix
=
"foo"
)
static
class
FooProperties
{
}
@ConfigurationProperties
(
prefix
=
"bar"
)
public
static
class
BarProperties
{
public
BarProperties
(
String
foo
)
{
}
}
@ConfigurationProperties
(
prefix
=
"bing"
)
public
static
class
BingProperties
{
public
BingProperties
()
{
}
public
BingProperties
(
String
foo
)
{
}
}
}
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/scan/a/AScanConfiguration.java
0 → 100644
View file @
8f693a02
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
context
.
properties
.
scan
.
a
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
/**
* @author Madhura Bhave
*/
public
class
AScanConfiguration
{
@ConfigurationProperties
(
prefix
=
"a"
)
static
class
AProperties
{
}
}
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/scan/b/BScanConfiguration.java
0 → 100644
View file @
8f693a02
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
context
.
properties
.
scan
.
b
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
/**
* @author Madhura Bhave
*/
public
class
BScanConfiguration
{
@ConfigurationProperties
(
prefix
=
"b"
)
static
class
BProperties
{
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment