Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
apollo
Project
Overview
Details
Activity
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
openSource
apollo
Commits
c9612f6d
Commit
c9612f6d
authored
Mar 10, 2018
by
nobodyiam
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
minor refactor and add more ut
parent
f2d85126
Show whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
459 additions
and
240 deletions
+459
-240
DefaultInjector.java
...com/ctrip/framework/apollo/internals/DefaultInjector.java
+2
-0
ApolloAnnotationProcessor.java
...k/apollo/spring/annotation/ApolloAnnotationProcessor.java
+6
-8
ApolloConfigRegistrar.java
...ework/apollo/spring/annotation/ApolloConfigRegistrar.java
+2
-3
ApolloJsonValue.java
...p/framework/apollo/spring/annotation/ApolloJsonValue.java
+18
-3
ApolloJsonValueProcessor.java
...rk/apollo/spring/annotation/ApolloJsonValueProcessor.java
+62
-54
ApolloProcessor.java
...p/framework/apollo/spring/annotation/ApolloProcessor.java
+1
-4
SpringValueProcessor.java
...mework/apollo/spring/annotation/SpringValueProcessor.java
+21
-42
ConfigPropertySourcesProcessor.java
.../apollo/spring/config/ConfigPropertySourcesProcessor.java
+3
-5
PropertySourcesProcessor.java
...mework/apollo/spring/config/PropertySourcesProcessor.java
+22
-6
AutoUpdateConfigChangeListener.java
...pollo/spring/property/AutoUpdateConfigChangeListener.java
+31
-51
PlaceholderHelper.java
...p/framework/apollo/spring/property/PlaceholderHelper.java
+45
-9
SpringValue.java
...m/ctrip/framework/apollo/spring/property/SpringValue.java
+2
-2
SpringValueRegistry.java
...framework/apollo/spring/property/SpringValueRegistry.java
+17
-0
MockInjector.java
...t/java/com/ctrip/framework/apollo/build/MockInjector.java
+1
-0
AbstractSpringIntegrationTest.java
...ramework/apollo/spring/AbstractSpringIntegrationTest.java
+2
-0
JavaConfigPlaceholderAutoUpdateTest.java
...rk/apollo/spring/JavaConfigPlaceholderAutoUpdateTest.java
+131
-12
JavaConfigPlaceholderTest.java
...ip/framework/apollo/spring/JavaConfigPlaceholderTest.java
+63
-17
AnnotatedBean.java
...amework/apollo/demo/spring/common/bean/AnnotatedBean.java
+30
-24
No files found.
apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultInjector.java
View file @
c9612f6d
...
...
@@ -9,6 +9,7 @@ import com.ctrip.framework.apollo.spi.DefaultConfigFactoryManager;
import
com.ctrip.framework.apollo.spi.DefaultConfigRegistry
;
import
com.ctrip.framework.apollo.spring.config.ConfigPropertySourceFactory
;
import
com.ctrip.framework.apollo.spring.property.PlaceholderHelper
;
import
com.ctrip.framework.apollo.spring.property.SpringValueRegistry
;
import
com.ctrip.framework.apollo.tracer.Tracer
;
import
com.ctrip.framework.apollo.util.ConfigUtil
;
import
com.ctrip.framework.apollo.util.http.HttpUtil
;
...
...
@@ -64,6 +65,7 @@ public class DefaultInjector implements Injector {
bind
(
RemoteConfigLongPollService
.
class
).
in
(
Singleton
.
class
);
bind
(
PlaceholderHelper
.
class
).
in
(
Singleton
.
class
);
bind
(
ConfigPropertySourceFactory
.
class
).
in
(
Singleton
.
class
);
bind
(
SpringValueRegistry
.
class
).
in
(
Singleton
.
class
);
}
}
}
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloAnnotationProcessor.java
View file @
c9612f6d
...
...
@@ -32,12 +32,10 @@ public class ApolloAnnotationProcessor extends ApolloProcessor {
ReflectionUtils
.
makeAccessible
(
field
);
ReflectionUtils
.
setField
(
field
,
bean
,
config
);
}
@Override
protected
void
processMethod
(
final
Object
bean
,
String
beanName
,
final
Method
method
)
{
ApolloConfigChangeListener
annotation
=
AnnotationUtils
.
findAnnotation
(
method
,
ApolloConfigChangeListener
.
class
);
if
(
annotation
==
null
)
{
...
...
@@ -53,17 +51,17 @@ public class ApolloAnnotationProcessor extends ApolloProcessor {
ReflectionUtils
.
makeAccessible
(
method
);
String
[]
namespaces
=
annotation
.
value
();
for
(
String
namespace
:
namespaces
)
{
Config
config
=
ConfigService
.
getConfig
(
namespace
);
config
.
addChangeListener
(
new
ConfigChangeListener
()
{
ConfigChangeListener
configChangeListener
=
new
ConfigChangeListener
()
{
@Override
public
void
onChange
(
ConfigChangeEvent
changeEvent
)
{
ReflectionUtils
.
invokeMethod
(
method
,
bean
,
changeEvent
);
}
})
;
}
;
for
(
String
namespace
:
namespaces
)
{
Config
config
=
ConfigService
.
getConfig
(
namespace
);
config
.
addChangeListener
(
configChangeListener
);
}
}
}
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloConfigRegistrar.java
View file @
c9612f6d
...
...
@@ -35,8 +35,7 @@ public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar {
BeanRegistrationUtil
.
registerBeanDefinitionIfNotExists
(
registry
,
SpringValueProcessor
.
class
.
getName
(),
SpringValueProcessor
.
class
);
BeanRegistrationUtil
.
registerBeanDefinitionIfNotExists
(
registry
,
SpringValueDefinitionProcessor
.
class
.
getName
(),
SpringValueDefinitionProcessor
.
class
);
BeanRegistrationUtil
.
registerBeanDefinitionIfNotExists
(
registry
,
ApolloJSONValueProcessor
.
class
.
getName
(),
ApolloJSONValueProcessor
.
class
);
BeanRegistrationUtil
.
registerBeanDefinitionIfNotExists
(
registry
,
ApolloJsonValueProcessor
.
class
.
getName
(),
ApolloJsonValueProcessor
.
class
);
}
}
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloJ
SON
Value.java
→
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloJ
son
Value.java
View file @
c9612f6d
...
...
@@ -7,13 +7,28 @@ import java.lang.annotation.RetentionPolicy;
import
java.lang.annotation.Target
;
/**
* Create by zhangzheng on 2018/2/6
* Use this annotation to inject json property from Apollo, support the same format as Spring @Value.
*
* <p>Usage example:</p>
* <pre class="code">
* // Inject the json property value for type SomeObject.
* // Suppose SomeObject has 2 properties, someString and someInt, then the possible config
* // in Apollo is someJsonPropertyKey={"someString":"someValue", "someInt":10}.
* @ApolloJsonValue("${someJsonPropertyKey:someDefaultValue}")
* private SomeObject someObject;
* </pre>
*
* Create by zhangzheng on 2018/3/6
*
* @see org.springframework.beans.factory.annotation.Value
*/
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
({
ElementType
.
FIELD
,
ElementType
.
METHOD
})
@Documented
public
@interface
ApolloJ
SON
Value
{
public
@interface
ApolloJ
son
Value
{
/**
* The actual value expression: e.g. "${someJsonPropertyKey:someDefaultValue}".
*/
String
value
();
}
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloJ
SON
ValueProcessor.java
→
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloJ
son
ValueProcessor.java
View file @
c9612f6d
package
com
.
ctrip
.
framework
.
apollo
.
spring
.
annotation
;
import
com.ctrip.framework.apollo.build.ApolloInjector
;
import
com.ctrip.framework.apollo.spring.
config
.AutoUpdateConfigChangeListener
;
import
com.ctrip.framework.apollo.spring.
property
.AutoUpdateConfigChangeListener
;
import
com.ctrip.framework.apollo.spring.property.PlaceholderHelper
;
import
com.ctrip.framework.apollo.spring.property.SpringValue
;
import
com.ctrip.framework.apollo.spring.property.SpringValueRegistry
;
import
com.ctrip.framework.apollo.util.ConfigUtil
;
import
com.ctrip.framework.foundation.internals.Utils
;
import
com.google.common.base.Preconditions
;
import
com.google.gson.Gson
;
import
com.google.gson.reflect.TypeToken
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Method
;
import
java.lang.reflect.Type
;
...
...
@@ -18,96 +19,103 @@ import org.springframework.beans.BeansException;
import
org.springframework.beans.factory.BeanFactory
;
import
org.springframework.beans.factory.BeanFactoryAware
;
import
org.springframework.beans.factory.config.ConfigurableBeanFactory
;
import
org.springframework.context.EnvironmentAware
;
import
org.springframework.core.annotation.AnnotationUtils
;
import
org.springframework.
core.env.Environment
;
import
org.springframework.
util.ReflectionUtils
;
/**
* Create by zhangzheng on 2018/2/6
*/
public
class
ApolloJ
SONValueProcessor
extends
ApolloProcessor
implements
EnvironmentAware
,
BeanFactoryAware
{
public
class
ApolloJ
sonValueProcessor
extends
ApolloProcessor
implements
BeanFactoryAware
{
private
Logger
logger
=
LoggerFactory
.
getLogger
(
ApolloJSONValueProcessor
.
class
);
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
ApolloJsonValueProcessor
.
class
);
private
static
final
Gson
gson
=
new
Gson
();
private
static
Gson
gson
=
new
Gson
();
private
Environment
environment
;
private
final
ConfigUtil
configUtil
;
private
final
PlaceholderHelper
placeholderHelper
;
private
final
SpringValueRegistry
springValueRegistry
;
private
ConfigurableBeanFactory
beanFactory
;
public
ApolloJ
SON
ValueProcessor
()
{
public
ApolloJ
son
ValueProcessor
()
{
configUtil
=
ApolloInjector
.
getInstance
(
ConfigUtil
.
class
);
placeholderHelper
=
ApolloInjector
.
getInstance
(
PlaceholderHelper
.
class
);
springValueRegistry
=
ApolloInjector
.
getInstance
(
SpringValueRegistry
.
class
);
}
@Override
protected
void
processField
(
Object
bean
,
String
beanName
,
Field
field
)
{
ApolloJ
SONValue
apolloJSONValue
=
AnnotationUtils
.
getAnnotation
(
field
,
ApolloJSON
Value
.
class
);
if
(
apolloJ
SON
Value
==
null
)
{
protected
void
processField
(
Object
bean
,
String
beanName
,
Field
field
)
{
ApolloJ
sonValue
apolloJsonValue
=
AnnotationUtils
.
getAnnotation
(
field
,
ApolloJson
Value
.
class
);
if
(
apolloJ
son
Value
==
null
)
{
return
;
}
try
{
String
placeHolder
=
apolloJSONValue
.
value
();
String
propertyValue
=
beanFactory
.
resolveEmbeddedValue
(
placeHolder
);
if
(!
Utils
.
isBlank
(
propertyValue
)){
String
placeholder
=
apolloJsonValue
.
value
();
Object
propertyValue
=
placeholderHelper
.
resolvePropertyValue
(
beanFactory
,
beanName
,
placeholder
);
// propertyValue will never be null, as @ApolloJsonValue will not allow that
if
(!(
propertyValue
instanceof
String
))
{
return
;
}
boolean
accessible
=
field
.
isAccessible
();
field
.
setAccessible
(
true
);
field
.
set
(
bean
,
gson
.
fromJson
(
propertyValue
,
field
.
getGenericType
()));
ReflectionUtils
.
setField
(
field
,
bean
,
parseJsonValue
((
String
)
propertyValue
,
field
.
getGenericType
()));
field
.
setAccessible
(
accessible
);
if
(
configUtil
.
isAutoUpdateInjectedSpringPropertiesEnabled
())
{
Set
<
String
>
keys
=
placeholderHelper
.
extractPlaceholderKeys
(
placeholder
);
for
(
String
key
:
keys
)
{
SpringValue
springValue
=
new
SpringValue
(
key
,
placeholder
,
bean
,
beanName
,
field
,
true
);
springValueRegistry
.
register
(
key
,
springValue
);
logger
.
debug
(
"Monitoring {}"
,
springValue
);
}
if
(
configUtil
.
isAutoUpdateInjectedSpringPropertiesEnabled
()){
Set
<
String
>
keys
=
placeholderHelper
.
extractPlaceholderKeys
(
placeHolder
);
for
(
String
key:
keys
){
SpringValue
springValue
=
new
SpringValue
(
key
,
placeHolder
,
bean
,
beanName
,
field
,
true
);
AutoUpdateConfigChangeListener
.
monitor
.
put
(
key
,
springValue
);
logger
.
debug
(
"Monitoring "
,
springValue
);
}
}
}
catch
(
Exception
e
)
{
logger
.
error
(
"set json value exception"
,
e
);
}
}
@Override
protected
void
processMethod
(
Object
bean
,
String
beanName
,
Method
method
)
{
ApolloJsonValue
apolloJsonValue
=
AnnotationUtils
.
getAnnotation
(
method
,
ApolloJsonValue
.
class
);
if
(
apolloJsonValue
==
null
)
{
return
;
}
String
placeHolder
=
apolloJsonValue
.
value
();
Object
propertyValue
=
placeholderHelper
.
resolvePropertyValue
(
beanFactory
,
beanName
,
placeHolder
);
ApolloJSONValue
apolloJSONValue
=
AnnotationUtils
.
getAnnotation
(
method
,
ApolloJSONValue
.
class
);
if
(
apolloJSONValue
==
null
)
{
// propertyValue will never be null, as @ApolloJsonValue will not allow that
if
(
!(
propertyValue
instanceof
String
)
)
{
return
;
}
try
{
String
placeHolder
=
apolloJSONValue
.
value
();
String
propertyValue
=
beanFactory
.
resolveEmbeddedValue
(
placeHolder
);
if
(!
Utils
.
isBlank
(
propertyValue
)){
boolean
accessible
=
method
.
isAccessible
();
method
.
setAccessible
(
true
);
Type
[]
types
=
method
.
getGenericParameterTypes
();
Preconditions
.
checkArgument
(
types
.
length
==
1
,
"Ignore @Value setter {}.{}, expecting 1 parameter, actual {} parameters"
,
Preconditions
.
checkArgument
(
types
.
length
==
1
,
"Ignore @Value setter {}.{}, expecting 1 parameter, actual {} parameters"
,
bean
.
getClass
().
getName
(),
method
.
getName
(),
method
.
getParameterTypes
().
length
);
method
.
invoke
(
bean
,
gson
.
fromJson
(
propertyValue
,
types
[
0
]));
boolean
accessible
=
method
.
isAccessible
();
method
.
setAccessible
(
true
);
ReflectionUtils
.
invokeMethod
(
method
,
bean
,
parseJsonValue
((
String
)
propertyValue
,
types
[
0
]));
method
.
setAccessible
(
accessible
);
}
if
(
configUtil
.
isAutoUpdateInjectedSpringPropertiesEnabled
())
{
if
(
configUtil
.
isAutoUpdateInjectedSpringPropertiesEnabled
())
{
Set
<
String
>
keys
=
placeholderHelper
.
extractPlaceholderKeys
(
placeHolder
);
for
(
String
key:
keys
)
{
SpringValue
springValue
=
new
SpringValue
(
key
,
apolloJSONValue
.
value
(),
bean
,
beanName
,
method
,
true
);
AutoUpdateConfigChangeListener
.
monitor
.
put
(
key
,
springVal
ue
);
logger
.
debug
(
"Monitoring "
,
springValue
);
}
for
(
String
key
:
keys
)
{
SpringValue
springValue
=
new
SpringValue
(
key
,
apolloJsonValue
.
value
(),
bean
,
beanName
,
method
,
tr
ue
);
springValueRegistry
.
register
(
key
,
springValue
);
logger
.
debug
(
"Monitoring {}"
,
springValue
);
}
}
catch
(
Exception
e
)
{
logger
.
error
(
"set json value exception"
,
e
);
}
}
@Override
public
void
setEnvironment
(
Environment
environment
)
{
this
.
environment
=
environment
;
private
Object
parseJsonValue
(
String
json
,
Type
targetType
)
{
try
{
return
gson
.
fromJson
(
json
,
targetType
);
}
catch
(
Throwable
ex
)
{
logger
.
error
(
"Parsing json '{}' to type {} failed!"
,
json
,
targetType
,
ex
);
throw
ex
;
}
}
@Override
...
...
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloProcessor.java
View file @
c9612f6d
...
...
@@ -35,21 +35,18 @@ public abstract class ApolloProcessor implements BeanPostProcessor, PriorityOrde
/**
* subclass should implement this method to process field
* @param bean
* @param field
*/
protected
abstract
void
processField
(
Object
bean
,
String
beanName
,
Field
field
);
/**
* subclass should implement this method to process method
* @param bean
* @param method
*/
protected
abstract
void
processMethod
(
Object
bean
,
String
beanName
,
Method
method
);
@Override
public
int
getOrder
()
{
//make it as late as possible
return
Ordered
.
LOWEST_PRECEDENCE
;
}
...
...
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/SpringValueProcessor.java
View file @
c9612f6d
package
com
.
ctrip
.
framework
.
apollo
.
spring
.
annotation
;
import
com.ctrip.framework.apollo.spring.config.AutoUpdateConfigChangeListener
;
import
com.ctrip.framework.apollo.build.ApolloInjector
;
import
com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener
;
import
com.ctrip.framework.apollo.spring.property.PlaceholderHelper
;
import
com.ctrip.framework.apollo.spring.property.SpringValue
;
import
com.ctrip.framework.apollo.spring.property.SpringValueDefinition
;
import
com.ctrip.framework.apollo.spring.property.SpringValueDefinitionProcessor
;
import
com.ctrip.framework.apollo.spring.property.SpringValueRegistry
;
import
com.ctrip.framework.apollo.util.ConfigUtil
;
import
com.google.common.collect.LinkedListMultimap
;
import
com.google.common.collect.Multimap
;
import
java.beans.PropertyDescriptor
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Method
;
import
java.util.Collection
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Objects
;
import
java.util.Set
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.BeanUtils
;
import
org.springframework.beans.BeansException
;
import
org.springframework.beans.TypeConverter
;
import
org.springframework.beans.factory.BeanFactory
;
import
org.springframework.beans.factory.BeanFactoryAware
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.beans.factory.config.BeanDefinition
;
import
org.springframework.beans.factory.config.BeanExpressionContext
;
import
org.springframework.beans.factory.config.BeanFactoryPostProcessor
;
import
org.springframework.beans.factory.config.BeanPostProcessor
;
import
org.springframework.beans.factory.config.ConfigurableBeanFactory
;
import
org.springframework.beans.factory.config.ConfigurableListableBeanFactory
;
import
org.springframework.beans.factory.config.Scope
;
import
org.springframework.context.EnvironmentAware
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.core.Ordered
;
import
org.springframework.core.PriorityOrdered
;
import
org.springframework.core.env.Environment
;
import
org.springframework.util.CollectionUtils
;
import
org.springframework.util.ReflectionUtils
;
import
com.ctrip.framework.apollo.ConfigChangeListener
;
import
com.ctrip.framework.apollo.build.ApolloInjector
;
import
com.ctrip.framework.apollo.model.ConfigChange
;
import
com.ctrip.framework.apollo.model.ConfigChangeEvent
;
import
com.ctrip.framework.apollo.spring.config.ConfigPropertySource
;
import
com.ctrip.framework.apollo.spring.config.ConfigPropertySourceFactory
;
import
com.ctrip.framework.apollo.spring.property.PlaceholderHelper
;
import
com.ctrip.framework.apollo.spring.property.SpringValue
;
import
com.ctrip.framework.apollo.spring.property.SpringValueDefinition
;
import
com.ctrip.framework.apollo.spring.property.SpringValueDefinitionProcessor
;
import
com.ctrip.framework.apollo.util.ConfigUtil
;
import
com.google.common.collect.LinkedListMultimap
;
import
com.google.common.collect.Multimap
;
/**
* Spring value processor of field or method which has @Value and xml config placeholders.
...
...
@@ -59,20 +36,23 @@ public class SpringValueProcessor extends ApolloProcessor implements BeanFactor
private
final
ConfigUtil
configUtil
;
private
final
PlaceholderHelper
placeholderHelper
;
private
final
SpringValueRegistry
springValueRegistry
;
private
static
Multimap
<
String
,
SpringValueDefinition
>
beanName2SpringValueDefinitions
=
LinkedListMultimap
.
create
();
private
static
Multimap
<
String
,
SpringValueDefinition
>
beanName2SpringValueDefinitions
=
LinkedListMultimap
.
create
();
public
SpringValueProcessor
()
{
configUtil
=
ApolloInjector
.
getInstance
(
ConfigUtil
.
class
);
placeholderHelper
=
ApolloInjector
.
getInstance
(
PlaceholderHelper
.
class
);
springValueRegistry
=
ApolloInjector
.
getInstance
(
SpringValueRegistry
.
class
);
}
@Override
public
void
postProcessBeanFactory
(
ConfigurableListableBeanFactory
beanFactory
)
throws
BeansException
{
public
void
postProcessBeanFactory
(
ConfigurableListableBeanFactory
beanFactory
)
throws
BeansException
{
if
(
configUtil
.
isAutoUpdateInjectedSpringPropertiesEnabled
())
{
beanName2SpringValueDefinitions
=
SpringValueDefinitionProcessor
.
getBeanName2SpringValueDefinitions
();
beanName2SpringValueDefinitions
=
SpringValueDefinitionProcessor
.
getBeanName2SpringValueDefinitions
();
}
}
...
...
@@ -102,7 +82,7 @@ public class SpringValueProcessor extends ApolloProcessor implements BeanFactor
for
(
String
key
:
keys
)
{
SpringValue
springValue
=
new
SpringValue
(
key
,
value
.
value
(),
bean
,
beanName
,
field
,
false
);
AutoUpdateConfigChangeListener
.
monitor
.
put
(
key
,
springValue
);
springValueRegistry
.
register
(
key
,
springValue
);
logger
.
debug
(
"Monitoring {}"
,
springValue
);
}
}
...
...
@@ -132,13 +112,12 @@ public class SpringValueProcessor extends ApolloProcessor implements BeanFactor
for
(
String
key
:
keys
)
{
SpringValue
springValue
=
new
SpringValue
(
key
,
value
.
value
(),
bean
,
beanName
,
method
,
false
);
AutoUpdateConfigChangeListener
.
monitor
.
put
(
key
,
springValue
);
springValueRegistry
.
register
(
key
,
springValue
);
logger
.
debug
(
"Monitoring {}"
,
springValue
);
}
}
private
void
processBeanPropertyValues
(
Object
bean
,
String
beanName
)
{
Collection
<
SpringValueDefinition
>
propertySpringValues
=
beanName2SpringValueDefinitions
.
get
(
beanName
);
...
...
@@ -156,7 +135,7 @@ public class SpringValueProcessor extends ApolloProcessor implements BeanFactor
}
SpringValue
springValue
=
new
SpringValue
(
definition
.
getKey
(),
definition
.
getPlaceholder
(),
bean
,
beanName
,
method
,
false
);
AutoUpdateConfigChangeListener
.
monitor
.
put
(
definition
.
getKey
(),
springValue
);
springValueRegistry
.
register
(
definition
.
getKey
(),
springValue
);
logger
.
debug
(
"Monitoring {}"
,
springValue
);
}
catch
(
Throwable
ex
)
{
logger
.
error
(
"Failed to enable auto update feature for {}.{}"
,
bean
.
getClass
(),
...
...
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/ConfigPropertySourcesProcessor.java
View file @
c9612f6d
...
...
@@ -2,7 +2,7 @@ package com.ctrip.framework.apollo.spring.config;
import
com.ctrip.framework.apollo.spring.annotation.SpringValueProcessor
;
import
com.ctrip.framework.apollo.spring.property.SpringValueDefinitionProcessor
;
import
com.ctrip.framework.apollo.spring.annotation.ApolloJ
SON
ValueProcessor
;
import
com.ctrip.framework.apollo.spring.annotation.ApolloJ
son
ValueProcessor
;
import
org.springframework.beans.BeansException
;
import
org.springframework.beans.factory.support.BeanDefinitionRegistry
;
import
org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
;
...
...
@@ -26,8 +26,8 @@ public class ConfigPropertySourcesProcessor extends PropertySourcesProcessor
BeanRegistrationUtil
.
registerBeanDefinitionIfNotExists
(
registry
,
ApolloAnnotationProcessor
.
class
.
getName
(),
ApolloAnnotationProcessor
.
class
);
BeanRegistrationUtil
.
registerBeanDefinitionIfNotExists
(
registry
,
SpringValueProcessor
.
class
.
getName
(),
SpringValueProcessor
.
class
);
BeanRegistrationUtil
.
registerBeanDefinitionIfNotExists
(
registry
,
ApolloJ
SON
ValueProcessor
.
class
.
getName
(),
ApolloJ
SON
ValueProcessor
.
class
);
BeanRegistrationUtil
.
registerBeanDefinitionIfNotExists
(
registry
,
ApolloJ
son
ValueProcessor
.
class
.
getName
(),
ApolloJ
son
ValueProcessor
.
class
);
processSpringValueDefinition
(
registry
);
}
...
...
@@ -41,7 +41,5 @@ public class ConfigPropertySourcesProcessor extends PropertySourcesProcessor
SpringValueDefinitionProcessor
springValueDefinitionProcessor
=
new
SpringValueDefinitionProcessor
();
springValueDefinitionProcessor
.
postProcessBeanDefinitionRegistry
(
registry
);
}
}
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesProcessor.java
View file @
c9612f6d
package
com
.
ctrip
.
framework
.
apollo
.
spring
.
config
;
import
com.ctrip.framework.apollo.build.ApolloInjector
;
import
com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener
;
import
com.ctrip.framework.apollo.util.ConfigUtil
;
import
com.google.common.collect.ImmutableSortedSet
;
import
com.google.common.collect.LinkedHashMultimap
;
...
...
@@ -10,6 +11,7 @@ import com.ctrip.framework.apollo.Config;
import
com.ctrip.framework.apollo.ConfigService
;
import
java.util.List
;
import
java.util.concurrent.atomic.AtomicBoolean
;
import
org.springframework.beans.BeansException
;
import
org.springframework.beans.factory.config.BeanFactoryPostProcessor
;
import
org.springframework.beans.factory.config.ConfigurableListableBeanFactory
;
...
...
@@ -35,6 +37,7 @@ import java.util.Iterator;
*/
public
class
PropertySourcesProcessor
implements
BeanFactoryPostProcessor
,
EnvironmentAware
,
PriorityOrdered
{
private
static
final
Multimap
<
Integer
,
String
>
NAMESPACE_NAMES
=
LinkedHashMultimap
.
create
();
private
static
final
AtomicBoolean
INITIALIZED
=
new
AtomicBoolean
(
false
);
private
final
ConfigPropertySourceFactory
configPropertySourceFactory
=
ApolloInjector
.
getInstance
(
ConfigPropertySourceFactory
.
class
);
...
...
@@ -47,16 +50,14 @@ public class PropertySourcesProcessor implements BeanFactoryPostProcessor, Envir
@Override
public
void
postProcessBeanFactory
(
ConfigurableListableBeanFactory
beanFactory
)
throws
BeansException
{
if
(
INITIALIZED
.
compareAndSet
(
false
,
true
))
{
initializePropertySources
();
if
(
configUtil
.
isAutoUpdateInjectedSpringPropertiesEnabled
())
{
List
<
ConfigPropertySource
>
configPropertySources
=
configPropertySourceFactory
.
getAllConfigPropertySources
();
for
(
ConfigPropertySource
configPropertySource
:
configPropertySources
)
{
configPropertySource
.
addChangeListener
(
new
AutoUpdateConfigChangeListener
(
environment
,
beanFactory
));
}
initializeAutoUpdatePropertiesFeature
(
beanFactory
);
}
}
pr
otected
void
initializePropertySources
()
{
pr
ivate
void
initializePropertySources
()
{
if
(
environment
.
getPropertySources
().
contains
(
PropertySourcesConstants
.
APOLLO_PROPERTY_SOURCE_NAME
))
{
//already initialized
return
;
...
...
@@ -86,6 +87,20 @@ public class PropertySourcesProcessor implements BeanFactoryPostProcessor, Envir
}
}
private
void
initializeAutoUpdatePropertiesFeature
(
ConfigurableListableBeanFactory
beanFactory
)
{
if
(!
configUtil
.
isAutoUpdateInjectedSpringPropertiesEnabled
())
{
return
;
}
AutoUpdateConfigChangeListener
autoUpdateConfigChangeListener
=
new
AutoUpdateConfigChangeListener
(
environment
,
beanFactory
);
List
<
ConfigPropertySource
>
configPropertySources
=
configPropertySourceFactory
.
getAllConfigPropertySources
();
for
(
ConfigPropertySource
configPropertySource
:
configPropertySources
)
{
configPropertySource
.
addChangeListener
(
autoUpdateConfigChangeListener
);
}
}
@Override
public
void
setEnvironment
(
Environment
environment
)
{
//it is safe enough to cast as all known environment is derived from ConfigurableEnvironment
...
...
@@ -95,6 +110,7 @@ public class PropertySourcesProcessor implements BeanFactoryPostProcessor, Envir
//only for test
private
static
void
reset
()
{
NAMESPACE_NAMES
.
clear
();
INITIALIZED
.
set
(
false
);
}
@Override
...
...
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/
config
/AutoUpdateConfigChangeListener.java
→
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/
property
/AutoUpdateConfigChangeListener.java
View file @
c9612f6d
package
com
.
ctrip
.
framework
.
apollo
.
spring
.
config
;
package
com
.
ctrip
.
framework
.
apollo
.
spring
.
property
;
import
com.ctrip.framework.apollo.ConfigChangeListener
;
import
com.ctrip.framework.apollo.build.ApolloInjector
;
import
com.ctrip.framework.apollo.model.ConfigChange
;
import
com.ctrip.framework.apollo.model.ConfigChangeEvent
;
import
com.ctrip.framework.apollo.spring.annotation.SpringValueProcessor
;
import
com.ctrip.framework.apollo.spring.property.SpringValue
;
import
com.google.common.collect.LinkedListMultimap
;
import
com.google.common.collect.Multimap
;
import
com.google.gson.Gson
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Type
;
...
...
@@ -16,11 +14,8 @@ import java.util.Set;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.TypeConverter
;
import
org.springframework.beans.factory.BeanFactory
;
import
org.springframework.beans.factory.config.BeanDefinition
;
import
org.springframework.beans.factory.config.BeanExpressionContext
;
import
org.springframework.beans.factory.config.ConfigurableBeanFactory
;
import
org.springframework.beans.factory.config.
Scope
;
import
org.springframework.beans.factory.config.
ConfigurableListableBeanFactory
;
import
org.springframework.core.env.Environment
;
import
org.springframework.util.CollectionUtils
;
...
...
@@ -28,24 +23,26 @@ import org.springframework.util.CollectionUtils;
* Create by zhangzheng on 2018/3/6
*/
public
class
AutoUpdateConfigChangeListener
implements
ConfigChangeListener
{
public
static
final
Multimap
<
String
,
SpringValue
>
monitor
=
LinkedListMultimap
.
create
();
private
Environment
environment
;
private
ConfigurableBeanFactory
beanFactory
;
private
TypeConverter
typeConverter
;
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
SpringValueProcessor
.
class
);
private
final
boolean
typeConverterHasConvertIfNecessaryWithFieldParameter
;
private
Gson
gson
=
new
Gson
();
public
AutoUpdateConfigChangeListener
(
Environment
environment
,
BeanFactory
beanFactory
){
typeConverterHasConvertIfNecessaryWithFieldParameter
=
testTypeConverterHasConvertIfNecessaryWithFieldParameter
();
this
.
beanFactory
=
(
ConfigurableBeanFactory
)
beanFactory
;
private
final
boolean
typeConverterHasConvertIfNecessaryWithFieldParameter
;
private
final
Environment
environment
;
private
final
ConfigurableBeanFactory
beanFactory
;
private
final
TypeConverter
typeConverter
;
private
final
PlaceholderHelper
placeholderHelper
;
private
final
SpringValueRegistry
springValueRegistry
;
private
final
Gson
gson
;
public
AutoUpdateConfigChangeListener
(
Environment
environment
,
ConfigurableListableBeanFactory
beanFactory
){
this
.
typeConverterHasConvertIfNecessaryWithFieldParameter
=
testTypeConverterHasConvertIfNecessaryWithFieldParameter
();
this
.
beanFactory
=
beanFactory
;
this
.
typeConverter
=
this
.
beanFactory
.
getTypeConverter
();
this
.
environment
=
environment
;
this
.
placeholderHelper
=
ApolloInjector
.
getInstance
(
PlaceholderHelper
.
class
);
this
.
springValueRegistry
=
ApolloInjector
.
getInstance
(
SpringValueRegistry
.
class
);
this
.
gson
=
new
Gson
();
}
@Override
public
void
onChange
(
ConfigChangeEvent
changeEvent
)
{
Set
<
String
>
keys
=
changeEvent
.
changedKeys
();
...
...
@@ -54,7 +51,7 @@ public class AutoUpdateConfigChangeListener implements ConfigChangeListener{
}
for
(
String
key
:
keys
)
{
// 1. check whether the changed key is relevant
Collection
<
SpringValue
>
targetValues
=
monitor
.
get
(
key
);
Collection
<
SpringValue
>
targetValues
=
springValueRegistry
.
get
(
key
);
if
(
targetValues
==
null
||
targetValues
.
isEmpty
())
{
continue
;
}
...
...
@@ -67,27 +64,10 @@ public class AutoUpdateConfigChangeListener implements ConfigChangeListener{
// 3. update the value
for
(
SpringValue
val
:
targetValues
)
{
if
(
val
.
isJson
()){
updateJsonValue
(
val
);
}
else
{
updateSpringValue
(
val
);
}
}
}
}
private
void
updateJsonValue
(
SpringValue
springValue
){
try
{
Type
type
=
springValue
.
getGenericType
();
String
propertyValue
=
beanFactory
.
resolveEmbeddedValue
(
springValue
.
getPlaceholder
());
Object
val
=
gson
.
fromJson
(
propertyValue
,
type
);
springValue
.
update
(
val
);
logger
.
debug
(
"Auto update apollo changed value successfully, new value: {}, {}"
,
val
,
springValue
.
toString
());
}
catch
(
Throwable
ex
)
{
logger
.
error
(
"Auto update apollo changed value failed, {}"
,
springValue
.
toString
(),
ex
);
}
}
private
void
updateSpringValue
(
SpringValue
springValue
)
{
try
{
...
...
@@ -106,13 +86,13 @@ public class AutoUpdateConfigChangeListener implements ConfigChangeListener{
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, java.lang.String, java.util.Set, org.springframework.beans.TypeConverter)
*/
private
Object
resolvePropertyValue
(
SpringValue
springValue
)
{
String
strVal
=
beanFactory
.
resolveEmbeddedValue
(
springValue
.
getPlaceholder
());
Object
value
;
BeanDefinition
bd
=
(
beanFactory
.
containsBean
(
springValue
.
getBeanName
())
?
beanFactory
.
getMergedBeanDefinition
(
springValue
.
getBeanName
())
:
null
);
value
=
evaluateBeanDefinitionString
(
strVal
,
bd
);
// value will never be null, as @Value and @ApolloJsonValue will not allow that
Object
value
=
placeholderHelper
.
resolvePropertyValue
(
beanFactory
,
springValue
.
getBeanName
(),
springValue
.
getPlaceholder
());
if
(
springValue
.
isJson
())
{
value
=
parseJsonValue
((
String
)
value
,
springValue
.
getGenericType
());
}
else
{
if
(
springValue
.
isField
())
{
// org.springframework.beans.TypeConverter#convertIfNecessary(java.lang.Object, java.lang.Class, java.lang.reflect.Field) is available from Spring 3.2.0+
if
(
typeConverterHasConvertIfNecessaryWithFieldParameter
)
{
...
...
@@ -125,16 +105,18 @@ public class AutoUpdateConfigChangeListener implements ConfigChangeListener{
value
=
this
.
typeConverter
.
convertIfNecessary
(
value
,
springValue
.
getTargetType
(),
springValue
.
getMethodParameter
());
}
}
return
value
;
}
private
Object
evaluateBeanDefinitionString
(
String
value
,
BeanDefinition
beanDefinition
)
{
if
(
beanFactory
.
getBeanExpressionResolver
()
==
null
)
{
return
value
;
private
Object
parseJsonValue
(
String
json
,
Type
targetType
)
{
try
{
return
gson
.
fromJson
(
json
,
targetType
);
}
catch
(
Throwable
ex
)
{
logger
.
error
(
"Parsing json '{}' to type {} failed!"
,
json
,
targetType
,
ex
);
throw
ex
;
}
Scope
scope
=
(
beanDefinition
!=
null
?
beanFactory
.
getRegisteredScope
(
beanDefinition
.
getScope
())
:
null
);
return
beanFactory
.
getBeanExpressionResolver
().
evaluate
(
value
,
new
BeanExpressionContext
(
beanFactory
,
scope
));
}
private
boolean
testTypeConverterHasConvertIfNecessaryWithFieldParameter
()
{
...
...
@@ -146,6 +128,4 @@ public class AutoUpdateConfigChangeListener implements ConfigChangeListener{
return
true
;
}
}
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/property/PlaceholderHelper.java
View file @
c9612f6d
...
...
@@ -4,18 +4,15 @@ import com.google.common.base.Strings;
import
com.google.common.collect.Sets
;
import
java.util.Set
;
import
java.util.Stack
;
import
org.springframework.beans.factory.BeanFactory
;
import
org.springframework.beans.factory.config.BeanDefinition
;
import
org.springframework.beans.factory.config.BeanExpressionContext
;
import
org.springframework.beans.factory.config.ConfigurableBeanFactory
;
import
org.springframework.beans.factory.config.Scope
;
import
org.springframework.util.StringUtils
;
/**
* Extract keys from placeholder, e.g.
* <ul>
* <li>${some.key} => "some.key"</li>
* <li>${some.key:${some.other.key:100}} => "some.key", "some.other.key"</li>
* <li>${${some.key}} => "some.key"</li>
* <li>${${some.key:other.key}} => "some.key"</li>
* <li>${${some.key}:${another.key}} => "some.key", "another.key"</li>
* <li>#{new java.text.SimpleDateFormat('${some.key}').parse('${another.key}')} => "some.key", "another.key"</li>
* </ul>
* Placeholder helper functions.
*/
public
class
PlaceholderHelper
{
...
...
@@ -26,6 +23,45 @@ public class PlaceholderHelper {
private
static
final
String
EXPRESSION_PREFIX
=
"#{"
;
private
static
final
String
EXPRESSION_SUFFIX
=
"}"
;
/**
* Resolve placeholder property values, e.g.
* <br />
* <br />
* "${somePropertyValue}" -> "the actual property value"
*/
public
Object
resolvePropertyValue
(
ConfigurableBeanFactory
beanFactory
,
String
beanName
,
String
placeholder
)
{
// resolve string value
String
strVal
=
beanFactory
.
resolveEmbeddedValue
(
placeholder
);
BeanDefinition
bd
=
(
beanFactory
.
containsBean
(
beanName
)
?
beanFactory
.
getMergedBeanDefinition
(
beanName
)
:
null
);
// resolve expressions like "#{systemProperties.myProp}"
return
evaluateBeanDefinitionString
(
beanFactory
,
strVal
,
bd
);
}
private
Object
evaluateBeanDefinitionString
(
ConfigurableBeanFactory
beanFactory
,
String
value
,
BeanDefinition
beanDefinition
)
{
if
(
beanFactory
.
getBeanExpressionResolver
()
==
null
)
{
return
value
;
}
Scope
scope
=
(
beanDefinition
!=
null
?
beanFactory
.
getRegisteredScope
(
beanDefinition
.
getScope
())
:
null
);
return
beanFactory
.
getBeanExpressionResolver
()
.
evaluate
(
value
,
new
BeanExpressionContext
(
beanFactory
,
scope
));
}
/**
* Extract keys from placeholder, e.g.
* <ul>
* <li>${some.key} => "some.key"</li>
* <li>${some.key:${some.other.key:100}} => "some.key", "some.other.key"</li>
* <li>${${some.key}} => "some.key"</li>
* <li>${${some.key:other.key}} => "some.key"</li>
* <li>${${some.key}:${another.key}} => "some.key", "another.key"</li>
* <li>#{new java.text.SimpleDateFormat('${some.key}').parse('${another.key}')} => "some.key", "another.key"</li>
* </ul>
*/
public
Set
<
String
>
extractPlaceholderKeys
(
String
propertyString
)
{
Set
<
String
>
placeholderKeys
=
Sets
.
newHashSet
();
...
...
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/property/SpringValue.java
View file @
c9612f6d
...
...
@@ -31,9 +31,9 @@ public class SpringValue {
this
.
key
=
key
;
this
.
placeholder
=
placeholder
;
this
.
targetType
=
field
.
getType
();
this
.
isJson
=
isJson
;
if
(
isJson
){
this
.
genericType
=
field
.
getGenericType
();
this
.
isJson
=
isJson
;
}
}
...
...
@@ -45,9 +45,9 @@ public class SpringValue {
this
.
placeholder
=
placeholder
;
Class
<?>[]
paramTps
=
method
.
getParameterTypes
();
this
.
targetType
=
paramTps
[
0
];
this
.
isJson
=
isJson
;
if
(
isJson
){
this
.
genericType
=
method
.
getGenericParameterTypes
()[
0
];
this
.
isJson
=
isJson
;
}
}
...
...
apollo-client/src/main/java/com/ctrip/framework/apollo/spring/property/SpringValueRegistry.java
0 → 100644
View file @
c9612f6d
package
com
.
ctrip
.
framework
.
apollo
.
spring
.
property
;
import
com.google.common.collect.LinkedListMultimap
;
import
com.google.common.collect.Multimap
;
import
java.util.Collection
;
public
class
SpringValueRegistry
{
private
final
Multimap
<
String
,
SpringValue
>
registry
=
LinkedListMultimap
.
create
();
public
void
register
(
String
key
,
SpringValue
springValue
)
{
registry
.
put
(
key
,
springValue
);
}
public
Collection
<
SpringValue
>
get
(
String
key
)
{
return
registry
.
get
(
key
);
}
}
apollo-client/src/test/java/com/ctrip/framework/apollo/build/MockInjector.java
View file @
c9612f6d
...
...
@@ -59,5 +59,6 @@ public class MockInjector implements Injector {
public
static
void
reset
()
{
classMap
.
clear
();
classTable
.
clear
();
delegate
=
null
;
}
}
apollo-client/src/test/java/com/ctrip/framework/apollo/spring/AbstractSpringIntegrationTest.java
View file @
c9612f6d
...
...
@@ -5,6 +5,7 @@ import static org.mockito.Mockito.when;
import
com.ctrip.framework.apollo.core.ConfigConsts
;
import
com.ctrip.framework.apollo.internals.ConfigRepository
;
import
com.ctrip.framework.apollo.internals.DefaultInjector
;
import
com.ctrip.framework.apollo.internals.SimpleConfig
;
import
com.ctrip.framework.apollo.spring.property.SpringValueDefinitionProcessor
;
import
com.ctrip.framework.apollo.util.ConfigUtil
;
...
...
@@ -119,6 +120,7 @@ public abstract class AbstractSpringIntegrationTest {
ReflectionUtils
.
invokeMethod
(
CONFIG_SERVICE_RESET
,
null
);
MockInjector
.
reset
();
MockInjector
.
setInstance
(
ConfigManager
.
class
,
new
MockConfigManager
());
MockInjector
.
setDelegate
(
new
DefaultInjector
());
}
protected
static
void
doTearDown
()
{
...
...
apollo-client/src/test/java/com/ctrip/framework/apollo/spring/JavaConfigPlaceholderAutoUpdateTest.java
View file @
c9612f6d
...
...
@@ -2,13 +2,14 @@ package com.ctrip.framework.apollo.spring;
import
static
org
.
junit
.
Assert
.
assertArrayEquals
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
com.ctrip.framework.apollo.build.MockInjector
;
import
com.ctrip.framework.apollo.core.ConfigConsts
;
import
com.ctrip.framework.apollo.internals.SimpleConfig
;
import
com.ctrip.framework.apollo.spring.JavaConfigPlaceholderTest.JsonBean
;
import
com.ctrip.framework.apollo.spring.XmlConfigPlaceholderTest.TestXmlBean
;
import
com.ctrip.framework.apollo.spring.annotation.ApolloJ
SON
Value
;
import
com.ctrip.framework.apollo.spring.annotation.ApolloJ
son
Value
;
import
com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig
;
import
com.ctrip.framework.apollo.util.ConfigUtil
;
import
com.google.common.primitives.Ints
;
...
...
@@ -626,7 +627,7 @@ public class JavaConfigPlaceholderAutoUpdateTest extends AbstractSpringIntegrati
String
someString
=
"someString"
;
String
someNewString
=
"someNewString"
;
String
someJsonProperty
=
"[{\"a\":\"astring\", \"b\":10},{\"a\":\"astring2\", \"b\":20}]"
;
String
someNewJsonProperty
=
"[{\"a\":\"newString\", \"b\":
1
0},{\"a\":\"astring2\", \"b\":20}]"
;
String
someNewJsonProperty
=
"[{\"a\":\"newString\", \"b\":
2
0},{\"a\":\"astring2\", \"b\":20}]"
;
String
someDateFormat
=
"yyyy-MM-dd HH:mm:ss.SSS"
;
Date
someDate
=
assembleDate
(
2018
,
2
,
23
,
20
,
1
,
2
,
123
);
...
...
@@ -664,6 +665,7 @@ public class JavaConfigPlaceholderAutoUpdateTest extends AbstractSpringIntegrati
assertEquals
(
someString
,
bean
.
getStringProperty
());
assertEquals
(
someDate
,
bean
.
getDateProperty
());
assertEquals
(
"astring"
,
bean
.
getJsonBeanList
().
get
(
0
).
getA
());
assertEquals
(
10
,
bean
.
getJsonBeanList
().
get
(
0
).
getB
());
Properties
newProperties
=
new
Properties
();
newProperties
.
setProperty
(
"intProperty"
,
String
.
valueOf
(
someNewInt
));
...
...
@@ -694,6 +696,91 @@ public class JavaConfigPlaceholderAutoUpdateTest extends AbstractSpringIntegrati
assertEquals
(
someNewString
,
bean
.
getStringProperty
());
assertEquals
(
someNewDate
,
bean
.
getDateProperty
());
assertEquals
(
"newString"
,
bean
.
getJsonBeanList
().
get
(
0
).
getA
());
assertEquals
(
20
,
bean
.
getJsonBeanList
().
get
(
0
).
getB
());
}
@Test
public
void
testAutoUpdateJsonValueWithInvalidValue
()
throws
Exception
{
String
someValidValue
=
"{\"a\":\"someString\", \"b\":10}"
;
String
someInvalidValue
=
"someInvalidValue"
;
Properties
properties
=
assembleProperties
(
"jsonProperty"
,
someValidValue
);
SimpleConfig
config
=
prepareConfig
(
ConfigConsts
.
NAMESPACE_APPLICATION
,
properties
);
AnnotationConfigApplicationContext
context
=
new
AnnotationConfigApplicationContext
(
AppConfig10
.
class
);
TestApolloJsonValue
bean
=
context
.
getBean
(
TestApolloJsonValue
.
class
);
JsonBean
jsonBean
=
bean
.
getJsonBean
();
assertEquals
(
"someString"
,
jsonBean
.
getA
());
assertEquals
(
10
,
jsonBean
.
getB
());
Properties
newProperties
=
assembleProperties
(
"jsonProperty"
,
someInvalidValue
);
config
.
onRepositoryChange
(
ConfigConsts
.
NAMESPACE_APPLICATION
,
newProperties
);
TimeUnit
.
MILLISECONDS
.
sleep
(
50
);
// should not change anything
assertTrue
(
jsonBean
==
bean
.
getJsonBean
());
}
@Test
public
void
testAutoUpdateJsonValueWithNoValueAndNoDefaultValue
()
throws
Exception
{
String
someValidValue
=
"{\"a\":\"someString\", \"b\":10}"
;
Properties
properties
=
assembleProperties
(
"jsonProperty"
,
someValidValue
);
SimpleConfig
config
=
prepareConfig
(
ConfigConsts
.
NAMESPACE_APPLICATION
,
properties
);
AnnotationConfigApplicationContext
context
=
new
AnnotationConfigApplicationContext
(
AppConfig10
.
class
);
TestApolloJsonValue
bean
=
context
.
getBean
(
TestApolloJsonValue
.
class
);
JsonBean
jsonBean
=
bean
.
getJsonBean
();
assertEquals
(
"someString"
,
jsonBean
.
getA
());
assertEquals
(
10
,
jsonBean
.
getB
());
Properties
newProperties
=
new
Properties
();
config
.
onRepositoryChange
(
ConfigConsts
.
NAMESPACE_APPLICATION
,
newProperties
);
TimeUnit
.
MILLISECONDS
.
sleep
(
50
);
// should not change anything
assertTrue
(
jsonBean
==
bean
.
getJsonBean
());
}
@Test
public
void
testAutoUpdateJsonValueWithNoValueAndDefaultValue
()
throws
Exception
{
String
someValidValue
=
"{\"a\":\"someString\", \"b\":10}"
;
Properties
properties
=
assembleProperties
(
"jsonProperty"
,
someValidValue
);
SimpleConfig
config
=
prepareConfig
(
ConfigConsts
.
NAMESPACE_APPLICATION
,
properties
);
AnnotationConfigApplicationContext
context
=
new
AnnotationConfigApplicationContext
(
AppConfig11
.
class
);
TestApolloJsonValueWithDefaultValue
bean
=
context
.
getBean
(
TestApolloJsonValueWithDefaultValue
.
class
);
JsonBean
jsonBean
=
bean
.
getJsonBean
();
assertEquals
(
"someString"
,
jsonBean
.
getA
());
assertEquals
(
10
,
jsonBean
.
getB
());
Properties
newProperties
=
new
Properties
();
config
.
onRepositoryChange
(
ConfigConsts
.
NAMESPACE_APPLICATION
,
newProperties
);
TimeUnit
.
MILLISECONDS
.
sleep
(
50
);
JsonBean
newJsonBean
=
bean
.
getJsonBean
();
assertEquals
(
"defaultString"
,
newJsonBean
.
getA
());
assertEquals
(
1
,
newJsonBean
.
getB
());
}
@Configuration
...
...
@@ -812,6 +899,26 @@ public class JavaConfigPlaceholderAutoUpdateTest extends AbstractSpringIntegrati
}
}
@Configuration
@EnableApolloConfig
static
class
AppConfig10
{
@Bean
TestApolloJsonValue
testApolloJsonValue
()
{
return
new
TestApolloJsonValue
();
}
}
@Configuration
@EnableApolloConfig
static
class
AppConfig11
{
@Bean
TestApolloJsonValueWithDefaultValue
testApolloJsonValue
()
{
return
new
TestApolloJsonValueWithDefaultValue
();
}
}
static
class
TestJavaConfigBean
{
@Value
(
"${timeout:100}"
)
...
...
@@ -939,15 +1046,6 @@ public class JavaConfigPlaceholderAutoUpdateTest extends AbstractSpringIntegrati
}
}
static
class
TestJsonPropertyBean
{
@ApolloJSONValue
(
"${jsonPropery}"
)
private
List
<
JsonBean
>
jsonBeanList
;
public
List
<
JsonBean
>
getJsonBeanList
()
{
return
jsonBeanList
;
}
}
static
class
TestAllKindsOfDataTypesBean
{
@Value
(
"${intProperty}"
)
...
...
@@ -980,7 +1078,7 @@ public class JavaConfigPlaceholderAutoUpdateTest extends AbstractSpringIntegrati
@Value
(
"#{new java.text.SimpleDateFormat('${dateFormat}').parse('${dateProperty}')}"
)
private
Date
dateProperty
;
@ApolloJ
SON
Value
(
"${jsonProperty}"
)
@ApolloJ
son
Value
(
"${jsonProperty}"
)
private
List
<
JsonBean
>
jsonBeanList
;
public
int
getIntProperty
()
{
...
...
@@ -1027,4 +1125,25 @@ public class JavaConfigPlaceholderAutoUpdateTest extends AbstractSpringIntegrati
return
jsonBeanList
;
}
}
static
class
TestApolloJsonValue
{
@ApolloJsonValue
(
"${jsonProperty}"
)
private
JsonBean
jsonBean
;
public
JsonBean
getJsonBean
()
{
return
jsonBean
;
}
}
static
class
TestApolloJsonValueWithDefaultValue
{
@ApolloJsonValue
(
"${jsonProperty:{\"a\":\"defaultString\", \"b\":1}}"
)
private
JsonBean
jsonBean
;
public
JsonBean
getJsonBean
()
{
return
jsonBean
;
}
}
}
apollo-client/src/test/java/com/ctrip/framework/apollo/spring/JavaConfigPlaceholderTest.java
View file @
c9612f6d
...
...
@@ -8,10 +8,11 @@ import static org.mockito.Mockito.when;
import
com.ctrip.framework.apollo.Config
;
import
com.ctrip.framework.apollo.core.ConfigConsts
;
import
com.ctrip.framework.apollo.spring.annotation.ApolloJ
SON
Value
;
import
com.ctrip.framework.apollo.spring.annotation.ApolloJ
son
Value
;
import
com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig
;
import
java.util.List
;
import
org.junit.Test
;
import
org.springframework.beans.factory.BeanCreationException
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.context.annotation.AnnotationConfigApplicationContext
;
...
...
@@ -284,15 +285,14 @@ public class JavaConfigPlaceholderTest extends AbstractSpringIntegrationTest {
assertEquals
(
someValue
,
bean
.
getNestedProperty
());
}
@Test
public
void
test
JsonDeserialization
()
{
public
void
test
ApolloJsonValue
()
{
String
someJson
=
"[{\"a\":\"astring\", \"b\":10},{\"a\":\"astring2\", \"b\":20}]"
;
String
otherJson
=
"[{\"a\":\"otherString\", \"b\":10},{\"a\":\"astring2\", \"b\":20}]"
;
Config
config
=
mock
(
Config
.
class
);
when
(
config
.
getProperty
(
eq
(
JSON_PROPERTY
),
anyString
())).
thenReturn
(
String
.
valueOf
(
someJson
)
);
when
(
config
.
getProperty
(
eq
(
OTHER_JSON_PROPERTY
),
anyString
())).
thenReturn
(
String
.
valueOf
(
otherJson
)
);
when
(
config
.
getProperty
(
eq
(
JSON_PROPERTY
),
anyString
())).
thenReturn
(
someJson
);
when
(
config
.
getProperty
(
eq
(
OTHER_JSON_PROPERTY
),
anyString
())).
thenReturn
(
otherJson
);
when
(
config
.
getProperty
(
eq
(
"a"
),
anyString
())).
thenReturn
(
JSON_PROPERTY
);
mockConfig
(
ConfigConsts
.
NAMESPACE_APPLICATION
,
config
);
...
...
@@ -301,13 +301,37 @@ public class JavaConfigPlaceholderTest extends AbstractSpringIntegrationTest {
TestJsonPropertyBean
testJsonPropertyBean
=
context
.
getBean
(
TestJsonPropertyBean
.
class
);
assertEquals
(
2
,
testJsonPropertyBean
.
getJsonBeanList
().
size
());
assertEquals
(
"astring"
,
testJsonPropertyBean
.
getJsonBeanList
().
get
(
0
).
a
);
assertEquals
(
"astring"
,
testJsonPropertyBean
.
getEmbeddedJsonBeanList
().
get
(
0
).
a
);
assertEquals
(
"otherString"
,
testJsonPropertyBean
.
getOtherJsonBeanList
().
get
(
0
).
a
);
assertEquals
(
"astring"
,
testJsonPropertyBean
.
getJsonBeanList
().
get
(
0
).
getA
());
assertEquals
(
10
,
testJsonPropertyBean
.
getJsonBeanList
().
get
(
0
).
getB
());
assertEquals
(
"astring2"
,
testJsonPropertyBean
.
getJsonBeanList
().
get
(
1
).
getA
());
assertEquals
(
20
,
testJsonPropertyBean
.
getJsonBeanList
().
get
(
1
).
getB
());
assertEquals
(
testJsonPropertyBean
.
getJsonBeanList
(),
testJsonPropertyBean
.
getEmbeddedJsonBeanList
());
assertEquals
(
"otherString"
,
testJsonPropertyBean
.
getOtherJsonBeanList
().
get
(
0
).
getA
());
assertEquals
(
10
,
testJsonPropertyBean
.
getOtherJsonBeanList
().
get
(
0
).
getB
());
assertEquals
(
"astring2"
,
testJsonPropertyBean
.
getOtherJsonBeanList
().
get
(
1
).
getA
());
assertEquals
(
20
,
testJsonPropertyBean
.
getOtherJsonBeanList
().
get
(
1
).
getB
());
}
@Test
(
expected
=
BeanCreationException
.
class
)
public
void
testApolloJsonValueWithInvalidJson
()
throws
Exception
{
String
someInvalidJson
=
"someInvalidJson"
;
Config
config
=
mock
(
Config
.
class
);
when
(
config
.
getProperty
(
eq
(
JSON_PROPERTY
),
anyString
())).
thenReturn
(
someInvalidJson
);
when
(
config
.
getProperty
(
eq
(
OTHER_JSON_PROPERTY
),
anyString
())).
thenReturn
(
someInvalidJson
);
when
(
config
.
getProperty
(
eq
(
"a"
),
anyString
())).
thenReturn
(
JSON_PROPERTY
);
mockConfig
(
ConfigConsts
.
NAMESPACE_APPLICATION
,
config
);
new
AnnotationConfigApplicationContext
(
AppConfig8
.
class
).
getBean
(
TestJsonPropertyBean
.
class
);
}
@Test
(
expected
=
BeanCreationException
.
class
)
public
void
testApolloJsonValueWithNoPropertyValue
()
throws
Exception
{
Config
config
=
mock
(
Config
.
class
);
mockConfig
(
ConfigConsts
.
NAMESPACE_APPLICATION
,
config
);
new
AnnotationConfigApplicationContext
(
AppConfig8
.
class
);
}
private
void
check
(
int
expectedTimeout
,
int
expectedBatch
,
Class
<?>...
annotatedClasses
)
{
AnnotationConfigApplicationContext
context
=
new
AnnotationConfigApplicationContext
(
annotatedClasses
);
...
...
@@ -395,12 +419,11 @@ public class JavaConfigPlaceholderTest extends AbstractSpringIntegrationTest {
static
class
AppConfig8
{
@Bean
TestJsonPropertyBean
testJavaConfigBean
4
()
{
TestJsonPropertyBean
testJavaConfigBean
()
{
return
new
TestJsonPropertyBean
();
}
}
@Component
static
class
TestJavaConfigBean
{
@Value
(
"${timeout:100}"
)
...
...
@@ -476,12 +499,12 @@ public class JavaConfigPlaceholderTest extends AbstractSpringIntegrationTest {
static
class
TestJsonPropertyBean
{
@ApolloJ
SON
Value
(
"${jsonProperty}"
)
@ApolloJ
son
Value
(
"${jsonProperty}"
)
private
List
<
JsonBean
>
jsonBeanList
;
private
List
<
JsonBean
>
otherJsonBeanList
;
@ApolloJ
SON
Value
(
"${${a}}"
)
@ApolloJ
son
Value
(
"${${a}}"
)
private
List
<
JsonBean
>
embeddedJsonBeanList
;
...
...
@@ -489,9 +512,8 @@ public class JavaConfigPlaceholderTest extends AbstractSpringIntegrationTest {
return
jsonBeanList
;
}
@ApolloJSONValue
(
"${otherJsonProperty}"
)
public
void
setOtherJsonBeanList
(
List
<
JsonBean
>
otherJsonBeanList
)
{
@ApolloJsonValue
(
"${otherJsonProperty}"
)
public
void
setOtherJsonBeanList
(
List
<
JsonBean
>
otherJsonBeanList
)
{
this
.
otherJsonBeanList
=
otherJsonBeanList
;
}
...
...
@@ -510,7 +532,7 @@ public class JavaConfigPlaceholderTest extends AbstractSpringIntegrationTest {
private
String
a
;
private
int
b
;
public
String
getA
()
{
String
getA
()
{
return
a
;
}
...
...
@@ -518,12 +540,36 @@ public class JavaConfigPlaceholderTest extends AbstractSpringIntegrationTest {
this
.
a
=
a
;
}
public
int
getB
()
{
int
getB
()
{
return
b
;
}
public
void
setB
(
int
b
)
{
this
.
b
=
b
;
}
@Override
public
boolean
equals
(
Object
o
)
{
if
(
this
==
o
)
{
return
true
;
}
if
(
o
==
null
||
getClass
()
!=
o
.
getClass
())
{
return
false
;
}
JsonBean
jsonBean
=
(
JsonBean
)
o
;
if
(
b
!=
jsonBean
.
b
)
{
return
false
;
}
return
a
!=
null
?
a
.
equals
(
jsonBean
.
a
)
:
jsonBean
.
a
==
null
;
}
@Override
public
int
hashCode
()
{
int
result
=
a
!=
null
?
a
.
hashCode
()
:
0
;
result
=
31
*
result
+
b
;
return
result
;
}
}
}
apollo-demo/src/main/java/com/ctrip/framework/apollo/demo/spring/common/bean/AnnotatedBean.java
View file @
c9612f6d
package
com
.
ctrip
.
framework
.
apollo
.
demo
.
spring
.
common
.
bean
;
import
com.ctrip.framework.apollo.spring.annotation.ApolloJSONValue
;
import
com.google.gson.Gson
;
import
com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue
;
import
java.util.List
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.cloud.context.config.annotation.RefreshScope
;
import
org.springframework.stereotype.Component
;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RefreshScope
@Component
(
"annotatedBean"
)
public
class
AnnotatedBean
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
AnnotatedBean
.
class
);
private
int
timeout
;
private
int
batch
;
@ApolloJSONValue
(
"${objectList}"
)
private
List
<
JsonBean
>
jsonBeans
;
/**
* ApolloJsonValue annotated on fields example, the default value is specified as empty list - []
* <br />
* jsonBeanProperty=[{"someString":"hello","someInt":100},{"someString":"world!","someInt":200}]
*/
@ApolloJsonValue
(
"${jsonBeanProperty:[]}"
)
private
List
<
JsonBean
>
anotherJsonBeans
;
@Value
(
"${batch:100}"
)
public
void
setBatch
(
int
batch
)
{
logger
.
info
(
"updating batch, old value: {}, new value: {}"
,
this
.
batch
,
batch
);
...
...
@@ -34,31 +38,33 @@ public class AnnotatedBean {
this
.
timeout
=
timeout
;
}
@Override
public
String
toString
()
{
return
String
.
format
(
"[AnnotatedBean] timeout: %d, batch: %d, jsonBeans: %s"
,
timeout
,
batch
,
new
Gson
().
toJson
(
jsonBeans
));
/**
* ApolloJsonValue annotated on methods example, the default value is specified as empty list - []
* <br />
* jsonBeanProperty=[{"someString":"hello","someInt":100},{"someString":"world!","someInt":200}]
*/
@ApolloJsonValue
(
"${jsonBeanProperty:[]}"
)
public
void
setJsonBeans
(
List
<
JsonBean
>
jsonBeans
)
{
logger
.
info
(
"updating json beans, old value: {}, new value: {}"
,
this
.
jsonBeans
,
jsonBeans
);
this
.
jsonBeans
=
jsonBeans
;
}
static
class
JsonBean
{
private
String
a
;
private
int
b
;
public
String
getA
()
{
return
a
;
@Override
public
String
toString
()
{
return
String
.
format
(
"[AnnotatedBean] timeout: %d, batch: %d, jsonBeans: %s"
,
timeout
,
batch
,
jsonBeans
);
}
public
void
setA
(
String
a
)
{
this
.
a
=
a
;
}
private
static
class
JsonBean
{
public
int
getB
()
{
return
b
;
}
private
String
someString
;
private
int
someInt
;
public
void
setB
(
int
b
)
{
this
.
b
=
b
;
@Override
public
String
toString
()
{
return
"JsonBean{"
+
"someString='"
+
someString
+
'\''
+
", someInt="
+
someInt
+
'}'
;
}
}
}
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