Compare commits
218 Commits
hekai-stud
...
main
Author | SHA1 | Date |
---|---|---|
Olga MaciaszekSharma | 8222ce4bbb | 1 year ago |
buildmaster | b634184f50 | 1 year ago |
buildmaster | 3d6b89ffd8 | 1 year ago |
Olga MaciaszekSharma | 89d30ad32f | 1 year ago |
Olga MaciaszekSharma | a738a87dda | 1 year ago |
Alexey Elin | 7ee6246991 | 1 year ago |
dependabot[bot] | 1926023ea1 | 1 year ago |
dependabot[bot] | a130f31761 | 1 year ago |
dependabot[bot] | 28a701bbd5 | 1 year ago |
dependabot[bot] | 7f6d13c2bc | 1 year ago |
dependabot[bot] | 0dbc8f287a | 1 year ago |
dependabot[bot] | a5b5d203e6 | 1 year ago |
dependabot[bot] | 0d85c6d548 | 1 year ago |
dependabot[bot] | f39ae8e67e | 1 year ago |
dependabot[bot] | 294d964f71 | 1 year ago |
dependabot[bot] | 283c73c21c | 1 year ago |
spencergibb | 1d0b70251e | 1 year ago |
spencergibb | 2a5e815b6b | 1 year ago |
Olga MaciaszekSharma | 3a353b13ec | 1 year ago |
buildmaster | 52ec6def53 | 1 year ago |
Olga MaciaszekSharma | 8bc5decb8e | 1 year ago |
Olga MaciaszekSharma | 0e9bbb55b1 | 1 year ago |
Olga MaciaszekSharma | cbb711c08d | 1 year ago |
Olga MaciaszekSharma | 835b9b5746 | 1 year ago |
Olga MaciaszekSharma | b80680e17d | 1 year ago |
Olga MaciaszekSharma | 9876860dcb | 1 year ago |
Olga MaciaszekSharma | 31201de020 | 1 year ago |
Olga MaciaszekSharma | a0dafafc3f | 1 year ago |
Olga MaciaszekSharma | a8b5494451 | 1 year ago |
Olga Maciaszek-Sharma | 17a2b8cada | 1 year ago |
Olga MaciaszekSharma | 352760c2f0 | 1 year ago |
buildmaster | 6e93dafa2a | 1 year ago |
Olga Maciaszek-Sharma | fc6fd70abd | 1 year ago |
Olga MaciaszekSharma | 6fc80939d6 | 1 year ago |
Olga MaciaszekSharma | 0c1bafc2fa | 1 year ago |
Olga MaciaszekSharma | 802defdd9f | 1 year ago |
Olga MaciaszekSharma | 86c74a4636 | 1 year ago |
Olga MaciaszekSharma | fd16ec8593 | 1 year ago |
Olga MaciaszekSharma | 22edbeca2b | 1 year ago |
spencergibb | 3fc733f7ad | 1 year ago |
spencergibb | 7798edef69 | 1 year ago |
buildmaster | 63a2465503 | 1 year ago |
buildmaster | bba14dff62 | 1 year ago |
buildmaster | d04022602d | 1 year ago |
buildmaster | 24c8990fe6 | 1 year ago |
buildmaster | 3bdf70948a | 1 year ago |
Olga MaciaszekSharma | 9761a18a9a | 1 year ago |
Olga MaciaszekSharma | d59bc46ebe | 1 year ago |
spencergibb | 413f361f87 | 1 year ago |
buildmaster | 45d28abb49 | 1 year ago |
buildmaster | a0ca64a90f | 1 year ago |
buildmaster | 92e5d92e52 | 1 year ago |
小魏,小魏,我们要去哪里呀 | 8020b8840e | 1 year ago |
spencergibb | cca510cbb2 | 1 year ago |
Olga MaciaszekSharma | 235eb6d207 | 1 year ago |
Olga MaciaszekSharma | 4b3c0d0039 | 1 year ago |
buildmaster | 11686a1966 | 1 year ago |
buildmaster | c5b9a04ab0 | 1 year ago |
buildmaster | 491e4bbe39 | 1 year ago |
buildmaster | 61b7e94e6f | 1 year ago |
buildmaster | 23e5a08650 | 1 year ago |
Olga MaciaszekSharma | 07ded06f73 | 2 years ago |
Olga MaciaszekSharma | fd0958e190 | 2 years ago |
buildmaster | 04da648959 | 2 years ago |
buildmaster | 35f28bdaed | 2 years ago |
buildmaster | 5c021abb64 | 2 years ago |
buildmaster | 5ed7831723 | 2 years ago |
Olga MaciaszekSharma | d411f57b7c | 2 years ago |
Olga MaciaszekSharma | 033e6a85d0 | 2 years ago |
Olga MaciaszekSharma | 5bc8856903 | 2 years ago |
buildmaster | 0828fff93f | 2 years ago |
buildmaster | 974631b9c2 | 2 years ago |
buildmaster | 6ff470d528 | 2 years ago |
Olga Maciaszek-Sharma | 06192a575c | 2 years ago |
Olga Maciaszek-Sharma | 85c41513f9 | 2 years ago |
Olga Maciaszek-Sharma | 6e48199c42 | 2 years ago |
Olga Maciaszek-Sharma | b7de95e0df | 2 years ago |
Olga Maciaszek-Sharma | e70b66c187 | 2 years ago |
Olga Maciaszek-Sharma | e09bb5f23f | 2 years ago |
Olga Maciaszek-Sharma | 21c2b829ee | 2 years ago |
Olga Maciaszek-Sharma | 5e7eab89a5 | 2 years ago |
Olga Maciaszek-Sharma | 8e9b11ffdf | 2 years ago |
Olga Maciaszek-Sharma | f61ca17ad5 | 2 years ago |
Olga Maciaszek-Sharma | 471c5c5cb1 | 2 years ago |
apikozh | 23ca17df2c | 2 years ago |
MangKyu | 874ff0d383 | 2 years ago |
buildmaster | 647b0b3d1a | 2 years ago |
buildmaster | 6be252163f | 2 years ago |
buildmaster | 01ef53091e | 2 years ago |
Olga Maciaszek-Sharma | 5568778f6d | 2 years ago |
Olga Maciaszek-Sharma | bc3f12a247 | 2 years ago |
Olga Maciaszek-Sharma | 701c4c0c61 | 2 years ago |
Olga Maciaszek-Sharma | 68c4513ed9 | 2 years ago |
Olga Maciaszek-Sharma | 54a6a56b36 | 2 years ago |
Olga Maciaszek-Sharma | da159123d4 | 2 years ago |
min1854 | b0d9438208 | 2 years ago |
buildmaster | 41565b73a3 | 2 years ago |
buildmaster | 280283b1c5 | 2 years ago |
buildmaster | 2c946e5924 | 2 years ago |
Ryan Baxter | c10b06ec07 | 2 years ago |
buildmaster | ec245b03f7 | 2 years ago |
Olga Maciaszek-Sharma | 7ad582dbe9 | 2 years ago |
小魏,小魏,我们要去哪里呀 | 10b55a0a13 | 2 years ago |
buildmaster | 80c80556b4 | 2 years ago |
buildmaster | 9a7957e419 | 2 years ago |
buildmaster | b6c2c821fc | 2 years ago |
buildmaster | 96383d53f9 | 2 years ago |
buildmaster | bb09a2e3aa | 2 years ago |
Olga Maciaszek-Sharma | 8365c90ce5 | 2 years ago |
Olga Maciaszek-Sharma | bcb9b74889 | 2 years ago |
Dominique Villard | fead135d92 | 2 years ago |
小魏,小魏,我们要去哪里呀 | ba07f19c50 | 2 years ago |
Jared Rufer | 8a1f888aac | 2 years ago |
buildmaster | 935405407a | 2 years ago |
buildmaster | 9cf0d2c584 | 2 years ago |
Olga Maciaszek-Sharma | 7f66cf3829 | 2 years ago |
spencergibb | 62f9d76127 | 2 years ago |
Olga Maciaszek-Sharma | 5fb3790dab | 2 years ago |
Olga Maciaszek-Sharma | cc85cdce8f | 2 years ago |
Jonatan Ivanov | 372a631c38 | 2 years ago |
Jonatan Ivanov | 47d8094250 | 2 years ago |
buildmaster | da70160113 | 2 years ago |
buildmaster | db7eab8f0e | 2 years ago |
Ryan Baxter | 83c0eff4be | 2 years ago |
Ryan Baxter | 1ec3c6522d | 2 years ago |
Olga Maciaszek-Sharma | d30540d144 | 2 years ago |
Olga Maciaszek-Sharma | 6f1fab1ab3 | 2 years ago |
Olga Maciaszek-Sharma | a410fd1d10 | 2 years ago |
buildmaster | a86b8cda5e | 2 years ago |
buildmaster | af8db99454 | 2 years ago |
buildmaster | cce0de9e89 | 2 years ago |
buildmaster | 3de3f8f46a | 2 years ago |
buildmaster | 09f342ae6b | 2 years ago |
Jasbir | e33062fac4 | 2 years ago |
Olga Maciaszek-Sharma | 3b02c044cb | 2 years ago |
Olga Maciaszek-Sharma | 9328a21d64 | 2 years ago |
Olga Maciaszek-Sharma | 9b97262f1f | 2 years ago |
pandaapo | ed40511b19 | 2 years ago |
Olga Maciaszek-Sharma | 0875ba2e93 | 2 years ago |
Olga Maciaszek-Sharma | 172fcbbedf | 2 years ago |
vicasong | c862311811 | 2 years ago |
Olga Maciaszek-Sharma | 8fa27ddf56 | 2 years ago |
정원식 | 561acf6276 | 2 years ago |
小魏,小魏,我们要去哪里呀 | e2c2e6c7b0 | 2 years ago |
Olga Maciaszek-Sharma | d7fd71dfb5 | 2 years ago |
buildmaster | 6ca3ef8fa6 | 2 years ago |
Nikita Konev | e1a9b775ad | 2 years ago |
Olga Maciaszek-Sharma | 28345b2ad9 | 2 years ago |
Olga Maciaszek-Sharma | 075887eec3 | 2 years ago |
Olga Maciaszek-Sharma | 5b0f9f74af | 2 years ago |
dependabot[bot] | b3578fc454 | 2 years ago |
dependabot[bot] | 536c53a705 | 2 years ago |
buildmaster | d76455cf3d | 2 years ago |
buildmaster | e05eba9834 | 2 years ago |
buildmaster | a44d6e1a3b | 2 years ago |
Olga Maciaszek-Sharma | 1e31a0d245 | 2 years ago |
Olga Maciaszek-Sharma | 77072d96e4 | 2 years ago |
dzcr | afbe0b232b | 2 years ago |
Olga Maciaszek-Sharma | 60fc4a53e7 | 2 years ago |
dzcr | d501cbbd07 | 2 years ago |
Olga Maciaszek-Sharma | 6098d910ac | 2 years ago |
Olga Maciaszek-Sharma | 09939b6174 | 2 years ago |
dzcr | c35fcffa48 | 2 years ago |
Olga Maciaszek-Sharma | 8d3d5c574f | 2 years ago |
dzcr | 66cc168a33 | 2 years ago |
spencergibb | 4dc2852ee0 | 2 years ago |
spencergibb | 6f583d5bad | 2 years ago |
buildmaster | 245b106a76 | 2 years ago |
buildmaster | 11e9c5dab0 | 2 years ago |
Olga Maciaszek-Sharma | 666a59df6b | 2 years ago |
Olga Maciaszek-Sharma | 5a21e22002 | 2 years ago |
小魏,小魏,我们要去哪里呀 | 5bddccf720 | 2 years ago |
Olga Maciaszek-Sharma | 5f40f245af | 2 years ago |
buildmaster | e4bdc13c51 | 2 years ago |
buildmaster | f344125527 | 2 years ago |
Olga Maciaszek-Sharma | 26e157fd46 | 2 years ago |
spencergibb | 540ec0410d | 2 years ago |
Olga Maciaszek-Sharma | 1c8e8056e4 | 2 years ago |
buildmaster | 3f55dbecba | 2 years ago |
buildmaster | 1a72f2502e | 2 years ago |
Olga Maciaszek-Sharma | 8493de9aa6 | 3 years ago |
buildmaster | 001b7388a7 | 3 years ago |
buildmaster | 0c5c3befbc | 3 years ago |
Olga Maciaszek-Sharma | 952a7b0ff0 | 3 years ago |
Olga Maciaszek-Sharma | 2b1ac9caa8 | 3 years ago |
Olga Maciaszek-Sharma | 2a008d12c8 | 3 years ago |
Ryan Baxter | 295b5e4861 | 3 years ago |
Ryan Baxter | efbb0b8224 | 3 years ago |
Ryan Baxter | 4e1859eea6 | 3 years ago |
spencergibb | 0972d4211f | 3 years ago |
buildmaster | 2f8f114667 | 3 years ago |
Olga Maciaszek-Sharma | 0d0ff50842 | 3 years ago |
Olga Maciaszek-Sharma | 088221b9fb | 3 years ago |
Olga Maciaszek-Sharma | 8998eb3423 | 3 years ago |
Can Bezmen | ddc1b94555 | 3 years ago |
Olga Maciaszek-Sharma | cef268947f | 3 years ago |
buildmaster | 8d802a3fee | 3 years ago |
Olga Maciaszek-Sharma | e674f488d2 | 3 years ago |
Olga Maciaszek-Sharma | 2eea81f93b | 3 years ago |
buildmaster | ee8a42e401 | 3 years ago |
buildmaster | 53428758ad | 3 years ago |
buildmaster | 59ac5c1b3c | 3 years ago |
spencergibb | 4f7892f34d | 3 years ago |
buildmaster | 261fc58a5e | 3 years ago |
Olga MaciaszekSharma | 67e8c9072d | 3 years ago |
Olga MaciaszekSharma | 7b08279aae | 3 years ago |
buildmaster | f60859120d | 3 years ago |
Olga MaciaszekSharma | c665d2c029 | 3 years ago |
Olga MaciaszekSharma | 7f425704e4 | 3 years ago |
Olga MaciaszekSharma | 6c130af613 | 3 years ago |
Olga MaciaszekSharma | 98b2511d1f | 3 years ago |
Olga MaciaszekSharma | 10ad8a0c6e | 3 years ago |
Olga MaciaszekSharma | 505894315d | 3 years ago |
Olga MaciaszekSharma | e2048b3719 | 3 years ago |
buildmaster | e389726ba2 | 3 years ago |
Spencer Gibb | 2cf8adf482 | 3 years ago |
spencergibb | 6a1d9fe074 | 3 years ago |
spencergibb | b6b16a034a | 3 years ago |
196 changed files with 5726 additions and 3178 deletions
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
version: 2 |
||||
updates: |
||||
- package-ecosystem: "github-actions" |
||||
directory: "/" |
||||
target-branch: "3.1.x" # oldest OSS supported branch |
||||
schedule: |
||||
interval: "weekly" |
||||
- package-ecosystem: "github-actions" |
||||
directory: "/" |
||||
target-branch: "4.0.x" # oldest OSS supported branch |
||||
schedule: |
||||
interval: "weekly" |
||||
- package-ecosystem: "github-actions" |
||||
directory: "/" |
||||
target-branch: "main" |
||||
schedule: |
||||
interval: "weekly" |
||||
- package-ecosystem: maven |
||||
directory: / |
||||
schedule: |
||||
interval: daily |
||||
target-branch: 3.1.x |
||||
ignore: |
||||
# only upgrade patch versions for maintenance branch |
||||
- dependency-name: "*" |
||||
update-types: |
||||
- version-update:semver-major |
||||
- version-update:semver-minor |
||||
- package-ecosystem: maven |
||||
directory: / |
||||
schedule: |
||||
interval: daily |
||||
target-branch: 4.0.x |
||||
ignore: |
||||
# only upgrade patch versions for maintenance branch |
||||
- dependency-name: "*" |
||||
update-types: |
||||
- version-update:semver-major |
||||
- version-update:semver-minor |
||||
- package-ecosystem: maven |
||||
directory: / |
||||
schedule: |
||||
interval: daily |
||||
target-branch: main |
||||
ignore: |
||||
# only upgrade by minor or patch |
||||
- dependency-name: "*" |
||||
update-types: |
||||
- version-update:semver-major |
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
name: Deploy Docs |
||||
on: |
||||
push: |
||||
branches-ignore: [ gh-pages ] |
||||
tags: '**' |
||||
repository_dispatch: |
||||
types: request-build-reference # legacy |
||||
#schedule: |
||||
#- cron: '0 10 * * *' # Once per day at 10am UTC |
||||
workflow_dispatch: |
||||
permissions: |
||||
actions: write |
||||
jobs: |
||||
build: |
||||
runs-on: ubuntu-latest |
||||
# if: github.repository_owner == 'spring-cloud' |
||||
steps: |
||||
- name: Checkout |
||||
uses: actions/checkout@v4 |
||||
with: |
||||
ref: docs-build |
||||
fetch-depth: 1 |
||||
- name: Dispatch (partial build) |
||||
if: github.ref_type == 'branch' |
||||
env: |
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
||||
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }} |
||||
- name: Dispatch (full build) |
||||
if: github.ref_type == 'tag' |
||||
env: |
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
||||
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) |
@ -1 +1,2 @@
@@ -1 +1,2 @@
|
||||
-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring |
||||
-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local |
||||
-P spring |
||||
|
Binary file not shown.
@ -1 +1,18 @@
@@ -1 +1,18 @@
|
||||
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip |
||||
# Licensed to the Apache Software Foundation (ASF) under one |
||||
# or more contributor license agreements. See the NOTICE file |
||||
# distributed with this work for additional information |
||||
# regarding copyright ownership. The ASF licenses this file |
||||
# to you 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. |
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.4/apache-maven-3.9.4-bin.zip |
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar |
||||
|
@ -1,3 +1,3 @@
@@ -1,3 +1,3 @@
|
||||
# Enable auto-env through the sdkman_auto_env config |
||||
# Add key=value pairs of SDKs to use below |
||||
java=8.0.292.hs-adpt |
||||
java=17.0.1-tem |
||||
|
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
antora: |
||||
extensions: |
||||
- '@springio/antora-extensions/partial-build-extension' |
||||
- require: '@springio/antora-extensions/latest-version-extension' |
||||
- require: '@springio/antora-extensions/inject-collector-cache-config-extension' |
||||
- '@antora/collector-extension' |
||||
- '@antora/atlas-extension' |
||||
- require: '@springio/antora-extensions/root-component-extension' |
||||
root_component_name: 'cloud-openfeign' |
||||
- '@springio/antora-extensions/static-page-extension' |
||||
site: |
||||
title: Spring Cloud Openfeign |
||||
url: https://docs.spring.io/spring-cloud-openfeign/reference/ |
||||
content: |
||||
sources: |
||||
- url: ./.. |
||||
branches: HEAD |
||||
start_path: docs |
||||
worktrees: true |
||||
asciidoc: |
||||
attributes: |
||||
page-stackoverflow-url: https://stackoverflow.com/tags/spring-cloud |
||||
page-pagination: '' |
||||
hide-uri-scheme: '@' |
||||
tabs-sync-option: '@' |
||||
chomp: 'all' |
||||
extensions: |
||||
- '@asciidoctor/tabs' |
||||
- '@springio/asciidoctor-extensions' |
||||
sourcemap: true |
||||
urls: |
||||
latest_version_segment: '' |
||||
runtime: |
||||
log: |
||||
failure_level: warn |
||||
format: pretty |
||||
ui: |
||||
bundle: |
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.2/ui-bundle.zip |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
name: cloud-openfeign |
||||
version: true |
||||
title: Spring Cloud OpenFeign |
||||
nav: |
||||
- modules/ROOT/nav.adoc |
||||
ext: |
||||
collector: |
||||
run: |
||||
command: ./mvnw --no-transfer-progress -B process-resources -Pdocs -pl docs -Dantora-maven-plugin.phase=none -Dgenerate-docs.phase=none -Dgenerate-readme.phase=none -Dgenerate-cloud-resources.phase=none -Dmaven-dependency-plugin-for-docs.phase=none -Dmaven-dependency-plugin-for-docs-classes.phase=none -DskipTests |
||||
local: true |
||||
scan: |
||||
dir: ./target/classes/antora-resources/ |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
* xref:index.adoc[Introduction] |
||||
* xref:spring-cloud-openfeign.adoc[] |
||||
* xref:appendix.adoc[] |
||||
** xref:configprops.adoc[] |
@ -1,8 +1,6 @@
@@ -1,8 +1,6 @@
|
||||
:doctype: book |
||||
:idprefix: |
||||
:idseparator: - |
||||
:toc: left |
||||
:toclevels: 4 |
||||
:tabsize: 4 |
||||
:numbered: |
||||
:sectanchors: |
@ -1,14 +1,13 @@
@@ -1,14 +1,13 @@
|
||||
:numbered!: |
||||
[appendix] |
||||
[[common-application-properties]] |
||||
== Common application properties |
||||
= Common application properties |
||||
:page-section-summary-toc: 1 |
||||
|
||||
include::_attributes.adoc[] |
||||
|
||||
Various properties can be specified inside your `application.properties` file, inside your `application.yml` file, or as command line switches. |
||||
This appendix provides a list of common {project-full-name} properties and references to the underlying classes that consume them. |
||||
This appendix provides a list of common Spring Cloud OpenFeign properties and references to the underlying classes that consume them. |
||||
|
||||
NOTE: Property contributions can come from additional jar files on your classpath, so you should not consider this an exhaustive list. |
||||
Also, you can define your own properties. |
||||
|
||||
include::_configprops.adoc[] |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
[[configuration-properties]] |
||||
= Configuration Properties |
||||
|
||||
Below you can find a list of configuration properties. |
||||
|
||||
include::partial$_configprops.adoc[] |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
include::intro.adoc[] |
@ -1,3 +1,6 @@
@@ -1,3 +1,6 @@
|
||||
[[introduction]] |
||||
= Spring Cloud OpenFeign |
||||
|
||||
This project provides OpenFeign integrations for Spring Boot apps through autoconfiguration |
||||
and binding to the Spring Environment and other Spring programming model idioms. |
||||
|
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
:sc-ext: java |
||||
:project-full-name: Spring Cloud OpenFeign |
||||
:all: {asterisk}{asterisk} |
@ -0,0 +1,113 @@
@@ -0,0 +1,113 @@
|
||||
|=== |
||||
|Name | Default | Description |
||||
|
||||
|spring.cloud.compatibility-verifier.compatible-boot-versions | `+++3.2.x+++` | Default accepted versions for the Spring Boot dependency. You can set {@code x} for the patch version if you don't want to specify a concrete value. Example: {@code 3.4.x} |
||||
|spring.cloud.compatibility-verifier.enabled | `+++false+++` | Enables creation of Spring Cloud compatibility verification. |
||||
|spring.cloud.config.allow-override | `+++true+++` | Flag to indicate that {@link #isOverrideSystemProperties() systemPropertiesOverride} can be used. Set to false to prevent users from changing the default accidentally. Default true. |
||||
|spring.cloud.config.initialize-on-context-refresh | `+++false+++` | Flag to initialize bootstrap configuration on context refresh event. Default false. |
||||
|spring.cloud.config.override-none | `+++false+++` | Flag to indicate that when {@link #setAllowOverride(boolean) allowOverride} is true, external properties should take lowest priority and should not override any existing property sources (including local config files). Default false. This will only have an effect when using config first bootstrap. |
||||
|spring.cloud.config.override-system-properties | `+++true+++` | Flag to indicate that the external properties should override system properties. Default true. |
||||
|spring.cloud.decrypt-environment-post-processor.enabled | `+++true+++` | Enable the DecryptEnvironmentPostProcessor. |
||||
|spring.cloud.discovery.client.composite-indicator.enabled | `+++true+++` | Enables discovery client composite health indicator. |
||||
|spring.cloud.discovery.client.health-indicator.enabled | `+++true+++` | |
||||
|spring.cloud.discovery.client.health-indicator.include-description | `+++false+++` | |
||||
|spring.cloud.discovery.client.health-indicator.use-services-query | `+++true+++` | Whether or not the indicator should use {@link DiscoveryClient#getServices} to check its health. When set to {@code false} the indicator instead uses the lighter {@link DiscoveryClient#probe()}. This can be helpful in large deployments where the number of services returned makes the operation unnecessarily heavy. |
||||
|spring.cloud.discovery.client.simple.instances | | |
||||
|spring.cloud.discovery.client.simple.local.host | | |
||||
|spring.cloud.discovery.client.simple.local.instance-id | | |
||||
|spring.cloud.discovery.client.simple.local.metadata | | |
||||
|spring.cloud.discovery.client.simple.local.port | `+++0+++` | |
||||
|spring.cloud.discovery.client.simple.local.secure | `+++false+++` | |
||||
|spring.cloud.discovery.client.simple.local.service-id | | |
||||
|spring.cloud.discovery.client.simple.local.uri | | |
||||
|spring.cloud.discovery.client.simple.order | | |
||||
|spring.cloud.discovery.enabled | `+++true+++` | Enables discovery client health indicators. |
||||
|spring.cloud.features.enabled | `+++true+++` | Enables the features endpoint. |
||||
|spring.cloud.httpclientfactories.apache.enabled | `+++true+++` | Enables creation of Apache Http Client factory beans. |
||||
|spring.cloud.httpclientfactories.ok.enabled | `+++true+++` | Enables creation of OK Http Client factory beans. |
||||
|spring.cloud.hypermedia.refresh.fixed-delay | `+++5000+++` | |
||||
|spring.cloud.hypermedia.refresh.initial-delay | `+++10000+++` | |
||||
|spring.cloud.inetutils.default-hostname | `+++localhost+++` | The default hostname. Used in case of errors. |
||||
|spring.cloud.inetutils.default-ip-address | `+++127.0.0.1+++` | The default IP address. Used in case of errors. |
||||
|spring.cloud.inetutils.ignored-interfaces | | List of Java regular expressions for network interfaces that will be ignored. |
||||
|spring.cloud.inetutils.preferred-networks | | List of Java regular expressions for network addresses that will be preferred. |
||||
|spring.cloud.inetutils.timeout-seconds | `+++1+++` | Timeout, in seconds, for calculating hostname. |
||||
|spring.cloud.inetutils.use-only-site-local-interfaces | `+++false+++` | Whether to use only interfaces with site local addresses. See {@link InetAddress#isSiteLocalAddress()} for more details. |
||||
|spring.cloud.loadbalancer.call-get-with-request-on-delegates | `+++true+++` | If this flag is set to {@code true}, {@code ServiceInstanceListSupplier#get(Request request)} method will be implemented to call {@code delegate.get(request)} in classes assignable from {@code DelegatingServiceInstanceListSupplier} that don't already implement that method, with the exclusion of {@code CachingServiceInstanceListSupplier} and {@code HealthCheckServiceInstanceListSupplier}, which should be placed in the instance supplier hierarchy directly after the supplier performing instance retrieval over the network, before any request-based filtering is done, {@code true} by default. |
||||
|spring.cloud.loadbalancer.clients | | |
||||
|spring.cloud.loadbalancer.eager-load.clients | | |
||||
|spring.cloud.loadbalancer.health-check.initial-delay | `+++0+++` | Initial delay value for the HealthCheck scheduler. |
||||
|spring.cloud.loadbalancer.health-check.interval | `+++25s+++` | Interval for rerunning the HealthCheck scheduler. |
||||
|spring.cloud.loadbalancer.health-check.interval | `+++25s+++` | Interval for rerunning the HealthCheck scheduler. |
||||
|spring.cloud.loadbalancer.health-check.path | | Path at which the health-check request should be made. Can be set up per `serviceId`. A `default` value can be set up as well. If none is set up, `/actuator/health` will be used. |
||||
|spring.cloud.loadbalancer.health-check.port | | Path at which the health-check request should be made. If none is set, the port under which the requested service is available at the service instance. |
||||
|spring.cloud.loadbalancer.health-check.refetch-instances | `+++false+++` | Indicates whether the instances should be refetched by the `HealthCheckServiceInstanceListSupplier`. This can be used if the instances can be updated and the underlying delegate does not provide an ongoing flux. |
||||
|spring.cloud.loadbalancer.health-check.refetch-instances-interval | `+++25s+++` | Interval for refetching available service instances. |
||||
|spring.cloud.loadbalancer.health-check.repeat-health-check | `+++true+++` | Indicates whether health checks should keep repeating. It might be useful to set it to `false` if periodically refetching the instances, as every refetch will also trigger a healthcheck. |
||||
|spring.cloud.loadbalancer.health-check.update-results-list | `+++true+++` | Indicates whether the {@code healthCheckFlux} should emit on each alive {@link ServiceInstance} that has been retrieved. If set to {@code false}, the entire alive instances sequence is first collected into a list and only then emitted. |
||||
|spring.cloud.loadbalancer.hint | | Allows setting the value of <code>hint</code> that is passed on to the LoadBalancer request and can subsequently be used in {@link ReactiveLoadBalancer} implementations. |
||||
|spring.cloud.loadbalancer.hint-header-name | `+++X-SC-LB-Hint+++` | Allows setting the name of the header used for passing the hint for hint-based service instance filtering. |
||||
|spring.cloud.loadbalancer.retry.backoff.enabled | `+++false+++` | Indicates whether Reactor Retry backoffs should be applied. |
||||
|spring.cloud.loadbalancer.retry.backoff.jitter | `+++0.5+++` | Used to set `RetryBackoffSpec.jitter`. |
||||
|spring.cloud.loadbalancer.retry.backoff.max-backoff | `+++Long.MAX ms+++` | Used to set `RetryBackoffSpec.maxBackoff`. |
||||
|spring.cloud.loadbalancer.retry.backoff.min-backoff | `+++5 ms+++` | Used to set `RetryBackoffSpec#minBackoff`. |
||||
|spring.cloud.loadbalancer.retry.enabled | `+++true+++` | Enables LoadBalancer retries. |
||||
|spring.cloud.loadbalancer.retry.max-retries-on-next-service-instance | `+++1+++` | Number of retries to be executed on the next `ServiceInstance`. A `ServiceInstance` is chosen before each retry call. |
||||
|spring.cloud.loadbalancer.retry.max-retries-on-same-service-instance | `+++0+++` | Number of retries to be executed on the same `ServiceInstance`. |
||||
|spring.cloud.loadbalancer.retry.retry-on-all-exceptions | `+++false+++` | Indicates retries should be attempted for all exceptions, not only those specified in `retryableExceptions`. |
||||
|spring.cloud.loadbalancer.retry.retry-on-all-operations | `+++false+++` | Indicates retries should be attempted on operations other than `HttpMethod.GET`. |
||||
|spring.cloud.loadbalancer.retry.retryable-exceptions | `+++{}+++` | A `Set` of `Throwable` classes that should trigger a retry. |
||||
|spring.cloud.loadbalancer.retry.retryable-status-codes | `+++{}+++` | A `Set` of status codes that should trigger a retry. |
||||
|spring.cloud.loadbalancer.sticky-session.add-service-instance-cookie | `+++false+++` | Indicates whether a cookie with the newly selected instance should be added by LoadBalancer. |
||||
|spring.cloud.loadbalancer.sticky-session.instance-id-cookie-name | `+++sc-lb-instance-id+++` | The name of the cookie holding the preferred instance id. |
||||
|spring.cloud.loadbalancer.x-forwarded.enabled | `+++false+++` | To Enable X-Forwarded Headers. |
||||
|spring.cloud.openfeign.autoconfiguration.jackson.enabled | `+++true+++` | If true, PageJacksonModule and SortJacksonModule bean will be provided for Jackson page decoding. |
||||
|spring.cloud.openfeign.circuitbreaker.alphanumeric-ids.enabled | `+++false+++` | If true, Circuit Breaker ids will only contain alphanumeric characters to allow for configuration via configuration properties. |
||||
|spring.cloud.openfeign.circuitbreaker.enabled | `+++false+++` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker. |
||||
|spring.cloud.openfeign.circuitbreaker.group.enabled | `+++false+++` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with with group. |
||||
|spring.cloud.openfeign.client.config | | |
||||
|spring.cloud.openfeign.client.decode-slash | `+++true+++` | Feign clients do not encode slash `/` characters by default. To change this behavior, set the `decodeSlash` to `false`. |
||||
|spring.cloud.openfeign.client.default-config | `+++default+++` | |
||||
|spring.cloud.openfeign.client.default-to-properties | `+++true+++` | |
||||
|spring.cloud.openfeign.client.refresh-enabled | `+++false+++` | Enables options value refresh capability for Feign. |
||||
|spring.cloud.openfeign.compression.request.enabled | `+++false+++` | Enables the request sent by Feign to be compressed. |
||||
|spring.cloud.openfeign.compression.request.mime-types | `+++[text/xml, application/xml, application/json]+++` | The list of supported mime types. |
||||
|spring.cloud.openfeign.compression.request.min-request-size | `+++2048+++` | The minimum threshold content size. |
||||
|spring.cloud.openfeign.compression.response.enabled | `+++false+++` | Enables the response from Feign to be compressed. |
||||
|spring.cloud.openfeign.encoder.charset-from-content-type | `+++false+++` | Indicates whether the charset should be derived from the {@code Content-Type} header. |
||||
|spring.cloud.openfeign.http2client.enabled | `+++false+++` | Enables the use of the Java11 HTTP 2 Client by Feign. |
||||
|spring.cloud.openfeign.httpclient.connection-timeout | `+++2000+++` | |
||||
|spring.cloud.openfeign.httpclient.connection-timer-repeat | `+++3000+++` | |
||||
|spring.cloud.openfeign.httpclient.disable-ssl-validation | `+++false+++` | |
||||
|spring.cloud.openfeign.httpclient.enabled | `+++true+++` | Enables the use of the Apache HTTP Client by Feign. |
||||
|spring.cloud.openfeign.httpclient.follow-redirects | `+++true+++` | |
||||
|spring.cloud.openfeign.httpclient.hc5.connection-request-timeout | `+++3+++` | Default value for connection request timeout. |
||||
|spring.cloud.openfeign.httpclient.hc5.connection-request-timeout-unit | | Default value for connection request timeout unit. |
||||
|spring.cloud.openfeign.httpclient.hc5.enabled | `+++false+++` | Enables the use of the Apache HTTP Client 5 by Feign. |
||||
|spring.cloud.openfeign.httpclient.hc5.pool-concurrency-policy | | Pool concurrency policies. |
||||
|spring.cloud.openfeign.httpclient.hc5.pool-reuse-policy | | Pool connection re-use policies. |
||||
|spring.cloud.openfeign.httpclient.hc5.socket-timeout | `+++5+++` | Default value for socket timeout. |
||||
|spring.cloud.openfeign.httpclient.hc5.socket-timeout-unit | | Default value for socket timeout unit. |
||||
|spring.cloud.openfeign.httpclient.http2.version | `+++HTTP_2+++` | Configure the protocols used by this client to communicate with remote servers. Uses {@link String} value of {@link HttpClient.Version}. |
||||
|spring.cloud.openfeign.httpclient.max-connections | `+++200+++` | |
||||
|spring.cloud.openfeign.httpclient.max-connections-per-route | `+++50+++` | |
||||
|spring.cloud.openfeign.httpclient.ok-http.protocols | | Configure the protocols used by this client to communicate with remote servers. Uses {@link String} values of {@link Protocol}. |
||||
|spring.cloud.openfeign.httpclient.ok-http.read-timeout | `+++60s+++` | {@link OkHttpClient} read timeout; defaults to 60 seconds. |
||||
|spring.cloud.openfeign.httpclient.time-to-live | `+++900+++` | |
||||
|spring.cloud.openfeign.httpclient.time-to-live-unit | | |
||||
|spring.cloud.openfeign.lazy-attributes-resolution | `+++false+++` | Switches @FeignClient attributes resolution mode to lazy. |
||||
|spring.cloud.openfeign.micrometer.enabled | `+++true+++` | Enables Micrometer capabilities for Feign. |
||||
|spring.cloud.openfeign.oauth2.clientRegistrationId | | Provides a clientId to be used with OAuth2. |
||||
|spring.cloud.openfeign.oauth2.enabled | `+++false+++` | Enables feign interceptor for managing oauth2 access token. |
||||
|spring.cloud.openfeign.okhttp.enabled | `+++false+++` | Enables the use of the OK HTTP Client by Feign. |
||||
|spring.cloud.refresh.additional-property-sources-to-retain | | Additional property sources to retain during a refresh. Typically only system property sources are retained. This property allows property sources, such as property sources created by EnvironmentPostProcessors to be retained as well. |
||||
|spring.cloud.refresh.enabled | `+++true+++` | Enables autoconfiguration for the refresh scope and associated features. |
||||
|spring.cloud.refresh.extra-refreshable | `+++true+++` | Additional class names for beans to post process into refresh scope. |
||||
|spring.cloud.refresh.never-refreshable | `+++true+++` | Comma separated list of class names for beans to never be refreshed or rebound. |
||||
|spring.cloud.refresh.on-restart.enabled | `+++true+++` | Enable refreshing context on start. |
||||
|spring.cloud.service-registry.auto-registration.enabled | `+++true+++` | Whether service auto-registration is enabled. Defaults to true. |
||||
|spring.cloud.service-registry.auto-registration.fail-fast | `+++false+++` | Whether startup fails if there is no AutoServiceRegistration. Defaults to false. |
||||
|spring.cloud.service-registry.auto-registration.register-management | `+++true+++` | Whether to register the management as a service. Defaults to true. |
||||
|spring.cloud.util.enabled | `+++true+++` | Enables creation of Spring Cloud utility beans. |
||||
|
||||
|=== |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
[[observability-conventions]] |
||||
=== Observability - Conventions |
||||
|
||||
Below you can find a list of all `GlobalObservationConvention` and `ObservationConvention` declared by this project. |
||||
|
||||
|
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
[[observability-metrics]] |
||||
=== Observability - Metrics |
||||
|
||||
Below you can find a list of all metrics declared by this project. |
||||
|
||||
|
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
[[observability-spans]] |
||||
=== Observability - Spans |
||||
|
||||
Below you can find a list of all spans declared by this project. |
||||
|
||||
|
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
version: @antora-component.version@ |
||||
prerelease: @antora-component.prerelease@ |
||||
|
||||
asciidoc: |
||||
attributes: |
||||
attribute-missing: 'warn' |
||||
chomp: 'all' |
||||
project-root: @maven.multiModuleProjectDirectory@ |
||||
github-repo: @docs.main@ |
||||
github-raw: https://raw.githubusercontent.com/spring-cloud/@docs.main@/@github-tag@ |
||||
github-code: https://github.com/spring-cloud/@docs.main@/tree/@github-tag@ |
||||
github-issues: https://github.com/spring-cloud/@docs.main@/issues/ |
||||
github-wiki: https://github.com/spring-cloud/@docs.main@/wiki |
||||
spring-cloud-version: @project.version@ |
||||
github-tag: @github-tag@ |
||||
version-type: @version-type@ |
||||
docs-url: https://docs.spring.io/@docs.main@/docs/@project.version@ |
||||
raw-docs-url: https://raw.githubusercontent.com/spring-cloud/@docs.main@/@github-tag@ |
||||
project-version: @project.version@ |
||||
project-name: @docs.main@ |
@ -1,25 +1,24 @@
@@ -1,25 +1,24 @@
|
||||
image::https://github.com/spring-cloud/spring-cloud-openfeign/workflows/Build/badge.svg?branch=main&style=svg["Build",link="https://github.com/spring-cloud/spring-cloud-openfeign/actions"] |
||||
|
||||
image:https://codecov.io/gh/spring-cloud/spring-cloud-openfeign/branch/main/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-openfeign"] |
||||
image:https://codecov.io/gh/spring-cloud/spring-cloud-openfeign/branch/main/graph/badge.svg["Codecov", link="https://app.codecov.io/gh/spring-cloud/spring-cloud-openfeign/tree/main"] |
||||
|
||||
image:https://api.codacy.com/project/badge/Grade/97b04c4e609c4b4f86b415e4437a6484["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-openfeign?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-openfeign&utm_campaign=Badge_Grade"] |
||||
|
||||
include::_attributes.adoc[] |
||||
|
||||
include::intro.adoc[] |
||||
|
||||
[[features]] |
||||
== Features |
||||
|
||||
* Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations |
||||
|
||||
[[building]] |
||||
== Building |
||||
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/src/main/asciidoc/building-jdk8.adoc[] |
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/modules/ROOT/partials/building.adoc[] |
||||
|
||||
[[contributing]] |
||||
== Contributing |
||||
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/src/main/asciidoc/contributing.adoc[] |
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/modules/ROOT/partials/contributing-docs.adoc[] |
||||
|
||||
[[license]] |
||||
== License |
||||
|
||||
The project license file is available https://raw.githubusercontent.com/spring-cloud/spring-cloud-openfeign/main/LICENSE.txt[here]. |
||||
|
@ -1,38 +0,0 @@
@@ -1,38 +0,0 @@
|
||||
|=== |
||||
|Name | Default | Description |
||||
|
||||
|feign.autoconfiguration.jackson.enabled | `+++false+++` | If true, PageJacksonModule and SortJacksonModule bean will be provided for Jackson page decoding. |
||||
|feign.circuitbreaker.alphanumeric-ids.enabled | `+++false+++` | If true, Circuit Breaker ids will only contain alphanumeric characters to allow for configuration via configuration properties. |
||||
|feign.circuitbreaker.enabled | `+++false+++` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker. |
||||
|feign.circuitbreaker.group.enabled | `+++false+++` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with with group. |
||||
|feign.client.config | | |
||||
|feign.client.decode-slash | `+++true+++` | Feign clients do not encode slash `/` characters by default. To change this behavior, set the `decodeSlash` to `false`. |
||||
|feign.client.default-config | `+++default+++` | |
||||
|feign.client.default-to-properties | `+++true+++` | |
||||
|feign.client.refresh-enabled | `+++false+++` | Enables options value refresh capability for Feign. |
||||
|feign.compression.request.enabled | `+++false+++` | Enables the request sent by Feign to be compressed. |
||||
|feign.compression.request.mime-types | `+++[text/xml, application/xml, application/json]+++` | The list of supported mime types. |
||||
|feign.compression.request.min-request-size | `+++2048+++` | The minimum threshold content size. |
||||
|feign.compression.response.enabled | `+++false+++` | Enables the response from Feign to be compressed. |
||||
|feign.encoder.charset-from-content-type | `+++false+++` | Indicates whether the charset should be derived from the {@code Content-Type} header. |
||||
|feign.httpclient.connection-timeout | `+++2000+++` | |
||||
|feign.httpclient.connection-timer-repeat | `+++3000+++` | |
||||
|feign.httpclient.disable-ssl-validation | `+++false+++` | |
||||
|feign.httpclient.enabled | `+++true+++` | Enables the use of the Apache HTTP Client by Feign. |
||||
|feign.httpclient.follow-redirects | `+++true+++` | |
||||
|feign.httpclient.hc5.enabled | `+++false+++` | Enables the use of the Apache HTTP Client 5 by Feign. |
||||
|feign.httpclient.hc5.pool-concurrency-policy | | Pool concurrency policies. |
||||
|feign.httpclient.hc5.pool-reuse-policy | | Pool connection re-use policies. |
||||
|feign.httpclient.hc5.socket-timeout | `+++5+++` | Default value for socket timeout. |
||||
|feign.httpclient.hc5.socket-timeout-unit | | Default value for socket timeout unit. |
||||
|feign.httpclient.max-connections | `+++200+++` | |
||||
|feign.httpclient.max-connections-per-route | `+++50+++` | |
||||
|feign.httpclient.ok-http.read-timeout | `+++60s+++` | {@link OkHttpClient} read timeout; defaults to 60 seconds. |
||||
|feign.httpclient.time-to-live | `+++900+++` | |
||||
|feign.httpclient.time-to-live-unit | | |
||||
|feign.metrics.enabled | `+++true+++` | Enables metrics capability for Feign. |
||||
|feign.oauth2.enabled | `+++false+++` | Enables feign interceptor for managing oauth2 access token. |
||||
|feign.oauth2.load-balanced | `+++false+++` | Enables load balancing for oauth2 access token provider. |
||||
|feign.okhttp.enabled | `+++false+++` | Enables the use of the OK HTTP Client by Feign. |
||||
|
||||
|=== |
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
include::spring-cloud-openfeign.adoc[] |
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
/* |
||||
* Copyright 2013-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign; |
||||
|
||||
import feign.Target; |
||||
|
||||
/** |
||||
* A {@link HardCodedTarget} implementation that resolves url from properties when the |
||||
* initial call is made. Using it allows specifying the url at runtime in an AOT-packaged |
||||
* application or a native image by setting the value of the |
||||
* `spring.cloud.openfeign.client.config.[clientId].url`. |
||||
* |
||||
* @author Olga Maciaszek-Sharma |
||||
* @see FeignClientProperties.FeignClientConfiguration#getUrl() |
||||
*/ |
||||
public class PropertyBasedTarget<T> extends Target.HardCodedTarget<T> { |
||||
|
||||
private String url; |
||||
|
||||
private final FeignClientProperties.FeignClientConfiguration config; |
||||
|
||||
public PropertyBasedTarget(Class<T> type, String name, FeignClientProperties.FeignClientConfiguration config) { |
||||
super(type, name, config.getUrl()); |
||||
this.config = config; |
||||
} |
||||
|
||||
@Override |
||||
public String url() { |
||||
if (url == null) { |
||||
url = config.getUrl(); |
||||
} |
||||
return url; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
/* |
||||
* Copyright 2013-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign; |
||||
|
||||
/** |
||||
* This class wraps url inside an object so that relevant proxy instance can be created |
||||
* using {@link RefreshableUrlFactoryBean}. |
||||
* |
||||
* @author Jasbir Singh |
||||
* @since 4.0.0 |
||||
*/ |
||||
public class RefreshableUrl { |
||||
|
||||
private final String url; |
||||
|
||||
public RefreshableUrl(String url) { |
||||
this.url = url; |
||||
} |
||||
|
||||
public String getUrl() { |
||||
return url; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,75 @@
@@ -0,0 +1,75 @@
|
||||
/* |
||||
* Copyright 2013-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
import org.springframework.beans.BeansException; |
||||
import org.springframework.beans.factory.FactoryBean; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.ApplicationContextAware; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* This factory bean creates {@link RefreshableUrl} instance as per the applicable |
||||
* configurations. |
||||
* |
||||
* @author Jasbir Singh |
||||
* @since 4.0.0 |
||||
*/ |
||||
public class RefreshableUrlFactoryBean implements FactoryBean<RefreshableUrl>, ApplicationContextAware { |
||||
|
||||
private ApplicationContext applicationContext; |
||||
|
||||
private String contextId; |
||||
|
||||
private RefreshableUrl refreshableUrl; |
||||
|
||||
@Override |
||||
public Class<?> getObjectType() { |
||||
return RefreshableUrl.class; |
||||
} |
||||
|
||||
@Override |
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
||||
this.applicationContext = applicationContext; |
||||
} |
||||
|
||||
@Override |
||||
public RefreshableUrl getObject() { |
||||
if (refreshableUrl != null) { |
||||
return refreshableUrl; |
||||
} |
||||
|
||||
FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class); |
||||
if (Objects.isNull(properties.getConfig())) { |
||||
return new RefreshableUrl(null); |
||||
} |
||||
FeignClientProperties.FeignClientConfiguration configuration = properties.getConfig().get(contextId); |
||||
if (Objects.isNull(configuration) || !StringUtils.hasText(configuration.getUrl())) { |
||||
return new RefreshableUrl(null); |
||||
} |
||||
|
||||
refreshableUrl = new RefreshableUrl(FeignClientsRegistrar.getUrl(configuration.getUrl())); |
||||
return refreshableUrl; |
||||
} |
||||
|
||||
public void setContextId(String contextId) { |
||||
this.contextId = contextId; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,126 @@
@@ -0,0 +1,126 @@
|
||||
/* |
||||
* Copyright 2022-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign.aot; |
||||
|
||||
import java.util.Collection; |
||||
import java.util.HashMap; |
||||
import java.util.HashSet; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import javax.lang.model.element.Modifier; |
||||
|
||||
import org.springframework.aot.generate.GeneratedMethod; |
||||
import org.springframework.aot.generate.GenerationContext; |
||||
import org.springframework.beans.factory.BeanFactory; |
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; |
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; |
||||
import org.springframework.beans.factory.aot.BeanRegistrationCode; |
||||
import org.springframework.beans.factory.support.RegisteredBean; |
||||
import org.springframework.cloud.openfeign.FeignClientFactory; |
||||
import org.springframework.cloud.openfeign.FeignClientSpecification; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.context.aot.ApplicationContextAotGenerator; |
||||
import org.springframework.context.support.GenericApplicationContext; |
||||
import org.springframework.javapoet.ClassName; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* A {@link BeanRegistrationAotProcessor} that creates an |
||||
* {@link BeanRegistrationAotContribution} for Feign child contexts. |
||||
* |
||||
* @author Olga Maciaszek-Sharma |
||||
* @since 4.0.0 |
||||
*/ |
||||
public class FeignChildContextInitializer implements BeanRegistrationAotProcessor { |
||||
|
||||
private final ApplicationContext applicationContext; |
||||
|
||||
private final FeignClientFactory feignClientFactory; |
||||
|
||||
public FeignChildContextInitializer(ApplicationContext applicationContext, FeignClientFactory feignClientFactory) { |
||||
this.applicationContext = applicationContext; |
||||
this.feignClientFactory = feignClientFactory; |
||||
} |
||||
|
||||
@Override |
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { |
||||
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext); |
||||
ConfigurableApplicationContext context = ((ConfigurableApplicationContext) applicationContext); |
||||
BeanFactory applicationBeanFactory = context.getBeanFactory(); |
||||
if (!((registeredBean.getBeanClass().equals(FeignClientFactory.class)) |
||||
&& registeredBean.getBeanFactory().equals(applicationBeanFactory))) { |
||||
return null; |
||||
} |
||||
Set<String> contextIds = new HashSet<>(getContextIdsFromConfig()); |
||||
Map<String, GenericApplicationContext> childContextAotContributions = contextIds.stream() |
||||
.map(contextId -> Map.entry(contextId, buildChildContext(contextId))) |
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
||||
return new AotContribution(childContextAotContributions); |
||||
} |
||||
|
||||
private GenericApplicationContext buildChildContext(String contextId) { |
||||
GenericApplicationContext childContext = feignClientFactory.buildContext(contextId); |
||||
feignClientFactory.registerBeans(contextId, childContext); |
||||
return childContext; |
||||
} |
||||
|
||||
private Collection<String> getContextIdsFromConfig() { |
||||
Map<String, FeignClientSpecification> configurations = feignClientFactory.getConfigurations(); |
||||
return configurations.keySet().stream().filter(key -> !key.startsWith("default.")).collect(Collectors.toSet()); |
||||
} |
||||
|
||||
private static class AotContribution implements BeanRegistrationAotContribution { |
||||
|
||||
private final Map<String, GenericApplicationContext> childContexts; |
||||
|
||||
AotContribution(Map<String, GenericApplicationContext> childContexts) { |
||||
this.childContexts = childContexts.entrySet().stream().filter(entry -> entry.getValue() != null) |
||||
.map(entry -> Map.entry(entry.getKey(), entry.getValue())) |
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
||||
} |
||||
|
||||
@Override |
||||
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) { |
||||
Map<String, ClassName> generatedInitializerClassNames = childContexts.entrySet().stream().map(entry -> { |
||||
String name = entry.getValue().getDisplayName(); |
||||
name = name.replaceAll("[-]", "_"); |
||||
GenerationContext childGenerationContext = generationContext.withName(name); |
||||
ClassName initializerClassName = new ApplicationContextAotGenerator() |
||||
.processAheadOfTime(entry.getValue(), childGenerationContext); |
||||
return Map.entry(entry.getKey(), initializerClassName); |
||||
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
||||
GeneratedMethod postProcessorMethod = beanRegistrationCode.getMethods() |
||||
.add("addFeignChildContextInitializer", method -> { |
||||
method.addJavadoc("Use AOT child context management initialization") |
||||
.addModifiers(Modifier.PRIVATE, Modifier.STATIC) |
||||
.addParameter(RegisteredBean.class, "registeredBean") |
||||
.addParameter(FeignClientFactory.class, "instance").returns(FeignClientFactory.class) |
||||
.addStatement("$T<String, Object> initializers = new $T<>()", Map.class, HashMap.class); |
||||
generatedInitializerClassNames.keySet() |
||||
.forEach(contextId -> method.addStatement("initializers.put($S, new $L())", contextId, |
||||
generatedInitializerClassNames.get(contextId))); |
||||
method.addStatement("return instance.withApplicationContextInitializers(initializers)"); |
||||
}); |
||||
beanRegistrationCode.addInstancePostProcessor(postProcessorMethod.toMethodReference()); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,215 @@
@@ -0,0 +1,215 @@
|
||||
/* |
||||
* Copyright 2022-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign.aot; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.lang.reflect.Parameter; |
||||
import java.util.Map; |
||||
import java.util.Objects; |
||||
import java.util.Set; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import javax.lang.model.element.Modifier; |
||||
|
||||
import org.springframework.aot.generate.GenerationContext; |
||||
import org.springframework.aot.generate.MethodReference; |
||||
import org.springframework.aot.hint.BindingReflectionHintsRegistrar; |
||||
import org.springframework.aot.hint.ReflectionHints; |
||||
import org.springframework.aot.hint.RuntimeHints; |
||||
import org.springframework.beans.MutablePropertyValues; |
||||
import org.springframework.beans.factory.BeanFactory; |
||||
import org.springframework.beans.factory.FactoryBean; |
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; |
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor; |
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode; |
||||
import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter; |
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder; |
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; |
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition; |
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder; |
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; |
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry; |
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory; |
||||
import org.springframework.beans.factory.support.GenericBeanDefinition; |
||||
import org.springframework.beans.factory.support.RegisteredBean; |
||||
import org.springframework.cloud.openfeign.FeignClientFactory; |
||||
import org.springframework.cloud.openfeign.FeignClientFactoryBean; |
||||
import org.springframework.cloud.openfeign.FeignClientSpecification; |
||||
import org.springframework.context.support.GenericApplicationContext; |
||||
import org.springframework.core.MethodParameter; |
||||
import org.springframework.javapoet.MethodSpec; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ClassUtils; |
||||
|
||||
/** |
||||
* A {@link BeanFactoryInitializationAotProcessor} that creates an |
||||
* {@link BeanFactoryInitializationAotContribution} that registers bean definitions and |
||||
* proxy and reflection hints for Feign client beans. |
||||
* |
||||
* @author Olga Maciaszek-Sharma |
||||
* @since 4.0.0 |
||||
*/ |
||||
public class FeignClientBeanFactoryInitializationAotProcessor |
||||
implements BeanRegistrationExcludeFilter, BeanFactoryInitializationAotProcessor { |
||||
|
||||
private final GenericApplicationContext context; |
||||
|
||||
private final Map<String, BeanDefinition> feignClientBeanDefinitions; |
||||
|
||||
private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar(); |
||||
|
||||
public FeignClientBeanFactoryInitializationAotProcessor(GenericApplicationContext context, |
||||
FeignClientFactory feignClientFactory) { |
||||
this.context = context; |
||||
this.feignClientBeanDefinitions = getFeignClientBeanDefinitions(feignClientFactory); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) { |
||||
return registeredBean.getBeanClass().equals(FeignClientFactoryBean.class) |
||||
|| feignClientBeanDefinitions.containsKey(registeredBean.getBeanClass().getName()); |
||||
} |
||||
|
||||
private Map<String, BeanDefinition> getFeignClientBeanDefinitions(FeignClientFactory feignClientFactory) { |
||||
Map<String, FeignClientSpecification> configurations = feignClientFactory.getConfigurations(); |
||||
return configurations.values().stream().map(FeignClientSpecification::getClassName).filter(Objects::nonNull) |
||||
.filter(className -> !className.equals("default")) |
||||
.map(className -> Map.entry(className, context.getBeanDefinition(className))) |
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
||||
} |
||||
|
||||
@SuppressWarnings("NullableProblems") |
||||
@Override |
||||
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) { |
||||
BeanFactory applicationBeanFactory = context.getBeanFactory(); |
||||
if (feignClientBeanDefinitions.isEmpty() || !beanFactory.equals(applicationBeanFactory)) { |
||||
return null; |
||||
} |
||||
return new AotContribution(feignClientBeanDefinitions); |
||||
} |
||||
|
||||
private void registerMethodHints(ReflectionHints hints, Class<?> clazz) { |
||||
for (Method method : clazz.getDeclaredMethods()) { |
||||
registerMethodHints(hints, method); |
||||
} |
||||
} |
||||
|
||||
private void registerMethodHints(ReflectionHints hints, Method method) { |
||||
for (Parameter parameter : method.getParameters()) { |
||||
bindingRegistrar.registerReflectionHints(hints, |
||||
MethodParameter.forParameter(parameter).getGenericParameterType()); |
||||
} |
||||
MethodParameter returnTypeParameter = MethodParameter.forExecutable(method, -1); |
||||
if (!void.class.equals(returnTypeParameter.getParameterType())) { |
||||
bindingRegistrar.registerReflectionHints(hints, returnTypeParameter.getGenericParameterType()); |
||||
} |
||||
|
||||
} |
||||
|
||||
// Visible for tests
|
||||
final class AotContribution implements BeanFactoryInitializationAotContribution { |
||||
|
||||
private final Map<String, BeanDefinition> feignClientBeanDefinitions; |
||||
|
||||
private AotContribution(Map<String, BeanDefinition> feignClientBeanDefinitions) { |
||||
this.feignClientBeanDefinitions = feignClientBeanDefinitions; |
||||
} |
||||
|
||||
@Override |
||||
public void applyTo(GenerationContext generationContext, |
||||
BeanFactoryInitializationCode beanFactoryInitializationCode) { |
||||
RuntimeHints hints = generationContext.getRuntimeHints(); |
||||
Set<String> feignClientRegistrationMethods = feignClientBeanDefinitions.values().stream() |
||||
.map(beanDefinition -> { |
||||
Assert.notNull(beanDefinition, "beanDefinition cannot be null"); |
||||
Assert.isInstanceOf(GenericBeanDefinition.class, beanDefinition); |
||||
GenericBeanDefinition registeredBeanDefinition = (GenericBeanDefinition) beanDefinition; |
||||
MutablePropertyValues feignClientProperties = registeredBeanDefinition.getPropertyValues(); |
||||
String className = (String) feignClientProperties.get("type"); |
||||
Assert.notNull(className, "className cannot be null"); |
||||
Class<?> clazz = ClassUtils.resolveClassName(className, null); |
||||
hints.proxies().registerJdkProxy(clazz); |
||||
registerMethodHints(hints.reflection(), clazz); |
||||
return beanFactoryInitializationCode.getMethods() |
||||
.add(buildMethodName(className), method -> generateFeignClientRegistrationMethod(method, |
||||
feignClientProperties, registeredBeanDefinition)) |
||||
.getName(); |
||||
}).collect(Collectors.toSet()); |
||||
MethodReference initializerMethod = beanFactoryInitializationCode.getMethods() |
||||
.add("initialize", method -> generateInitializerMethod(method, feignClientRegistrationMethods)) |
||||
.toMethodReference(); |
||||
beanFactoryInitializationCode.addInitializer(initializerMethod); |
||||
} |
||||
|
||||
private String buildMethodName(String clientName) { |
||||
return "register" + clientName + "FeignClient"; |
||||
} |
||||
|
||||
private void generateInitializerMethod(MethodSpec.Builder method, Set<String> feignClientRegistrationMethods) { |
||||
method.addModifiers(Modifier.PUBLIC); |
||||
method.addParameter(DefaultListableBeanFactory.class, "registry"); |
||||
feignClientRegistrationMethods.forEach(feignClientRegistrationMethod -> method.addStatement("$N(registry)", |
||||
feignClientRegistrationMethod)); |
||||
} |
||||
|
||||
private void generateFeignClientRegistrationMethod(MethodSpec.Builder method, |
||||
MutablePropertyValues feignClientPropertyValues, GenericBeanDefinition registeredBeanDefinition) { |
||||
Object feignQualifiers = feignClientPropertyValues.get("qualifiers"); |
||||
Assert.notNull(feignQualifiers, "Feign qualifiers cannot be null"); |
||||
String qualifiers = "{\"" + String.join("\",\"", (String[]) feignQualifiers) + "\"}"; |
||||
method.addJavadoc("register Feign Client: $L", feignClientPropertyValues.get("type")) |
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC) |
||||
.addParameter(BeanDefinitionRegistry.class, "registry") |
||||
.addStatement("Class clazz = $T.resolveClassName(\"$L\", null)", ClassUtils.class, |
||||
feignClientPropertyValues.get("type")) |
||||
.addStatement("$T definition = $T.genericBeanDefinition($T.class)", BeanDefinitionBuilder.class, |
||||
BeanDefinitionBuilder.class, FeignClientFactoryBean.class) |
||||
.addStatement("definition.addPropertyValue(\"name\",\"$L\")", feignClientPropertyValues.get("name")) |
||||
.addStatement("definition.addPropertyValue(\"contextId\", \"$L\")", |
||||
feignClientPropertyValues.get("contextId")) |
||||
.addStatement("definition.addPropertyValue(\"type\", clazz)") |
||||
.addStatement("definition.addPropertyValue(\"url\", \"$L\")", feignClientPropertyValues.get("url")) |
||||
.addStatement("definition.addPropertyValue(\"path\", \"$L\")", |
||||
feignClientPropertyValues.get("path")) |
||||
.addStatement("definition.addPropertyValue(\"dismiss404\", $L)", |
||||
feignClientPropertyValues.get("dismiss404")) |
||||
.addStatement("definition.addPropertyValue(\"fallback\", $T.class)", |
||||
feignClientPropertyValues.get("fallback")) |
||||
.addStatement("definition.addPropertyValue(\"fallbackFactory\", $T.class)", |
||||
feignClientPropertyValues.get("fallbackFactory")) |
||||
.addStatement("definition.setAutowireMode($L)", registeredBeanDefinition.getAutowireMode()) |
||||
.addStatement("definition.setLazyInit($L)", |
||||
registeredBeanDefinition.getLazyInit() != null ? registeredBeanDefinition.getLazyInit() |
||||
: false) |
||||
.addStatement("$T beanDefinition = definition.getBeanDefinition()", AbstractBeanDefinition.class) |
||||
.addStatement("beanDefinition.setAttribute(\"$L\", clazz)", FactoryBean.OBJECT_TYPE_ATTRIBUTE) |
||||
.addStatement("beanDefinition.setPrimary($L)", registeredBeanDefinition.isPrimary()) |
||||
.addStatement("$T holder = new $T(beanDefinition, \"$L\", new String[]$L)", |
||||
BeanDefinitionHolder.class, BeanDefinitionHolder.class, |
||||
feignClientPropertyValues.get("type"), qualifiers) |
||||
.addStatement("$T.registerBeanDefinition(holder, registry) ", BeanDefinitionReaderUtils.class); |
||||
} |
||||
|
||||
// Visible for tests
|
||||
Map<String, BeanDefinition> getFeignClientBeanDefinitions() { |
||||
return feignClientBeanDefinitions; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
/* |
||||
* Copyright 2013-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign.clientconfig; |
||||
|
||||
import java.net.http.HttpClient; |
||||
import java.time.Duration; |
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
||||
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
/** |
||||
* Default configuration for {@link HttpClient}. |
||||
* |
||||
* @author changjin wei(魏昌进) |
||||
*/ |
||||
@Configuration(proxyBeanMethods = false) |
||||
@ConditionalOnMissingBean(HttpClient.class) |
||||
public class Http2ClientFeignConfiguration { |
||||
|
||||
@Bean |
||||
public HttpClient httpClient(FeignHttpClientProperties httpClientProperties) { |
||||
return HttpClient.newBuilder() |
||||
.followRedirects(httpClientProperties.isFollowRedirects() ? HttpClient.Redirect.ALWAYS |
||||
: HttpClient.Redirect.NEVER) |
||||
.version(HttpClient.Version.valueOf(httpClientProperties.getHttp2().getVersion())) |
||||
.connectTimeout(Duration.ofMillis(httpClientProperties.getConnectionTimeout())).build(); |
||||
} |
||||
|
||||
} |
@ -1,125 +0,0 @@
@@ -1,125 +0,0 @@
|
||||
/* |
||||
* Copyright 2013-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign.clientconfig; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.Timer; |
||||
import java.util.TimerTask; |
||||
|
||||
import javax.annotation.PreDestroy; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.apache.http.client.config.RequestConfig; |
||||
import org.apache.http.config.RegistryBuilder; |
||||
import org.apache.http.conn.HttpClientConnectionManager; |
||||
import org.apache.http.impl.client.CloseableHttpClient; |
||||
import org.apache.http.impl.client.HttpClientBuilder; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
||||
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory; |
||||
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory; |
||||
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
/** |
||||
* Default configuration for {@link CloseableHttpClient}. |
||||
* |
||||
* @author Ryan Baxter |
||||
* @author Marcin Grzejszczak |
||||
* @author Spencer Gibb |
||||
* @author Olga Maciaszek-Sharma |
||||
*/ |
||||
@Configuration(proxyBeanMethods = false) |
||||
@ConditionalOnMissingBean(CloseableHttpClient.class) |
||||
public class HttpClientFeignConfiguration { |
||||
|
||||
private static final Log LOG = LogFactory.getLog(HttpClientFeignConfiguration.class); |
||||
|
||||
private final Timer connectionManagerTimer = new Timer("FeignApacheHttpClientConfiguration.connectionManagerTimer", |
||||
true); |
||||
|
||||
private CloseableHttpClient httpClient; |
||||
|
||||
@Autowired(required = false) |
||||
private RegistryBuilder registryBuilder; |
||||
|
||||
@Bean |
||||
@ConditionalOnMissingBean(HttpClientConnectionManager.class) |
||||
public HttpClientConnectionManager connectionManager( |
||||
ApacheHttpClientConnectionManagerFactory connectionManagerFactory, |
||||
FeignHttpClientProperties httpClientProperties) { |
||||
final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager( |
||||
httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(), |
||||
httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(), |
||||
httpClientProperties.getTimeToLiveUnit(), this.registryBuilder); |
||||
this.connectionManagerTimer.schedule(new TimerTask() { |
||||
@Override |
||||
public void run() { |
||||
connectionManager.closeExpiredConnections(); |
||||
} |
||||
}, 30000, httpClientProperties.getConnectionTimerRepeat()); |
||||
return connectionManager; |
||||
} |
||||
|
||||
@Bean |
||||
@ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "true") |
||||
public CloseableHttpClient customHttpClient(HttpClientConnectionManager httpClientConnectionManager, |
||||
FeignHttpClientProperties httpClientProperties) { |
||||
HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement().useSystemProperties(); |
||||
this.httpClient = createClient(builder, httpClientConnectionManager, httpClientProperties); |
||||
return this.httpClient; |
||||
} |
||||
|
||||
@Bean |
||||
@ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "false", matchIfMissing = true) |
||||
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, |
||||
HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) { |
||||
this.httpClient = createClient(httpClientFactory.createBuilder(), httpClientConnectionManager, |
||||
httpClientProperties); |
||||
return this.httpClient; |
||||
} |
||||
|
||||
private CloseableHttpClient createClient(HttpClientBuilder builder, |
||||
HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) { |
||||
RequestConfig defaultRequestConfig = RequestConfig.custom() |
||||
.setConnectTimeout(httpClientProperties.getConnectionTimeout()) |
||||
.setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build(); |
||||
CloseableHttpClient httpClient = builder.setDefaultRequestConfig(defaultRequestConfig) |
||||
.setConnectionManager(httpClientConnectionManager).build(); |
||||
return httpClient; |
||||
} |
||||
|
||||
@PreDestroy |
||||
public void destroy() { |
||||
this.connectionManagerTimer.cancel(); |
||||
if (this.httpClient != null) { |
||||
try { |
||||
this.httpClient.close(); |
||||
} |
||||
catch (IOException e) { |
||||
if (LOG.isErrorEnabled()) { |
||||
LOG.error("Could not correctly close httpClient."); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,78 +0,0 @@
@@ -1,78 +0,0 @@
|
||||
/* |
||||
* Copyright 2013-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign.clientconfig; |
||||
|
||||
import java.time.Duration; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import javax.annotation.PreDestroy; |
||||
|
||||
import okhttp3.ConnectionPool; |
||||
import okhttp3.OkHttpClient; |
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
||||
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory; |
||||
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory; |
||||
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
/** |
||||
* Defualt configuration for {@link OkHttpClient}. |
||||
* |
||||
* @author Ryan Baxter |
||||
* @author Marcin Grzejszczak |
||||
* @author Spencer Gibb |
||||
* @author Olga Maciaszek-Sharma |
||||
*/ |
||||
@Configuration(proxyBeanMethods = false) |
||||
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class) |
||||
public class OkHttpFeignConfiguration { |
||||
|
||||
private okhttp3.OkHttpClient okHttpClient; |
||||
|
||||
@Bean |
||||
@ConditionalOnMissingBean(ConnectionPool.class) |
||||
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties, |
||||
OkHttpClientConnectionPoolFactory connectionPoolFactory) { |
||||
int maxTotalConnections = httpClientProperties.getMaxConnections(); |
||||
long timeToLive = httpClientProperties.getTimeToLive(); |
||||
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit(); |
||||
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit); |
||||
} |
||||
|
||||
@Bean |
||||
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, |
||||
FeignHttpClientProperties httpClientProperties) { |
||||
boolean followRedirects = httpClientProperties.isFollowRedirects(); |
||||
int connectTimeout = httpClientProperties.getConnectionTimeout(); |
||||
Duration reaTimeout = httpClientProperties.getOkHttp().getReadTimeout(); |
||||
this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()) |
||||
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects) |
||||
.readTimeout(reaTimeout).connectionPool(connectionPool).build(); |
||||
return this.okHttpClient; |
||||
} |
||||
|
||||
@PreDestroy |
||||
public void destroy() { |
||||
if (this.okHttpClient != null) { |
||||
this.okHttpClient.dispatcher().executorService().shutdown(); |
||||
this.okHttpClient.connectionPool().evictAll(); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
/* |
||||
* Copyright 2013-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign.encoding; |
||||
|
||||
import feign.Client; |
||||
import feign.okhttp.OkHttpClient; |
||||
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
||||
import org.springframework.context.annotation.Condition; |
||||
|
||||
/** |
||||
* A {@link Condition} that verifies whether the conditions for creating Feign |
||||
* {@link Client} beans that either are of type {@link OkHttpClient} or have a delegate of |
||||
* type {@link OkHttpClient} are not met. |
||||
* |
||||
* @author Olga Maciaszek-Sharma |
||||
* @since 4.0.2 |
||||
*/ |
||||
public class OkHttpFeignClientBeanMissingCondition extends AnyNestedCondition { |
||||
|
||||
public OkHttpFeignClientBeanMissingCondition() { |
||||
super(ConfigurationPhase.REGISTER_BEAN); |
||||
} |
||||
|
||||
@ConditionalOnMissingClass("feign.okhttp.OkHttpClient") |
||||
static class FeignOkHttpClientPresent { |
||||
|
||||
} |
||||
|
||||
@ConditionalOnProperty(value = "spring.cloud.openfeign.okhttp.enabled", havingValue = "false") |
||||
static class FeignOkHttpClientEnabled { |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
/* |
||||
* Copyright 2013-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign.loadbalancer; |
||||
|
||||
import feign.Request; |
||||
|
||||
import org.springframework.cloud.client.ServiceInstance; |
||||
import org.springframework.core.annotation.Order; |
||||
|
||||
/** |
||||
* Allows applications to transform the load-balanced {@link Request} given the chosen |
||||
* {@link org.springframework.cloud.client.ServiceInstance}. |
||||
* |
||||
* @author changjin wei(魏昌进) |
||||
*/ |
||||
@Order(LoadBalancerFeignRequestTransformer.DEFAULT_ORDER) |
||||
public interface LoadBalancerFeignRequestTransformer { |
||||
|
||||
/** |
||||
* Order for the {@link LoadBalancerFeignRequestTransformer}. |
||||
*/ |
||||
int DEFAULT_ORDER = 0; |
||||
|
||||
/** |
||||
* Allows transforming load-balanced requests based on the provided |
||||
* {@link ServiceInstance}. |
||||
* @param request Original request. |
||||
* @param instance ServiceInstance returned from LoadBalancer. |
||||
* @return New request or original request |
||||
*/ |
||||
Request transformRequest(Request request, ServiceInstance instance); |
||||
|
||||
} |
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
/* |
||||
* Copyright 2013-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign.loadbalancer; |
||||
|
||||
import java.net.URI; |
||||
import java.util.Collection; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import feign.Request; |
||||
|
||||
import org.springframework.cloud.client.ServiceInstance; |
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties; |
||||
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; |
||||
|
||||
/** |
||||
* To add X-Forwarded-Host and X-Forwarded-Proto Headers. |
||||
* |
||||
* @author changjin wei(魏昌进) |
||||
*/ |
||||
public class XForwardedHeadersTransformer implements LoadBalancerFeignRequestTransformer { |
||||
|
||||
private final ReactiveLoadBalancer.Factory<ServiceInstance> factory; |
||||
|
||||
public XForwardedHeadersTransformer(ReactiveLoadBalancer.Factory<ServiceInstance> factory) { |
||||
this.factory = factory; |
||||
} |
||||
|
||||
@Override |
||||
public Request transformRequest(Request request, ServiceInstance instance) { |
||||
if (instance == null) { |
||||
return request; |
||||
} |
||||
LoadBalancerProperties.XForwarded xForwarded = factory.getProperties(instance.getServiceId()).getXForwarded(); |
||||
if (xForwarded.isEnabled()) { |
||||
Map<String, Collection<String>> headers = new HashMap<>(request.headers()); |
||||
URI uri = URI.create(request.url()); |
||||
String xForwardedHost = uri.getHost(); |
||||
String xForwardedProto = uri.getScheme(); |
||||
headers.put("X-Forwarded-Host", Collections.singleton(xForwardedHost)); |
||||
headers.put("X-Forwarded-Proto", Collections.singleton(xForwardedProto)); |
||||
request = Request.create(request.httpMethod(), request.url(), headers, request.body(), request.charset(), |
||||
request.requestTemplate()); |
||||
} |
||||
return request; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,144 @@
@@ -0,0 +1,144 @@
|
||||
/* |
||||
* Copyright 2015-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign.security; |
||||
|
||||
import java.net.URI; |
||||
import java.util.Optional; |
||||
|
||||
import feign.RequestInterceptor; |
||||
import feign.RequestTemplate; |
||||
import feign.Target; |
||||
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.core.authority.AuthorityUtils; |
||||
import org.springframework.security.core.context.SecurityContextHolder; |
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; |
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; |
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; |
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* A {@link RequestInterceptor} for OAuth2 Feign Requests. By default, it uses the |
||||
* {@link OAuth2AuthorizedClientManager } to get {@link OAuth2AuthorizedClient } that |
||||
* holds an {@link OAuth2AccessToken }. If the user has specified an OAuth2 |
||||
* {@code clientRegistrationId} using the |
||||
* {@code spring.cloud.openfeign.oauth2.clientRegistrationId} property, it will be used to |
||||
* retrieve the token. If the token is not retrieved or the {@code clientRegistrationId} |
||||
* has not been specified, the {@code serviceId} retrieved from the {@code url} host |
||||
* segment will be used. This approach is convenient for load-balanced Feign clients. For |
||||
* non-load-balanced ones, the property-based {@code clientRegistrationId} is a suitable |
||||
* approach. |
||||
* |
||||
* @author Dangzhicairang(小水牛) |
||||
* @author Olga Maciaszek-Sharma |
||||
* @since 4.0.0 |
||||
*/ |
||||
public class OAuth2AccessTokenInterceptor implements RequestInterceptor { |
||||
|
||||
/** |
||||
* The name of the token. |
||||
*/ |
||||
public static final String BEARER = "Bearer"; |
||||
|
||||
/** |
||||
* The name of the header. |
||||
*/ |
||||
public static final String AUTHORIZATION = "Authorization"; |
||||
|
||||
private final String tokenType; |
||||
|
||||
private final String header; |
||||
|
||||
private final String clientRegistrationId; |
||||
|
||||
private final OAuth2AuthorizedClientManager authorizedClientManager; |
||||
|
||||
private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous", |
||||
"anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); |
||||
|
||||
public OAuth2AccessTokenInterceptor(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) { |
||||
this(null, oAuth2AuthorizedClientManager); |
||||
} |
||||
|
||||
public OAuth2AccessTokenInterceptor(String clientRegistrationId, |
||||
OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) { |
||||
this(BEARER, AUTHORIZATION, clientRegistrationId, oAuth2AuthorizedClientManager); |
||||
} |
||||
|
||||
public OAuth2AccessTokenInterceptor(String tokenType, String header, String clientRegistrationId, |
||||
OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) { |
||||
this.tokenType = tokenType; |
||||
this.header = header; |
||||
this.clientRegistrationId = clientRegistrationId; |
||||
this.authorizedClientManager = oAuth2AuthorizedClientManager; |
||||
} |
||||
|
||||
@Override |
||||
public void apply(RequestTemplate template) { |
||||
OAuth2AccessToken token = getToken(template); |
||||
String extractedToken = String.format("%s %s", tokenType, token.getTokenValue()); |
||||
template.header(header); |
||||
template.header(header, extractedToken); |
||||
} |
||||
|
||||
public OAuth2AccessToken getToken(RequestTemplate template) { |
||||
// If specified, try to use them to get token.
|
||||
if (StringUtils.hasText(clientRegistrationId)) { |
||||
OAuth2AccessToken token = getToken(clientRegistrationId); |
||||
if (token != null) { |
||||
return token; |
||||
} |
||||
} |
||||
|
||||
// If not specified use host (synonymous with serviceId for load-balanced
|
||||
// requests; non-load-balanced requests should use the method above).
|
||||
OAuth2AccessToken token = getToken(getServiceId(template)); |
||||
if (token != null) { |
||||
return token; |
||||
} |
||||
throw new IllegalStateException("OAuth2 token has not been successfully acquired."); |
||||
} |
||||
|
||||
protected OAuth2AccessToken getToken(String clientRegistrationId) { |
||||
if (!StringUtils.hasText(clientRegistrationId)) { |
||||
return null; |
||||
} |
||||
|
||||
Authentication principal = SecurityContextHolder.getContext().getAuthentication(); |
||||
if (principal == null) { |
||||
principal = ANONYMOUS_AUTHENTICATION; |
||||
} |
||||
|
||||
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId) |
||||
.principal(principal).build(); |
||||
OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest); |
||||
return Optional.ofNullable(authorizedClient).map(OAuth2AuthorizedClient::getAccessToken).orElse(null); |
||||
} |
||||
|
||||
private static String getServiceId(RequestTemplate template) { |
||||
Target<?> feignTarget = template.feignTarget(); |
||||
Assert.notNull(feignTarget, "FeignTarget may not be null."); |
||||
String url = feignTarget.url(); |
||||
Assert.hasLength(url, "Url may not be empty."); |
||||
final URI originalUri = URI.create(url); |
||||
return originalUri.getHost(); |
||||
} |
||||
|
||||
} |
@ -1,183 +0,0 @@
@@ -1,183 +0,0 @@
|
||||
/* |
||||
* Copyright 2015-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign.security; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
import feign.RequestInterceptor; |
||||
import feign.RequestTemplate; |
||||
|
||||
import org.springframework.security.oauth2.client.OAuth2ClientContext; |
||||
import org.springframework.security.oauth2.client.http.AccessTokenRequiredException; |
||||
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; |
||||
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException; |
||||
import org.springframework.security.oauth2.client.token.AccessTokenProvider; |
||||
import org.springframework.security.oauth2.client.token.AccessTokenProviderChain; |
||||
import org.springframework.security.oauth2.client.token.AccessTokenRequest; |
||||
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; |
||||
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; |
||||
import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider; |
||||
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; |
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken; |
||||
|
||||
/** |
||||
* Pre-defined custom RequestInterceptor for Feign Requests. It uses the |
||||
* {@link OAuth2ClientContext OAuth2ClientContext} provided from the environment and |
||||
* construct a new header on the request before it is made by Feign. |
||||
* |
||||
* @author Joao Pedro Evangelista |
||||
* @author Tim Ysewyn |
||||
* @since 3.0.0 |
||||
*/ |
||||
@Deprecated // spring-security-oauth2 reached EOL
|
||||
public class OAuth2FeignRequestInterceptor implements RequestInterceptor { |
||||
|
||||
/** |
||||
* The name of the token. |
||||
*/ |
||||
public static final String BEARER = "Bearer"; |
||||
|
||||
/** |
||||
* The name of the header. |
||||
*/ |
||||
public static final String AUTHORIZATION = "Authorization"; |
||||
|
||||
private final OAuth2ClientContext oAuth2ClientContext; |
||||
|
||||
private final OAuth2ProtectedResourceDetails resource; |
||||
|
||||
private final String tokenType; |
||||
|
||||
private final String header; |
||||
|
||||
private AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(Arrays.<AccessTokenProvider>asList( |
||||
new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), |
||||
new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider())); |
||||
|
||||
/** |
||||
* Default constructor which uses the provided OAuth2ClientContext and Bearer tokens |
||||
* within Authorization header. |
||||
* @param oAuth2ClientContext provided context |
||||
* @param resource type of resource to be accessed |
||||
*/ |
||||
public OAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, |
||||
OAuth2ProtectedResourceDetails resource) { |
||||
this(oAuth2ClientContext, resource, BEARER, AUTHORIZATION); |
||||
} |
||||
|
||||
/** |
||||
* Fully customizable constructor for changing token type and header name, in cases of |
||||
* Bearer and Authorization is not the default such as "bearer", "authorization". |
||||
* @param oAuth2ClientContext current oAuth2 Context |
||||
* @param resource type of resource to be accessed |
||||
* @param tokenType type of token e.g. "token", "Bearer" |
||||
* @param header name of the header e.g. "Authorization", "authorization" |
||||
*/ |
||||
public OAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, |
||||
OAuth2ProtectedResourceDetails resource, String tokenType, String header) { |
||||
this.oAuth2ClientContext = oAuth2ClientContext; |
||||
this.resource = resource; |
||||
this.tokenType = tokenType; |
||||
this.header = header; |
||||
} |
||||
|
||||
/** |
||||
* Create a template with the header of provided name and extracted extract. |
||||
* |
||||
* @see RequestInterceptor#apply(RequestTemplate) |
||||
*/ |
||||
@Override |
||||
public void apply(RequestTemplate template) { |
||||
template.header(header); // Clears out the header, no "clear" method available.
|
||||
template.header(header, extract(tokenType)); |
||||
} |
||||
|
||||
/** |
||||
* Extracts the token extract id the access token exists or returning an empty extract |
||||
* if there is no one on the context it may occasionally causes Unauthorized response |
||||
* since the token extract is empty. |
||||
* @param tokenType type name of token |
||||
* @return token value from context if it exists otherwise empty String |
||||
*/ |
||||
protected String extract(String tokenType) { |
||||
OAuth2AccessToken accessToken = getToken(); |
||||
return String.format("%s %s", tokenType, accessToken.getValue()); |
||||
} |
||||
|
||||
/** |
||||
* Extract the access token within the request or try to acquire a new one by |
||||
* delegating it to {@link #acquireAccessToken()}. |
||||
* @return valid token |
||||
*/ |
||||
public OAuth2AccessToken getToken() { |
||||
|
||||
OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken(); |
||||
if (accessToken == null || accessToken.isExpired()) { |
||||
try { |
||||
accessToken = acquireAccessToken(); |
||||
} |
||||
catch (UserRedirectRequiredException e) { |
||||
oAuth2ClientContext.setAccessToken(null); |
||||
String stateKey = e.getStateKey(); |
||||
if (stateKey != null) { |
||||
Object stateToPreserve = e.getStateToPreserve(); |
||||
if (stateToPreserve == null) { |
||||
stateToPreserve = "NONE"; |
||||
} |
||||
oAuth2ClientContext.setPreservedState(stateKey, stateToPreserve); |
||||
} |
||||
throw e; |
||||
} |
||||
} |
||||
return accessToken; |
||||
} |
||||
|
||||
/** |
||||
* Try to acquire the token using a access token provider. |
||||
* @return valid access token |
||||
* @throws UserRedirectRequiredException in case the user needs to be redirected to an |
||||
* approval page or login page |
||||
*/ |
||||
protected OAuth2AccessToken acquireAccessToken() throws UserRedirectRequiredException { |
||||
AccessTokenRequest tokenRequest = oAuth2ClientContext.getAccessTokenRequest(); |
||||
if (tokenRequest == null) { |
||||
throw new AccessTokenRequiredException( |
||||
"Cannot find valid context on request for resource '" + resource.getId() + "'.", resource); |
||||
} |
||||
String stateKey = tokenRequest.getStateKey(); |
||||
if (stateKey != null) { |
||||
tokenRequest.setPreservedState(oAuth2ClientContext.removePreservedState(stateKey)); |
||||
} |
||||
OAuth2AccessToken existingToken = oAuth2ClientContext.getAccessToken(); |
||||
if (existingToken != null) { |
||||
oAuth2ClientContext.setAccessToken(existingToken); |
||||
} |
||||
OAuth2AccessToken obtainableAccessToken; |
||||
obtainableAccessToken = accessTokenProvider.obtainAccessToken(resource, tokenRequest); |
||||
if (obtainableAccessToken == null || obtainableAccessToken.getValue() == null) { |
||||
throw new IllegalStateException( |
||||
" Access token provider returned a null token, which is illegal according to the contract."); |
||||
} |
||||
oAuth2ClientContext.setAccessToken(obtainableAccessToken); |
||||
return obtainableAccessToken; |
||||
} |
||||
|
||||
public void setAccessTokenProvider(AccessTokenProvider accessTokenProvider) { |
||||
this.accessTokenProvider = accessTokenProvider; |
||||
} |
||||
|
||||
} |
@ -1,81 +0,0 @@
@@ -1,81 +0,0 @@
|
||||
/* |
||||
* Copyright 2015-2021 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign.security; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor; |
||||
import org.springframework.security.oauth2.client.OAuth2ClientContext; |
||||
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; |
||||
import org.springframework.security.oauth2.client.token.AccessTokenProvider; |
||||
import org.springframework.security.oauth2.client.token.AccessTokenProviderChain; |
||||
import org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport; |
||||
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; |
||||
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; |
||||
import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider; |
||||
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; |
||||
|
||||
/** |
||||
* Allows to customize pre-defined {@link OAuth2FeignRequestInterceptor} using configurer |
||||
* beans of class {@link OAuth2FeignRequestInterceptorConfigurer}. Each configurer |
||||
* instance can add {@link AccessTokenProvider} new {@link ClientHttpRequestInterceptor} |
||||
* instances. |
||||
* |
||||
* @author Wojciech Mąka |
||||
* @since 3.1.1 |
||||
*/ |
||||
public class OAuth2FeignRequestInterceptorBuilder { |
||||
|
||||
private AccessTokenProvider accessTokenProvider; |
||||
|
||||
private final List<ClientHttpRequestInterceptor> accessTokenProviderInterceptors = new ArrayList<>(); |
||||
|
||||
public OAuth2FeignRequestInterceptorBuilder() { |
||||
accessTokenProvider = new AccessTokenProviderChain(Arrays.<AccessTokenProvider>asList( |
||||
new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), |
||||
new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider())); |
||||
} |
||||
|
||||
public OAuth2FeignRequestInterceptorBuilder withAccessTokenProviderInterceptors( |
||||
ClientHttpRequestInterceptor... interceptors) { |
||||
accessTokenProviderInterceptors.addAll(Arrays.asList(interceptors)); |
||||
return this; |
||||
} |
||||
|
||||
OAuth2FeignRequestInterceptor build(OAuth2ClientContext oAuth2ClientContext, |
||||
OAuth2ProtectedResourceDetails resource) { |
||||
if (OAuth2AccessTokenSupport.class.isAssignableFrom(accessTokenProvider.getClass())) { |
||||
((OAuth2AccessTokenSupport) accessTokenProvider).setInterceptors(accessTokenProviderInterceptors); |
||||
} |
||||
final OAuth2FeignRequestInterceptor feignRequestInterceptor = new OAuth2FeignRequestInterceptor( |
||||
oAuth2ClientContext, resource); |
||||
feignRequestInterceptor.setAccessTokenProvider(accessTokenProvider); |
||||
return feignRequestInterceptor; |
||||
} |
||||
|
||||
public static OAuth2FeignRequestInterceptor buildWithConfigurers(OAuth2ClientContext oAuth2ClientContext, |
||||
OAuth2ProtectedResourceDetails resource, List<OAuth2FeignRequestInterceptorConfigurer> buildConfigurers) { |
||||
final OAuth2FeignRequestInterceptorBuilder builder = new OAuth2FeignRequestInterceptorBuilder(); |
||||
for (OAuth2FeignRequestInterceptorConfigurer configurer : buildConfigurers) { |
||||
configurer.customize(builder); |
||||
} |
||||
return builder.build(oAuth2ClientContext, resource); |
||||
} |
||||
|
||||
} |
@ -1,35 +0,0 @@
@@ -1,35 +0,0 @@
|
||||
/* |
||||
* Copyright 2015-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign.security; |
||||
|
||||
import org.springframework.security.oauth2.client.token.AccessTokenProvider; |
||||
|
||||
/** |
||||
* Interface for configurer beans working with |
||||
* {@link OAuth2FeignRequestInterceptorBuilder} in order to provide custom interceptors |
||||
* for {@link AccessTokenProvider} managed internally by |
||||
* {@link OAuth2FeignRequestInterceptor}. |
||||
* |
||||
* @author Wojciech Mąka |
||||
* @since 3.1.1 |
||||
*/ |
||||
@FunctionalInterface |
||||
public interface OAuth2FeignRequestInterceptorConfigurer { |
||||
|
||||
void customize(OAuth2FeignRequestInterceptorBuilder requestInterceptorBuilder); |
||||
|
||||
} |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
org.springframework.aot.hint.RuntimeHintsRegistrar=\ |
||||
org.springframework.cloud.openfeign.FeignHints |
@ -1,6 +1,5 @@
@@ -1,6 +1,5 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
||||
org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\ |
||||
org.springframework.cloud.openfeign.FeignAutoConfiguration,\ |
||||
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\ |
||||
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration,\ |
||||
org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration |
||||
org.springframework.cloud.openfeign.FeignAutoConfiguration |
||||
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration |
||||
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration |
||||
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration |
@ -0,0 +1,169 @@
@@ -0,0 +1,169 @@
|
||||
/* |
||||
* Copyright 2013-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.cloud.openfeign; |
||||
|
||||
import java.lang.reflect.Field; |
||||
import java.util.List; |
||||
|
||||
import feign.Capability; |
||||
import feign.Feign; |
||||
import feign.Logger; |
||||
import feign.RequestInterceptor; |
||||
import feign.micrometer.MicrometerObservationCapability; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; |
||||
import org.springframework.boot.test.context.SpringBootTest; |
||||
import org.springframework.cloud.openfeign.clientconfig.FeignClientConfigurer; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.test.annotation.DirtiesContext; |
||||
import org.springframework.util.ReflectionUtils; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* @author matt king |
||||
* @author Jonatan Ivanov |
||||
* @author Olga Maciaszek-Sharma |
||||
*/ |
||||
@DirtiesContext |
||||
@SpringBootTest(classes = EagerInitFeignClientUsingConfigurerTests.Application.class, value = { |
||||
"spring.cloud.openfeign.client.config.default.loggerLevel=full", |
||||
"spring.cloud.openfeign.client.config.default.requestInterceptors[0]=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.FooRequestInterceptor", |
||||
"spring.cloud.openfeign.client.config.default.requestInterceptors[1]=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.BarRequestInterceptor" }) |
||||
class EagerInitFeignClientUsingConfigurerTests { |
||||
|
||||
@Autowired |
||||
private ApplicationContext applicationContext; |
||||
|
||||
@Autowired |
||||
private FeignClientFactory context; |
||||
|
||||
private static final String BEAN_NAME_PREFIX = "&org.springframework.cloud.openfeign.EagerInitFeignClientUsingConfigurerTests$"; |
||||
|
||||
@Test |
||||
public void testFeignClient() { |
||||
FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) applicationContext |
||||
.getBean(BEAN_NAME_PREFIX + "TestFeignClient"); |
||||
Feign.Builder builder = factoryBean.feign(context); |
||||
|
||||
List<RequestInterceptor> interceptors = (List) getBuilderValue(builder, "requestInterceptors"); |
||||
assertThat(interceptors.size()).as("interceptors not set").isEqualTo(3); |
||||
assertThat(getBuilderValue(builder, "logLevel")).as("log level not set").isEqualTo(Logger.Level.FULL); |
||||
|
||||
List<Capability> capabilities = (List) getBuilderValue(builder, "capabilities"); |
||||
assertThat(capabilities).hasSize(2).hasAtLeastOneElementOfType(NoOpCapability.class) |
||||
.hasAtLeastOneElementOfType(MicrometerObservationCapability.class); |
||||
} |
||||
|
||||
private Object getBuilderValue(Feign.Builder builder, String member) { |
||||
Field builderField = ReflectionUtils.findField(Feign.Builder.class, member); |
||||
ReflectionUtils.makeAccessible(builderField); |
||||
|
||||
return ReflectionUtils.getField(builderField, builder); |
||||
} |
||||
|
||||
@Test |
||||
public void testNoInheritFeignClient() { |
||||
FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) applicationContext |
||||
.getBean(BEAN_NAME_PREFIX + "NoInheritFeignClient"); |
||||
Feign.Builder builder = factoryBean.feign(context); |
||||
|
||||
List<RequestInterceptor> interceptors = (List) getBuilderValue(builder, "requestInterceptors"); |
||||
assertThat(interceptors).as("interceptors not set").isEmpty(); |
||||
assertThat(factoryBean.isInheritParentContext()).as("is inheriting from parent configuration").isFalse(); |
||||
|
||||
List<Capability> capabilities = (List) getBuilderValue(builder, "capabilities"); |
||||
assertThat(capabilities).hasSize(2).hasAtLeastOneElementOfType(NoOpCapability.class) |
||||
.hasAtLeastOneElementOfType(MicrometerObservationCapability.class); |
||||
} |
||||
|
||||
@Test |
||||
public void testNoInheritFeignClient_ignoreProperties() { |
||||
FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) applicationContext |
||||
.getBean(BEAN_NAME_PREFIX + "NoInheritFeignClient"); |
||||
Feign.Builder builder = factoryBean.feign(context); |
||||
|
||||
assertThat(getBuilderValue(builder, "logLevel")).as("log level not set").isEqualTo(Logger.Level.HEADERS); |
||||
|
||||
List<Capability> capabilities = (List) getBuilderValue(builder, "capabilities"); |
||||
assertThat(capabilities).hasSize(2).hasAtLeastOneElementOfType(NoOpCapability.class) |
||||
.hasAtLeastOneElementOfType(MicrometerObservationCapability.class); |
||||
} |
||||
|
||||
@EnableAutoConfiguration |
||||
@Configuration(proxyBeanMethods = false) |
||||
@EnableFeignClients(clients = { TestFeignClient.class, NoInheritFeignClient.class }) |
||||
protected static class Application { |
||||
|
||||
@Bean |
||||
public RequestInterceptor requestInterceptor() { |
||||
return requestTemplate -> { |
||||
}; |
||||
} |
||||
|
||||
@Bean |
||||
public NoOpCapability noOpCapability() { |
||||
return new NoOpCapability(); |
||||
} |
||||
|
||||
} |
||||
|
||||
public static class NoInheritConfiguration { |
||||
|
||||
@Bean |
||||
public Logger.Level logLevel() { |
||||
return Logger.Level.HEADERS; |
||||
} |
||||
|
||||
@Bean |
||||
public NoOpCapability noOpCapability() { |
||||
return new NoOpCapability(); |
||||
} |
||||
|
||||
@Bean |
||||
public FeignClientConfigurer feignClientConfigurer() { |
||||
return new FeignClientConfigurer() { |
||||
|
||||
@Override |
||||
public boolean inheritParentConfiguration() { |
||||
return false; |
||||
} |
||||
}; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
@FeignClient("testFeignClient") |
||||
interface TestFeignClient { |
||||
|
||||
} |
||||
|
||||
@FeignClient(name = "noInheritFeignClient", configuration = NoInheritConfiguration.class) |
||||
interface NoInheritFeignClient { |
||||
|
||||
} |
||||
|
||||
private static class NoOpCapability implements Capability { |
||||
|
||||
} |
||||
|
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue