javascript
Spring boot 使用@Endpoint注解自定义端点, 不能通过 Restfult 访问问题 原因分析
1、使用@Endpoint注解自定義端點
參考 spring-boot-starter-actuator.jar 包健康檢查端點源碼 org.springframework.boot.actuate.health.HealthEndpoint 實現
引入依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId> </dependency>自定義端點代碼
注意: @EndPoint中的id不能使用駝峰法,需要以-分割。
默認的基礎路徑是/actuator,如果一個端點配置的?id?是my-endpoint,那么它的全路徑就是/actuator/my-endpoint?
@Selector?的含義是讓這個訪問路徑變成restful風格:?/actuator/my-endpoint/{name}
@Component @Endpoint(id = "my-endpoint") public class MyEndpoint {@ReadOperationpublic String get(@Selector String name) {return name;} }配置
// management.endpoints.web.exposure.include=my-endpoint management.endpoints.web.exposure.include=*查詢端點列表:
通過端點的基礎路徑查詢端點列表:? http://localhost:9080/actuator
health 組件健康信息訪問路徑:??http://localhost:9080/actuator/health/{component}
我們可以通過?http://localhost:9080/actuator/health/consul?查看 consul的健康信息
而對應的自定義端點訪問路徑:??http://localhost:9080/actuator/my-endpoint/{arg0}
訪問?http://localhost:9080/actuator/my-endpoint/abc?卻報 404其可以正常訪問路徑變成:? http://localhost:9080/actuator/my-endpoint/{arg0}?name=abc
為什么自定義端點restfult風格訪問失效了呢?
經過斷點發現 WebEndpointDiscoverer.createOperation() 在綁定訪問路徑時, 自定義端點 通過 DiscoveredOperationMethod 獲取到的方法參數名變為 arg0.
而 DiscoveredOperationMethod 是通過 jdk 的 method.getParameters() 獲取參數, 其代碼如下:
private native Parameter[] getParameters0();public Parameter[] getParameters() {// TODO: This may eventually need to be guarded by security// mechanisms similar to those in Field, Method, etc.//// Need to copy the cached array to prevent users from messing// with it. Since parameters are immutable, we can// shallow-copy.return privateGetParameters().clone(); }private Parameter[] synthesizeAllParams() {final int realparams = getParameterCount();final Parameter[] out = new Parameter[realparams];for (int i = 0; i < realparams; i++)// TODO: is there a way to synthetically derive the// modifiers? Probably not in the general case, since// we'd have no way of knowing about them, but there// may be specific cases.out[i] = new Parameter("arg" + i, 0, this, i);return out; }private Parameter[] privateGetParameters() {// Use tmp to avoid multiple writes to a volatile.Parameter[] tmp = parameters;if (tmp == null) {// Otherwise, go to the JVM to get themtry {tmp = getParameters0();} catch(IllegalArgumentException e) {// Rethrow ClassFormatErrorsthrow new MalformedParametersException("Invalid constant pool index");}// If we get back nothing, then synthesize parametersif (tmp == null) {hasRealParameterData = false;tmp = synthesizeAllParams();} else {hasRealParameterData = true;verifyParameters(tmp);}parameters = tmp;}return tmp; }
自定義端點最終走到 native 方法 getParameters0() 獲取不到方法參數信息, 交由 synthesizeAllParams() 方法得到 arg0 參數.
而actuator自帶端點 走到 native 方法 getParameters0() 可以獲取靜態的方法的參數信息, 顯然與代碼編譯有關.
于是搜索 method.getParameters() 得到一些解釋:
在Java8之前,代碼編譯為class文件后,方法參數的類型是固定的,但參數名稱卻丟失了,這和動態語言嚴重依賴參數名稱形成了鮮明對比。(java是靜態語言,所以入參名稱叫什么其實無所謂的)
java1.8以后,官方提供了反射的方法能獲取到接口的參數名稱。并且需要在javac編譯時,加上-parameters參數才行。
后查看spring boot的源碼發現, 都需要加上 -parameters 編譯
./spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc:TIP: If you are using `@SpyBean` to spy on a bean with `@Cacheable` methods that refer to parameters by name, your application must be compiled with `-parameters`../spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc:NOTE: To let the input be mapped to the operation method's parameters, Java code that implements an endpoint should be compiled with `-parameters`, and Kotlin code that implements an endpoint should be compiled with `-java-parameters`../spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/reacting.adoc:10. Configures any `JavaCompile` tasks to use the `-parameters` compiler argument. ./spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/reacting.adoc:2. Configures any `KotlinCompile` tasks to use the `-java-parameters` compiler argument. ./spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc:The `application` closure uses Ant-style patch matching for include/exclude parameters. ./spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java: private static final String PARAMETERS_COMPILER_ARG = "-parameters"; ./spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/using.adoc:* Compilation with `-parameters`../buildSrc/src/main/java/org/springframework/boot/build/JavaConventions.java: * <li>{@link JavaCompile} tasks are configured to use {@code -parameters}. ./buildSrc/src/main/java/org/springframework/boot/build/JavaConventions.java: if (!args.contains("-parameters")) { ./buildSrc/src/main/java/org/springframework/boot/build/JavaConventions.java: args.add("-parameters");解決處理方法:
添加編譯參數 -parameters
1) 在IDEA中,
File->Settings->Java Compiler 的 Addintional command line parameters 的下面加上-parameters參數即可
2) 、在Maven中添加
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.3</version><configuration><source>1.8</source><target>1.8</target><compilerArgs><arg>-parameters</arg></compilerArgs></configuration> </plugin>3) 、 在eclipse中
Preferences->java->Compiler下勾選Store information about method parameters選項。
2、如何在運行期獲取method中的參數名
見: https://www.jianshu.com/p/a7f4336f445c
3、 Actuator端點配置
PATH, 訪問路徑:
默認的基礎路徑是/actuator,如果一個端點配置的 id?是sessions,那么它的全路徑就是/actuator/sessions
自定義管理端點路徑
management.endpoints.web.base-path = /manage此配置會將/actuator/sessions/{name}轉換成/manage/sessions/{name}
自定義管理服務器地址
默認端口和應用的端口是一致的
management.server.port = 8081 management.server.address = 127.0.0.1激活端點
//激活所有的端點的web方式請求 management.endpoints.web.exposure.include=* //關閉端點web方式 management.endpoints.web.exposure.exclude=env,beans //激活所有的JMX方式請求 management.endpoints.jmx.exposure.include=* //健康信息展示詳細信息, 值never:永遠不會顯示細節,always:顯示詳細信息,when-authorized:詳細信息僅向授權用戶顯示 management.endpoint.health.show-details=always //健康信息展示詳細信,配置授權角色 management.endpoint.health.roles跨域方式請求
//允許跨域的網址 management.endpoints.web.cors.allowed-origins=http://example.com //允許跨域的方法 management.endpoints.web.cors.allowed-methods=GET,POST4、 Actuator端點注解
Web 端點
@Endpoint、@WebEndpoint?或?@EndpointWebExtension?上的操作將使用 Jersey、Spring MVC 或 Spring WebFlux 通過 HTTP 自動暴露。
通過使用?@Selector?注解操作方法的一個或多個參數,可以進一步自定義路徑.
HTTP 方法由操作類型決定,如下表所示:
| @ReadOperation | GET |
| @WriteOperation | POST |
| @DeleteOperation | DELETE |
@ReadOperation?返回一個值,響應狀態為 200(OK)。如果它未返回值,則響應狀態將為 404(未找到)。
如果?@WriteOperation?或?@DeleteOperation?返回值,則響應狀態將為 200(OK)。如果它沒有返回值,則響應狀態將為 204(無內容)。
Servlet 端點
通過實現一個帶有 @ServletEndpoint 注解的類,Servlet 可以作為端點暴露,該類也實現了 Supplier。Servlet 端點提供了與 Servlet 容器更深層次的集成,但代價是可移植性。它們旨在用于將現有 Servlet 作為端點暴露。對于新端點,應盡可能首選 @Endpoint 和 @WebEndpoint 注解。
控制器端點
@ControllerEndpoint 和 @RestControllerEndpoint 可用于實現僅由 Spring MVC 或 Spring WebFlux 暴露的端點。使用 Spring MVC 和 Spring WebFlux 的標準注解(如 @RequestMapping 和 @GetMapping)映射方法,并將端點的 ID 用作路徑的前綴。控制器端點提供了與 Spring 的 Web 框架更深層次的集成,但代價是可移植性。應盡可能首選 @Endpoint 和 @WebEndpoint 注解。
5、@Endpoint 注解生效原理解析
https://blog.csdn.net/kangsa998/article/details/103166953/
總結
以上是生活随笔為你收集整理的Spring boot 使用@Endpoint注解自定义端点, 不能通过 Restfult 访问问题 原因分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 由于计算机的出现英语,电脑开机出现蓝屏怎
- 下一篇: mysql mysqldump导出数据