Spring boot 使用@Endpoint注解自定义端点, 不能通过 Restfult 访问问题 原因分析
Posted xlxxcc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 {
@ReadOperation
public 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 them
try {
tmp = getParameters0();
} catch(IllegalArgumentException e) {
// Rethrow ClassFormatErrors
throw new MalformedParametersException("Invalid constant pool index");
}
// If we get back nothing, then synthesize parameters
if (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,POST
4、 Actuator端点注解
Web 端点
@Endpoint
、@WebEndpoint
或 @EndpointWebExtension
上的操作将使用 Jersey、Spring MVC 或 Spring WebFlux 通过 HTTP 自动暴露。
通过使用 @Selector
注解操作方法的一个或多个参数,可以进一步自定义路径.
HTTP 方法由操作类型决定,如下表所示:
操作 | 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 访问问题 原因分析的主要内容,如果未能解决你的问题,请参考以下文章
Spring boot 使用@Endpoint注解自定义端点, 不能通过 Restfult 访问问题 原因分析
Spring boot 使用@Endpoint注解自定义端点, 不能通过 Restfult 访问问题 原因分析
Spring boot 使用@Endpoint注解自定义端点, 不能通过 Restfult 访问问题处理方法
Spring boot 使用@Endpoint注解自定义端点, 不能通过 Restfult 访问问题处理方法