Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
spring-cloud-netflix
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
spring-cloud-netflix
Commits
a71e13ef
Commit
a71e13ef
authored
Feb 16, 2016
by
Matt Benson
Committed by
Spencer Gibb
Mar 11, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Where a given String conversion is supported, register Feign Param.Expanders
backed by a Feign-specific FormattingConversionService.
parent
b8f11e79
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
175 additions
and
9 deletions
+175
-9
FeignClientsConfiguration.java
...mework/cloud/netflix/feign/FeignClientsConfiguration.java
+18
-3
FeignFormatterRegistrar.java
...ramework/cloud/netflix/feign/FeignFormatterRegistrar.java
+29
-0
SpringMvcContract.java
...mework/cloud/netflix/feign/support/SpringMvcContract.java
+43
-5
SpringMvcContractTests.java
...k/cloud/netflix/feign/support/SpringMvcContractTests.java
+16
-0
FeignClientTests.java
...framework/cloud/netflix/feign/valid/FeignClientTests.java
+69
-1
No files found.
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsConfiguration.java
View file @
a71e13ef
/*
/*
* Copyright 2013-201
5
the original author or authors.
* Copyright 2013-201
6
the original author or authors.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -33,6 +33,9 @@ import org.springframework.cloud.netflix.feign.support.SpringMvcContract;
...
@@ -33,6 +33,9 @@ import org.springframework.cloud.netflix.feign.support.SpringMvcContract;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.context.annotation.Scope
;
import
org.springframework.context.annotation.Scope
;
import
org.springframework.core.convert.ConversionService
;
import
org.springframework.format.support.DefaultFormattingConversionService
;
import
org.springframework.format.support.FormattingConversionService
;
import
com.netflix.hystrix.HystrixCommand
;
import
com.netflix.hystrix.HystrixCommand
;
...
@@ -56,6 +59,9 @@ public class FeignClientsConfiguration {
...
@@ -56,6 +59,9 @@ public class FeignClientsConfiguration {
@Autowired
(
required
=
false
)
@Autowired
(
required
=
false
)
private
List
<
AnnotatedParameterProcessor
>
parameterProcessors
=
new
ArrayList
<>();
private
List
<
AnnotatedParameterProcessor
>
parameterProcessors
=
new
ArrayList
<>();
@Autowired
(
required
=
false
)
private
List
<
FeignFormatterRegistrar
>
feignFormatterRegistrars
=
new
ArrayList
<>();
@Bean
@Bean
@ConditionalOnMissingBean
@ConditionalOnMissingBean
public
Decoder
feignDecoder
()
{
public
Decoder
feignDecoder
()
{
...
@@ -70,8 +76,17 @@ public class FeignClientsConfiguration {
...
@@ -70,8 +76,17 @@ public class FeignClientsConfiguration {
@Bean
@Bean
@ConditionalOnMissingBean
@ConditionalOnMissingBean
public
Contract
feignContract
()
{
public
Contract
feignContract
(
ConversionService
feignConversionService
)
{
return
new
SpringMvcContract
(
this
.
parameterProcessors
);
return
new
SpringMvcContract
(
this
.
parameterProcessors
,
feignConversionService
);
}
@Bean
public
FormattingConversionService
feignConversionService
()
{
FormattingConversionService
conversionService
=
new
DefaultFormattingConversionService
();
for
(
FeignFormatterRegistrar
feignFormatterRegistrar
:
feignFormatterRegistrars
)
{
feignFormatterRegistrar
.
registerFormatters
(
conversionService
);
}
return
conversionService
;
}
}
@Configuration
@Configuration
...
...
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignFormatterRegistrar.java
0 → 100644
View file @
a71e13ef
/*
* Copyright 2013-2016 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
*
* http://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
.
cloud
.
netflix
.
feign
;
import
org.springframework.format.FormatterRegistrar
;
import
org.springframework.format.support.FormattingConversionService
;
/**
* Allows an application to customize the Feign {@link FormattingConversionService}.
*
* @author Matt Benson
*/
public
interface
FeignFormatterRegistrar
extends
FormatterRegistrar
{
}
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/support/SpringMvcContract.java
View file @
a71e13ef
/*
/*
* Copyright 2013-201
5
the original author or authors.
* Copyright 2013-201
6
the original author or authors.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -23,6 +23,7 @@ import java.util.Arrays;
...
@@ -23,6 +23,7 @@ import java.util.Arrays;
import
java.util.Collection
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
...
@@ -33,16 +34,19 @@ import org.springframework.cloud.netflix.feign.annotation.RequestParamParameterP
...
@@ -33,16 +34,19 @@ import org.springframework.cloud.netflix.feign.annotation.RequestParamParameterP
import
org.springframework.core.DefaultParameterNameDiscoverer
;
import
org.springframework.core.DefaultParameterNameDiscoverer
;
import
org.springframework.core.ParameterNameDiscoverer
;
import
org.springframework.core.ParameterNameDiscoverer
;
import
org.springframework.core.annotation.AnnotationUtils
;
import
org.springframework.core.annotation.AnnotationUtils
;
import
org.springframework.core.convert.ConversionService
;
import
org.springframework.core.convert.support.DefaultConversionService
;
import
org.springframework.util.Assert
;
import
org.springframework.util.Assert
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
static
feign
.
Util
.
checkState
;
import
static
feign
.
Util
.
emptyToNull
;
import
static
org
.
springframework
.
core
.
annotation
.
AnnotatedElementUtils
.
findMergedAnnotation
;
import
feign.Contract
;
import
feign.Contract
;
import
feign.Feign
;
import
feign.Feign
;
import
feign.MethodMetadata
;
import
feign.MethodMetadata
;
import
feign.Param
;
import
static
feign
.
Util
.
checkState
;
import
static
feign
.
Util
.
emptyToNull
;
import
static
org
.
springframework
.
core
.
annotation
.
AnnotatedElementUtils
.
findMergedAnnotation
;
/**
/**
* @author Spencer Gibb
* @author Spencer Gibb
...
@@ -58,14 +62,25 @@ public class SpringMvcContract extends Contract.BaseContract {
...
@@ -58,14 +62,25 @@ public class SpringMvcContract extends Contract.BaseContract {
private
final
Map
<
Class
<?
extends
Annotation
>,
AnnotatedParameterProcessor
>
annotatedArgumentProcessors
;
private
final
Map
<
Class
<?
extends
Annotation
>,
AnnotatedParameterProcessor
>
annotatedArgumentProcessors
;
private
final
Map
<
String
,
Method
>
processedMethods
=
new
HashMap
<>();
private
final
Map
<
String
,
Method
>
processedMethods
=
new
HashMap
<>();
private
final
ConversionService
conversionService
;
private
final
Param
.
Expander
expander
;
public
SpringMvcContract
()
{
public
SpringMvcContract
()
{
this
(
Collections
.<
AnnotatedParameterProcessor
>
emptyList
());
this
(
Collections
.<
AnnotatedParameterProcessor
>
emptyList
());
}
}
public
SpringMvcContract
(
public
SpringMvcContract
(
List
<
AnnotatedParameterProcessor
>
annotatedParameterProcessors
)
{
List
<
AnnotatedParameterProcessor
>
annotatedParameterProcessors
)
{
this
(
annotatedParameterProcessors
,
new
DefaultConversionService
());
}
public
SpringMvcContract
(
List
<
AnnotatedParameterProcessor
>
annotatedParameterProcessors
,
ConversionService
conversionService
)
{
Assert
.
notNull
(
annotatedParameterProcessors
,
Assert
.
notNull
(
annotatedParameterProcessors
,
"Parameter processors can not be null."
);
"Parameter processors can not be null."
);
Assert
.
notNull
(
conversionService
,
"ConversionService can not be null."
);
List
<
AnnotatedParameterProcessor
>
processors
;
List
<
AnnotatedParameterProcessor
>
processors
;
if
(!
annotatedParameterProcessors
.
isEmpty
())
{
if
(!
annotatedParameterProcessors
.
isEmpty
())
{
...
@@ -75,6 +90,8 @@ public class SpringMvcContract extends Contract.BaseContract {
...
@@ -75,6 +90,8 @@ public class SpringMvcContract extends Contract.BaseContract {
processors
=
getDefaultAnnotatedArgumentsProcessors
();
processors
=
getDefaultAnnotatedArgumentsProcessors
();
}
}
this
.
annotatedArgumentProcessors
=
toAnnotatedArgumentProcessorMap
(
processors
);
this
.
annotatedArgumentProcessors
=
toAnnotatedArgumentProcessorMap
(
processors
);
this
.
conversionService
=
conversionService
;
this
.
expander
=
new
ConvertingExpander
(
conversionService
);
}
}
@Override
@Override
...
@@ -148,6 +165,8 @@ public class SpringMvcContract extends Contract.BaseContract {
...
@@ -148,6 +165,8 @@ public class SpringMvcContract extends Contract.BaseContract {
// headers
// headers
parseHeaders
(
data
,
method
,
methodMapping
);
parseHeaders
(
data
,
method
,
methodMapping
);
data
.
indexToExpander
(
new
LinkedHashMap
<
Integer
,
Param
.
Expander
>());
}
}
private
void
checkAtMostOne
(
Method
method
,
Object
[]
values
,
String
fieldName
)
{
private
void
checkAtMostOne
(
Method
method
,
Object
[]
values
,
String
fieldName
)
{
...
@@ -184,6 +203,11 @@ public class SpringMvcContract extends Contract.BaseContract {
...
@@ -184,6 +203,11 @@ public class SpringMvcContract extends Contract.BaseContract {
processParameterAnnotation
);
processParameterAnnotation
);
}
}
}
}
if
(
isHttpAnnotation
&&
data
.
indexToExpander
().
get
(
paramIndex
)
==
null
&&
this
.
conversionService
.
canConvert
(
method
.
getParameterTypes
()[
paramIndex
],
String
.
class
))
{
data
.
indexToExpander
().
put
(
paramIndex
,
expander
);
}
return
isHttpAnnotation
;
return
isHttpAnnotation
;
}
}
...
@@ -293,4 +317,18 @@ public class SpringMvcContract extends Contract.BaseContract {
...
@@ -293,4 +317,18 @@ public class SpringMvcContract extends Contract.BaseContract {
}
}
}
}
public
static
class
ConvertingExpander
implements
Param
.
Expander
{
private
final
ConversionService
conversionService
;
public
ConvertingExpander
(
ConversionService
conversionService
)
{
this
.
conversionService
=
conversionService
;
}
@Override
public
String
expand
(
Object
value
)
{
return
conversionService
.
convert
(
value
,
String
.
class
);
}
}
}
}
spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/support/SpringMvcContractTests.java
View file @
a71e13ef
/*
* Copyright 2013-2016 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
*
* http://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
.
cloud
.
netflix
.
feign
.
support
;
package
org
.
springframework
.
cloud
.
netflix
.
feign
.
support
;
import
java.lang.reflect.InvocationTargetException
;
import
java.lang.reflect.InvocationTargetException
;
...
...
spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java
View file @
a71e13ef
/*
/*
* Copyright 2013-201
5
the original author or authors.
* Copyright 2013-201
6
the original author or authors.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -18,9 +18,11 @@ package org.springframework.cloud.netflix.feign.valid;
...
@@ -18,9 +18,11 @@ package org.springframework.cloud.netflix.feign.valid;
import
java.lang.reflect.InvocationHandler
;
import
java.lang.reflect.InvocationHandler
;
import
java.lang.reflect.Proxy
;
import
java.lang.reflect.Proxy
;
import
java.text.ParseException
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Locale
;
import
java.util.concurrent.Future
;
import
java.util.concurrent.Future
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.TimeUnit
;
...
@@ -34,6 +36,7 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
...
@@ -34,6 +36,7 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
import
org.springframework.boot.test.WebIntegrationTest
;
import
org.springframework.boot.test.WebIntegrationTest
;
import
org.springframework.cloud.netflix.feign.EnableFeignClients
;
import
org.springframework.cloud.netflix.feign.EnableFeignClients
;
import
org.springframework.cloud.netflix.feign.FeignClient
;
import
org.springframework.cloud.netflix.feign.FeignClient
;
import
org.springframework.cloud.netflix.feign.FeignFormatterRegistrar
;
import
org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient
;
import
org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient
;
import
org.springframework.cloud.netflix.feign.support.FallbackCommand
;
import
org.springframework.cloud.netflix.feign.support.FallbackCommand
;
import
org.springframework.cloud.netflix.ribbon.RibbonClient
;
import
org.springframework.cloud.netflix.ribbon.RibbonClient
;
...
@@ -41,6 +44,8 @@ import org.springframework.cloud.netflix.ribbon.RibbonClients;
...
@@ -41,6 +44,8 @@ import org.springframework.cloud.netflix.ribbon.RibbonClients;
import
org.springframework.cloud.netflix.ribbon.StaticServerList
;
import
org.springframework.cloud.netflix.ribbon.StaticServerList
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.format.Formatter
;
import
org.springframework.format.FormatterRegistry
;
import
org.springframework.http.HttpEntity
;
import
org.springframework.http.HttpEntity
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.http.ResponseEntity
;
...
@@ -110,6 +115,23 @@ public class FeignClientTests {
...
@@ -110,6 +115,23 @@ public class FeignClientTests {
@Autowired
@Autowired
HystrixClient
hystrixClient
;
HystrixClient
hystrixClient
;
protected
enum
Arg
{
A
,
B
;
@Override
public
String
toString
()
{
return
name
().
toLowerCase
(
Locale
.
ENGLISH
);
}
}
protected
static
class
OtherArg
{
public
final
String
value
;
public
OtherArg
(
String
value
)
{
this
.
value
=
value
;
}
}
@FeignClient
(
value
=
"localapp"
,
configuration
=
TestClientConfig
.
class
)
@FeignClient
(
value
=
"localapp"
,
configuration
=
TestClientConfig
.
class
)
protected
interface
TestClient
{
protected
interface
TestClient
{
@RequestMapping
(
method
=
RequestMethod
.
GET
,
value
=
"/hello"
)
@RequestMapping
(
method
=
RequestMethod
.
GET
,
value
=
"/hello"
)
...
@@ -147,6 +169,12 @@ public class FeignClientTests {
...
@@ -147,6 +169,12 @@ public class FeignClientTests {
produces
=
"application/vnd.io.spring.cloud.test.v1+json"
,
produces
=
"application/vnd.io.spring.cloud.test.v1+json"
,
value
=
"/complex"
)
value
=
"/complex"
)
String
moreComplexContentType
(
String
body
);
String
moreComplexContentType
(
String
body
);
@RequestMapping
(
method
=
RequestMethod
.
GET
,
value
=
"/tostring"
)
String
getToString
(
@RequestParam
(
"arg"
)
Arg
arg
);
@RequestMapping
(
method
=
RequestMethod
.
GET
,
value
=
"/tostring2"
)
String
getToString
(
@RequestParam
(
"arg"
)
OtherArg
arg
);
}
}
public
static
class
TestClientConfig
{
public
static
class
TestClientConfig
{
...
@@ -242,6 +270,28 @@ public class FeignClientTests {
...
@@ -242,6 +270,28 @@ public class FeignClientTests {
})
})
protected
static
class
Application
{
protected
static
class
Application
{
@Bean
FeignFormatterRegistrar
feignFormatterRegistrar
()
{
return
new
FeignFormatterRegistrar
()
{
@Override
public
void
registerFormatters
(
FormatterRegistry
registry
)
{
registry
.
addFormatter
(
new
Formatter
<
OtherArg
>()
{
@Override
public
String
print
(
OtherArg
object
,
Locale
locale
)
{
return
object
.
value
;
}
@Override
public
OtherArg
parse
(
String
text
,
Locale
locale
)
throws
ParseException
{
return
new
OtherArg
(
text
);
}
});
}
};
}
@RequestMapping
(
method
=
RequestMethod
.
GET
,
value
=
"/hello"
)
@RequestMapping
(
method
=
RequestMethod
.
GET
,
value
=
"/hello"
)
public
Hello
getHello
()
{
public
Hello
getHello
()
{
...
@@ -304,6 +354,16 @@ public class FeignClientTests {
...
@@ -304,6 +354,16 @@ public class FeignClientTests {
return
"{\"value\":\"OK\"}"
;
return
"{\"value\":\"OK\"}"
;
}
}
@RequestMapping
(
method
=
RequestMethod
.
GET
,
value
=
"/tostring"
)
String
getToString
(
@RequestParam
(
"arg"
)
Arg
arg
)
{
return
arg
.
toString
();
}
@RequestMapping
(
method
=
RequestMethod
.
GET
,
value
=
"/tostring2"
)
String
getToString
(
@RequestParam
(
"arg"
)
OtherArg
arg
)
{
return
arg
.
value
;
}
public
static
void
main
(
String
[]
args
)
{
public
static
void
main
(
String
[]
args
)
{
new
SpringApplicationBuilder
(
Application
.
class
).
properties
(
new
SpringApplicationBuilder
(
Application
.
class
).
properties
(
"spring.application.name=feignclienttest"
,
"spring.application.name=feignclienttest"
,
...
@@ -433,6 +493,14 @@ public class FeignClientTests {
...
@@ -433,6 +493,14 @@ public class FeignClientTests {
}
}
@Test
@Test
public
void
testConvertingExpander
()
{
assertEquals
(
Arg
.
A
.
toString
(),
testClient
.
getToString
(
Arg
.
A
));
assertEquals
(
Arg
.
B
.
toString
(),
testClient
.
getToString
(
Arg
.
B
));
assertEquals
(
"foo"
,
testClient
.
getToString
(
new
OtherArg
(
"foo"
)));
}
@Test
public
void
testHystrixFallbackWorks
()
{
public
void
testHystrixFallbackWorks
()
{
Hello
hello
=
hystrixClient
.
fail
();
Hello
hello
=
hystrixClient
.
fail
();
assertNotNull
(
"hello was null"
,
hello
);
assertNotNull
(
"hello was null"
,
hello
);
...
...
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