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
59d3a79c
Commit
59d3a79c
authored
Feb 28, 2017
by
Andy Wilkinson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Avoid eager initialization when finding beans by annotation
Closes gh-8269
parent
8a326a87
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
175 additions
and
39 deletions
+175
-39
BeanTypeRegistry.java
...mework/boot/autoconfigure/condition/BeanTypeRegistry.java
+40
-12
OnBeanCondition.java
...amework/boot/autoconfigure/condition/OnBeanCondition.java
+24
-27
ConditionalOnBeanTests.java
.../boot/autoconfigure/condition/ConditionalOnBeanTests.java
+78
-0
ConditionalOnMissingBeanTests.java
...utoconfigure/condition/ConditionalOnMissingBeanTests.java
+33
-0
No files found.
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java
View file @
59d3a79c
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
package
org
.
springframework
.
boot
.
autoconfigure
.
condition
;
package
org
.
springframework
.
boot
.
autoconfigure
.
condition
;
import
java.lang.annotation.Annotation
;
import
java.lang.reflect.Method
;
import
java.lang.reflect.Method
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.HashMap
;
...
@@ -40,6 +41,7 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
...
@@ -40,6 +41,7 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
import
org.springframework.beans.factory.support.DefaultListableBeanFactory
;
import
org.springframework.beans.factory.support.DefaultListableBeanFactory
;
import
org.springframework.beans.factory.support.RootBeanDefinition
;
import
org.springframework.beans.factory.support.RootBeanDefinition
;
import
org.springframework.core.ResolvableType
;
import
org.springframework.core.ResolvableType
;
import
org.springframework.core.annotation.AnnotationUtils
;
import
org.springframework.core.type.MethodMetadata
;
import
org.springframework.core.type.MethodMetadata
;
import
org.springframework.core.type.StandardMethodMetadata
;
import
org.springframework.core.type.StandardMethodMetadata
;
import
org.springframework.util.Assert
;
import
org.springframework.util.Assert
;
...
@@ -85,7 +87,7 @@ final class BeanTypeRegistry implements SmartInitializingSingleton {
...
@@ -85,7 +87,7 @@ final class BeanTypeRegistry implements SmartInitializingSingleton {
* @param beanFactory the source bean factory
* @param beanFactory the source bean factory
* @return the {@link BeanTypeRegistry} for the given bean factory
* @return the {@link BeanTypeRegistry} for the given bean factory
*/
*/
public
static
BeanTypeRegistry
create
(
ListableBeanFactory
beanFactory
)
{
static
BeanTypeRegistry
get
(
ListableBeanFactory
beanFactory
)
{
Assert
.
isInstanceOf
(
DefaultListableBeanFactory
.
class
,
beanFactory
);
Assert
.
isInstanceOf
(
DefaultListableBeanFactory
.
class
,
beanFactory
);
DefaultListableBeanFactory
listableBeanFactory
=
(
DefaultListableBeanFactory
)
beanFactory
;
DefaultListableBeanFactory
listableBeanFactory
=
(
DefaultListableBeanFactory
)
beanFactory
;
Assert
.
isTrue
(
listableBeanFactory
.
isAllowEagerClassLoading
(),
Assert
.
isTrue
(
listableBeanFactory
.
isAllowEagerClassLoading
(),
...
@@ -101,26 +103,39 @@ final class BeanTypeRegistry implements SmartInitializingSingleton {
...
@@ -101,26 +103,39 @@ final class BeanTypeRegistry implements SmartInitializingSingleton {
/**
/**
* Return the names of beans matching the given type (including subclasses), judging
* Return the names of beans matching the given type (including subclasses), judging
* from either bean definitions or the value of {@code getObjectType} in the case of
* from either bean definitions or the value of {@link FactoryBean#getObjectType()} in
* FactoryBeans. Will include singletons but not cause early bean initialization.
* the case of {@link FactoryBean FactoryBeans}. Will include singletons but will not
* cause early bean initialization.
* @param type the class or interface to match (must not be {@code null})
* @param type the class or interface to match (must not be {@code null})
* @return the names of beans (or objects created by FactoryBeans) matching the given
* @return the names of beans (or objects created by FactoryBeans) matching the given
* object type (including subclasses), or an empty set if none
* object type (including subclasses), or an empty set if none
*/
*/
Set
<
String
>
getNamesForType
(
Class
<?>
type
)
{
Set
<
String
>
getNamesForType
(
Class
<?>
type
)
{
if
(
this
.
lastBeanDefinitionCount
!=
this
.
beanFactory
.
getBeanDefinitionCount
())
{
updateTypesIfNecessary
();
Iterator
<
String
>
names
=
this
.
beanFactory
.
getBeanNamesIterator
();
Set
<
String
>
matches
=
new
LinkedHashSet
<
String
>();
while
(
names
.
hasNext
())
{
for
(
Map
.
Entry
<
String
,
Class
<?>>
entry
:
this
.
beanTypes
.
entrySet
())
{
String
name
=
names
.
next
();
if
(
entry
.
getValue
()
!=
null
&&
type
.
isAssignableFrom
(
entry
.
getValue
()))
{
if
(!
this
.
beanTypes
.
containsKey
(
name
))
{
matches
.
add
(
entry
.
getKey
());
addBeanType
(
name
);
}
}
}
this
.
lastBeanDefinitionCount
=
this
.
beanFactory
.
getBeanDefinitionCount
();
}
}
return
matches
;
}
/**
* Returns the names of beans annotated with the given {@code annotation}, judging
* from either bean definitions or the value of {@link FactoryBean#getObjectType()} in
* the case of {@link FactoryBean FactoryBeans}. Will include singletons but will not
* cause early bean initialization.
* @param annotation the annotation to match (must not be {@code null})
* @return the names of beans (or objects created by FactoryBeans) annoated with the
* given annotation, or an empty set if none
*/
Set
<
String
>
getNamesForAnnotation
(
Class
<?
extends
Annotation
>
annotation
)
{
updateTypesIfNecessary
();
Set
<
String
>
matches
=
new
LinkedHashSet
<
String
>();
Set
<
String
>
matches
=
new
LinkedHashSet
<
String
>();
for
(
Map
.
Entry
<
String
,
Class
<?>>
entry
:
this
.
beanTypes
.
entrySet
())
{
for
(
Map
.
Entry
<
String
,
Class
<?>>
entry
:
this
.
beanTypes
.
entrySet
())
{
if
(
entry
.
getValue
()
!=
null
&&
type
.
isAssignableFrom
(
entry
.
getValue
()))
{
if
(
entry
.
getValue
()
!=
null
&&
AnnotationUtils
.
findAnnotation
(
entry
.
getValue
(),
annotation
)
!=
null
)
{
matches
.
add
(
entry
.
getKey
());
matches
.
add
(
entry
.
getKey
());
}
}
}
}
...
@@ -183,6 +198,19 @@ final class BeanTypeRegistry implements SmartInitializingSingleton {
...
@@ -183,6 +198,19 @@ final class BeanTypeRegistry implements SmartInitializingSingleton {
&&
!
this
.
beanFactory
.
containsSingleton
(
factoryBeanName
));
&&
!
this
.
beanFactory
.
containsSingleton
(
factoryBeanName
));
}
}
private
void
updateTypesIfNecessary
()
{
if
(
this
.
lastBeanDefinitionCount
!=
this
.
beanFactory
.
getBeanDefinitionCount
())
{
Iterator
<
String
>
names
=
this
.
beanFactory
.
getBeanNamesIterator
();
while
(
names
.
hasNext
())
{
String
name
=
names
.
next
();
if
(!
this
.
beanTypes
.
containsKey
(
name
))
{
addBeanType
(
name
);
}
}
this
.
lastBeanDefinitionCount
=
this
.
beanFactory
.
getBeanDefinitionCount
();
}
}
/**
/**
* Attempt to guess the type that a {@link FactoryBean} will return based on the
* Attempt to guess the type that a {@link FactoryBean} will return based on the
* generics in its method signature.
* generics in its method signature.
...
...
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java
View file @
59d3a79c
...
@@ -22,6 +22,7 @@ import java.util.ArrayList;
...
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.HashSet
;
import
java.util.LinkedHashSet
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Set
;
import
java.util.Set
;
...
@@ -59,8 +60,6 @@ import org.springframework.util.StringUtils;
...
@@ -59,8 +60,6 @@ import org.springframework.util.StringUtils;
@Order
(
Ordered
.
LOWEST_PRECEDENCE
)
@Order
(
Ordered
.
LOWEST_PRECEDENCE
)
class
OnBeanCondition
extends
SpringBootCondition
implements
ConfigurationCondition
{
class
OnBeanCondition
extends
SpringBootCondition
implements
ConfigurationCondition
{
private
static
final
String
[]
NO_BEANS
=
{};
/**
/**
* Bean definition attribute name for factory beans to signal their product type (if
* Bean definition attribute name for factory beans to signal their product type (if
* known and it can't be deduced from the factory bean class).
* known and it can't be deduced from the factory bean class).
...
@@ -184,7 +183,7 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
...
@@ -184,7 +183,7 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
private
void
collectBeanNamesForType
(
Set
<
String
>
result
,
private
void
collectBeanNamesForType
(
Set
<
String
>
result
,
ListableBeanFactory
beanFactory
,
Class
<?>
type
,
boolean
considerHierarchy
)
{
ListableBeanFactory
beanFactory
,
Class
<?>
type
,
boolean
considerHierarchy
)
{
result
.
addAll
(
BeanTypeRegistry
.
create
(
beanFactory
).
getNamesForType
(
type
));
result
.
addAll
(
BeanTypeRegistry
.
get
(
beanFactory
).
getNamesForType
(
type
));
if
(
considerHierarchy
&&
beanFactory
instanceof
HierarchicalBeanFactory
)
{
if
(
considerHierarchy
&&
beanFactory
instanceof
HierarchicalBeanFactory
)
{
BeanFactory
parent
=
((
HierarchicalBeanFactory
)
beanFactory
)
BeanFactory
parent
=
((
HierarchicalBeanFactory
)
beanFactory
)
.
getParentBeanFactory
();
.
getParentBeanFactory
();
...
@@ -198,34 +197,32 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
...
@@ -198,34 +197,32 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
private
String
[]
getBeanNamesForAnnotation
(
private
String
[]
getBeanNamesForAnnotation
(
ConfigurableListableBeanFactory
beanFactory
,
String
type
,
ConfigurableListableBeanFactory
beanFactory
,
String
type
,
ClassLoader
classLoader
,
boolean
considerHierarchy
)
throws
LinkageError
{
ClassLoader
classLoader
,
boolean
considerHierarchy
)
throws
LinkageError
{
S
tring
[]
result
=
NO_BEANS
;
S
et
<
String
>
names
=
new
HashSet
<
String
>()
;
try
{
try
{
@SuppressWarnings
(
"unchecked"
)
@SuppressWarnings
(
"unchecked"
)
Class
<?
extends
Annotation
>
typeClass
=
(
Class
<?
extends
Annotation
>)
ClassUtils
Class
<?
extends
Annotation
>
annotationType
=
(
Class
<?
extends
Annotation
>)
ClassUtils
.
forName
(
type
,
classLoader
);
.
forName
(
type
,
classLoader
);
result
=
beanFactory
.
getBeanNamesForAnnotation
(
typeClass
);
collectBeanNamesForAnnotation
(
names
,
beanFactory
,
annotationType
,
if
(
considerHierarchy
)
{
considerHierarchy
);
if
(
beanFactory
.
getParentBeanFactory
()
instanceof
ConfigurableListableBeanFactory
)
{
String
[]
parentResult
=
getBeanNamesForAnnotation
(
(
ConfigurableListableBeanFactory
)
beanFactory
.
getParentBeanFactory
(),
type
,
classLoader
,
true
);
List
<
String
>
resultList
=
new
ArrayList
<
String
>();
resultList
.
addAll
(
Arrays
.
asList
(
result
));
for
(
String
beanName
:
parentResult
)
{
if
(!
resultList
.
contains
(
beanName
)
&&
!
beanFactory
.
containsLocalBean
(
beanName
))
{
resultList
.
add
(
beanName
);
}
}
result
=
StringUtils
.
toStringArray
(
resultList
);
}
}
return
result
;
}
}
catch
(
ClassNotFoundException
ex
)
{
catch
(
ClassNotFoundException
e
)
{
return
NO_BEANS
;
// Continue
}
return
StringUtils
.
toStringArray
(
names
);
}
private
void
collectBeanNamesForAnnotation
(
Set
<
String
>
names
,
ListableBeanFactory
beanFactory
,
Class
<?
extends
Annotation
>
annotationType
,
boolean
considerHierarchy
)
{
names
.
addAll
(
BeanTypeRegistry
.
get
(
beanFactory
).
getNamesForAnnotation
(
annotationType
));
if
(
considerHierarchy
)
{
BeanFactory
parent
=
((
HierarchicalBeanFactory
)
beanFactory
)
.
getParentBeanFactory
();
if
(
parent
instanceof
ListableBeanFactory
)
{
collectBeanNamesForAnnotation
(
names
,
(
ListableBeanFactory
)
parent
,
annotationType
,
considerHierarchy
);
}
}
}
}
}
...
...
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBeanTests.java
View file @
59d3a79c
...
@@ -16,10 +16,16 @@
...
@@ -16,10 +16,16 @@
package
org
.
springframework
.
boot
.
autoconfigure
.
condition
;
package
org
.
springframework
.
boot
.
autoconfigure
.
condition
;
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
java.util.Date
;
import
java.util.Date
;
import
org.junit.Test
;
import
org.junit.Test
;
import
org.springframework.beans.factory.FactoryBean
;
import
org.springframework.beans.factory.support.BeanDefinitionRegistry
;
import
org.springframework.beans.factory.support.BeanDefinitionRegistry
;
import
org.springframework.beans.factory.support.RootBeanDefinition
;
import
org.springframework.beans.factory.support.RootBeanDefinition
;
import
org.springframework.boot.test.util.EnvironmentTestUtils
;
import
org.springframework.boot.test.util.EnvironmentTestUtils
;
...
@@ -124,6 +130,15 @@ public class ConditionalOnBeanTests {
...
@@ -124,6 +130,15 @@ public class ConditionalOnBeanTests {
this
.
context
.
refresh
();
this
.
context
.
refresh
();
}
}
@Test
public
void
beanProducedByFactoryBeanIsConsideredWhenMatchingOnAnnotation
()
{
this
.
context
.
register
(
FactoryBeanConfiguration
.
class
,
OnAnnotationWithFactoryBeanConfiguration
.
class
);
this
.
context
.
refresh
();
assertThat
(
this
.
context
.
containsBean
(
"bar"
)).
isTrue
();
assertThat
(
this
.
context
.
getBeansOfType
(
ExampleBean
.
class
)).
hasSize
(
1
);
}
@Configuration
@Configuration
@ConditionalOnBean
(
name
=
"foo"
)
@ConditionalOnBean
(
name
=
"foo"
)
protected
static
class
OnBeanNameConfiguration
{
protected
static
class
OnBeanNameConfiguration
{
...
@@ -220,6 +235,27 @@ public class ConditionalOnBeanTests {
...
@@ -220,6 +235,27 @@ public class ConditionalOnBeanTests {
}
}
@Configuration
static
class
FactoryBeanConfiguration
{
@Bean
public
ExampleFactoryBean
exampleBeanFactoryBean
()
{
return
new
ExampleFactoryBean
();
}
}
@Configuration
@ConditionalOnBean
(
annotation
=
TestAnnotation
.
class
)
static
class
OnAnnotationWithFactoryBeanConfiguration
{
@Bean
public
String
bar
()
{
return
"bar"
;
}
}
protected
static
class
WithPropertyPlaceholderClassNameRegistrar
protected
static
class
WithPropertyPlaceholderClassNameRegistrar
implements
ImportBeanDefinitionRegistrar
{
implements
ImportBeanDefinitionRegistrar
{
...
@@ -233,4 +269,46 @@ public class ConditionalOnBeanTests {
...
@@ -233,4 +269,46 @@ public class ConditionalOnBeanTests {
}
}
public
static
class
ExampleFactoryBean
implements
FactoryBean
<
ExampleBean
>
{
@Override
public
ExampleBean
getObject
()
throws
Exception
{
return
new
ExampleBean
(
"fromFactory"
);
}
@Override
public
Class
<?>
getObjectType
()
{
return
ExampleBean
.
class
;
}
@Override
public
boolean
isSingleton
()
{
return
false
;
}
}
@TestAnnotation
public
static
class
ExampleBean
{
private
String
value
;
public
ExampleBean
(
String
value
)
{
this
.
value
=
value
;
}
@Override
public
String
toString
()
{
return
this
.
value
;
}
}
@Target
(
ElementType
.
TYPE
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Documented
public
@interface
TestAnnotation
{
}
}
}
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java
View file @
59d3a79c
...
@@ -16,6 +16,11 @@
...
@@ -16,6 +16,11 @@
package
org
.
springframework
.
boot
.
autoconfigure
.
condition
;
package
org
.
springframework
.
boot
.
autoconfigure
.
condition
;
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
java.util.Date
;
import
java.util.Date
;
import
org.junit.Test
;
import
org.junit.Test
;
...
@@ -285,6 +290,15 @@ public class ConditionalOnMissingBeanTests {
...
@@ -285,6 +290,15 @@ public class ConditionalOnMissingBeanTests {
assertThat
(
child
.
getBeansOfType
(
ExampleBean
.
class
)).
hasSize
(
2
);
assertThat
(
child
.
getBeansOfType
(
ExampleBean
.
class
)).
hasSize
(
2
);
}
}
@Test
public
void
beanProducedByFactoryBeanIsConsideredWhenMatchingOnAnnotation
()
{
this
.
context
.
register
(
ConcreteFactoryBeanConfiguration
.
class
,
OnAnnotationWithFactoryBeanConfiguration
.
class
);
this
.
context
.
refresh
();
assertThat
(
this
.
context
.
containsBean
(
"bar"
)).
isFalse
();
assertThat
(
this
.
context
.
getBeansOfType
(
ExampleBean
.
class
)).
hasSize
(
1
);
}
@Configuration
@Configuration
protected
static
class
OnBeanInParentsConfiguration
{
protected
static
class
OnBeanInParentsConfiguration
{
...
@@ -500,6 +514,17 @@ public class ConditionalOnMissingBeanTests {
...
@@ -500,6 +514,17 @@ public class ConditionalOnMissingBeanTests {
}
}
@Configuration
@ConditionalOnMissingBean
(
annotation
=
TestAnnotation
.
class
)
protected
static
class
OnAnnotationWithFactoryBeanConfiguration
{
@Bean
public
String
bar
()
{
return
"bar"
;
}
}
@Configuration
@Configuration
@EnableScheduling
@EnableScheduling
protected
static
class
FooConfiguration
{
protected
static
class
FooConfiguration
{
...
@@ -554,6 +579,7 @@ public class ConditionalOnMissingBeanTests {
...
@@ -554,6 +579,7 @@ public class ConditionalOnMissingBeanTests {
}
}
@TestAnnotation
public
static
class
ExampleBean
{
public
static
class
ExampleBean
{
private
String
value
;
private
String
value
;
...
@@ -623,4 +649,11 @@ public class ConditionalOnMissingBeanTests {
...
@@ -623,4 +649,11 @@ public class ConditionalOnMissingBeanTests {
}
}
@Target
(
ElementType
.
TYPE
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Documented
public
@interface
TestAnnotation
{
}
}
}
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