https://www.baeldung.com/spring-boot-actuators

1. Overview

In this article, we’re going to introduce Spring Boot Actuator. We’ll cover the basics first, then discuss in detail what’s available in Spring Boot 1.x vs 2.x.

We’ll learn how to use, configure and extend this monitoring tool in Spring Boot 1.x. Then, we’ll discuss how to do the same using Boot 2.x and WebFlux taking advantage of the reactive programming model.

Spring Boot Actuator is available since April 2014, together with the first Spring Boot release.

With the release of Spring Boot 2, Actuator has been redesigned, and new exciting endpoints were added.

This guide is split into 3 main sections:

  • What is an Actuator?
  • Spring Boot 1.x Actuator
  • Spring Boot 2.x Actuator

2. What is an Actuator?

In essence, Actuator brings production-ready features to our application.

Monitoring our app, gathering metrics, understanding traffic or the state of our database becomes trivial with this dependency.

The main benefit of this library is that we can get production grade tools without having to actually implement these features ourselves.

Actuator is mainly used to expose operational information about the running application – health, metrics, info, dump, env, etc. It uses HTTP endpoints or JMX beans to enable us to interact with it.

Once this dependency is on the classpath several endpoints are available for us out of the box. As with most Spring modules, we can easily configure or extend it in many ways.

2.1. Getting started

To enable Spring Boot Actuator we’ll just need to add the spring-boot-actuator dependency to our package manager. In Maven:

1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Note that this remains valid regardless of the Boot version, as versions are specified in Spring Boot Bill of Materials (BOM).

3. Spring Boot 1.x Actuator

In 1.x Actuator follows a R/W model, that means we can either read from it or write to it. E.g. we can retrieve metrics or the health of our application. Alternatively, we could gracefully terminate our app or change our logging configuration.

In order to get it working, Actuator requires Spring MVC to expose its endpoints through HTTP. No other technology is supported.

3.1. Endpoints

In 1.x, Actuator brings its own security model. It takes advantage of Spring Security constructs, but needs to be configured independently from the rest of the application.

Also, most endpoints are sensitive – meaning they’re not fully public, or in other words, most information will be omitted – while a handful is not e.g. /info.

Here are some of the most common endpoints Boot provides out of the box:

  • /health – Shows application health information (a simple ‘status’ when accessed over an unauthenticated connection or full message details when authenticated); it’s not sensitive by default
  • /info – Displays arbitrary application info; not sensitive by default
  • /metrics – Shows ‘metrics’ information for the current application; it’s also sensitive by default
  • /trace – Displays trace information (by default the last few HTTP requests)

We can find the full list of existing endpoints over on the official docs.

3.2. Configuring Existing Endpoints

Each endpoint can be customized with properties using the following format: endpoints.[endpoint name].[property to customize]

Three properties are available:

  • id – by which this endpoint will be accessed over HTTP
  • enabled – if true then it can be accessed otherwise not
  • sensitive – if true then need the authorization to show crucial information over HTTP

For example, add the following properties will customize the /beans endpoint:

1
2
3
endpoints.beans.id=springbeans
endpoints.beans.sensitive=false
endpoints.beans.enabled=true

3.3. /health Endpoint

The /health endpoint is used to check the health or state of the running application. It’s usually exercised by monitoring software to alert us if the running instance goes down or gets unhealthy for other reasons. E.g. Connectivity issues with our DB, lack of disk space…

By default only health information is shown to unauthorized access over HTTP:

1
2
3
{
    "status" : "UP"
}

This health information is collected from all the beans implementing the HealthIndicator interface configured in our application context.

Some information returned by HealthIndicator is sensitive in nature – but we can configure endpoints.health.sensitive=false to expose more detailed information like disk space, messaging broker connectivity, custom checks etc.

We could also implement our own custom health indicator – which can collect any type of custom health data specific to the application and automatically expose it through the /health endpoint:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class HealthCheck implements HealthIndicator {
  
    @Override
    public Health health() {
        int errorCode = check(); // perform some specific health check
        if (errorCode != 0) {
            return Health.down()
              .withDetail("Error Code", errorCode).build();
        }
        return Health.up().build();
    }
     
    public int check() {
        // Our logic to check health
        return 0;
    }
}

Here’s how the output would look like:

1
2
3
4
5
6
7
8
9
10
11
12
{
    "status" : "DOWN",
    "myHealthCheck" : {
        "status" : "DOWN",
        "Error Code" : 1
     },
     "diskSpace" : {
         "status" : "UP",
         "free" : 209047318528,
         "threshold" : 10485760
     }
}

3.4. /info Endpoint

We can also customize the data shown by the /info endpoint – for example:

1
2
3
info.app.name=Spring Sample Application
info.app.description=This is my first spring boot application
info.app.version=1.0.0

And the sample output:

1
2
3
4
5
6
7
{
    "app" : {
        "version" : "1.0.0",
        "description" : "This is my first spring boot application",
        "name" : "Spring Sample Application"
    }
}

3.5. /metrics Endpoint

The metrics endpoint publishes information about OS, JVM as well as application level metrics. Once enabled, we get information such as memory, heap, processors, threads, classes loaded, classes unloaded, thread pools along with some HTTP metrics as well.

Here’s what the output of this endpoint looks like out of the box:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
    "mem" : 193024,
    "mem.free" : 87693,
    "processors" : 4,
    "instance.uptime" : 305027,
    "uptime" : 307077,
    "systemload.average" : 0.11,
    "heap.committed" : 193024,
    "heap.init" : 124928,
    "heap.used" : 105330,
    "heap" : 1764352,
    "threads.peak" : 22,
    "threads.daemon" : 19,
    "threads" : 22,
    "classes" : 5819,
    "classes.loaded" : 5819,
    "classes.unloaded" : 0,
    "gc.ps_scavenge.count" : 7,
    "gc.ps_scavenge.time" : 54,
    "gc.ps_marksweep.count" : 1,
    "gc.ps_marksweep.time" : 44,
    "httpsessions.max" : -1,
    "httpsessions.active" : 0,
    "counter.status.200.root" : 1,
    "gauge.response.root" : 37.0
}

In order to gather custom metrics, we have support for ‘gauges’, that is, single value snapshots of data, and ‘counters’ i.e. incrementing/decrementing metrics.

Let’s implement our own custom metrics into the /metrics endpoint. For example, we’ll customize the login flow to record a successful and failed login attempt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Service
public class LoginServiceImpl {
    private final CounterService counterService;
     
    public LoginServiceImpl(CounterService counterService) {
        this.counterService = counterService;
    }
     
    public boolean login(String userName, char[] password) {
        boolean success;
        if (userName.equals("admin") && "secret".toCharArray().equals(password)) {
            counterService.increment("counter.login.success");
            success = true;
        }
        else {
            counterService.increment("counter.login.failure");
            success = false;
        }
        return success;
    }
}

Here’s what the output might look like:

1
2
3
4
5
6
{
    ...
    "counter.login.success" : 105,
    "counter.login.failure" : 12,
    ...
}

Note that login attempts and other security related events are available out of the box in Actuator as audit events.

3.6. Creating A New Endpoint

Besides using the existing endpoints provided by Spring Boot, we could also create an entirely new one.

Firstly, we’d need to have the new endpoint implement the Endpoint<T> interface:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Component
public class CustomEndpoint implements Endpoint<List<String>> {
     
    @Override
    public String getId() {
        return "customEndpoint";
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
    @Override
    public boolean isSensitive() {
        return true;
    }
    @Override
    public List<String> invoke() {
        // Custom logic to build the output
        List<String> messages = new ArrayList<String>();
        messages.add("This is message 1");
        messages.add("This is message 2");
        return messages;
    }
}

In order to access this new endpoint, its id is used to map it, i.e. we could exercise it hitting /customEndpoint.

Output:

1
[ "This is message 1", "This is message 2" ]

3.7. Further Customization

For security purposes, we might choose to expose the actuator endpoints over a non-standard port – the management.port property can easily be used to configure that.

Also, as we already mentioned, in 1.x. Actuator configures its own security model, based on Spring Security but independent from the rest of the application.
Hence, we can change the management.address property to restrict where the endpoints can be accessed from over the network:

1
2
3
4
5
6
7
8
#port used to expose actuator
management.port=8081
#CIDR allowed to hit actuator
management.address=127.0.0.1
#Whether security should be enabled or disabled altogether
management.security.enabled=false

Besides, all the built-in endpoints except /info are sensitive by default. If the application is using Spring Security – we can secure these endpoints by defining the default security properties – username, password, and role – in the application.properties file:

1
2
3
security.user.name=admin
security.user.password=secret
management.security.role=SUPERUSER

4. Spring Boot 2.x Actuator

In 2.x Actuator keeps its fundamental intent, but simplifies its model, extends its capabilities and incorporate better defaults.

Firstly, this version becomes technology agnostic. Also, it simplifies its security model by merging it with the application one.

Lastly, among the various changes, it’s important to keep in mind that some of them are breaking. This includes HTTP request/responses as well as Java APIs.

Furthermore, the latest version supports now the CRUD model, as opposed to the old RW (read/write) model.

4.1. Technology Support

With its second major version, Actuator is now technology-agnostic whereas in 1.x it was tied to MVC, therefore to the Servlet API.

In 2.x Actuator defines its model, pluggable and extensible without relying on MVC for this.

Hence, with this new model, we’re able to take advantage of MVC as well as WebFlux as an underlying web technology.

Moreover, forthcoming technologies could be added by implementing the right adapters.

Lastly, JMX remains supported to expose endpoints without any additional code.

4.2. Important Changes

Unlike in previous versions, Actuator comes with most endpoints disabled.

Thus, the only two available by default are /health and /info.

Would we want to enable all of them, we could set management.endpoints.web.exposure.include=*. Alternatively, we could list endpoints which should be enabled.

Actuator now shares the security config with the regular App security rules. Hence, the security model is dramatically simplified.

Therefore, to tweak Actuator security rules, we could just add an entry for /actuator/**:

1
2
3
4
5
6
7
8
@Bean
public SecurityWebFilterChain securityWebFilterChain(
  ServerHttpSecurity http) {
    return http.authorizeExchange()
      .pathMatchers("/actuator/**").permitAll()
      .anyExchange().authenticated()
      .and().build();
}

We can find further details on the brand new Actuator official docs.

Also, by default, all Actuator endpoints are now placed under the /actuator path.

Same as in the previous version, we can tweak this path, using the new property management.endpoints.web.base-path.

4.3. Predefined Endpoints

Let’s have a look at some available endpoints, most of them were available in 1.x already.

Nonetheless, some endpoints have been added, some removed and some have been restructured:

  • /auditevents – lists security audit-related events such as user login/logout. Also, we can filter by principal or type among others fields
  • /beans – returns all available beans in our BeanFactory. Unlike /auditevents, it doesn’t support filtering
  • /conditions – formerly known as /autoconfig, builds a report of conditions around auto-configuration
  • /configprops – allows us to fetch all @ConfigurationProperties beans
  • /env – returns the current environment properties. Additionally, we can retrieve single properties
  • /flyway – provides details about our Flyway database migrations
  • /health – summarises the health status of our application
  • /heapdump – builds and returns a heap dump from the JVM used by our application
  • /info – returns general information. It might be custom data, build information or details about the latest commit
  • /liquibase – behaves like /flyway but for Liquibase
  • /logfile – returns ordinary application logs
  • /loggers – enables us to query and modify the logging level of our application
  • /metrics – details metrics of our application. This might include generic metrics as well as custom ones
  • /prometheus – returns metrics like the previous one, but formatted to work with a Prometheus server
  • /scheduledtasks – provides details about every scheduled task within our application
  • /sessions – lists HTTP sessions given we are using Spring Session
  • /shutdown – performs a graceful shutdown of the application
  • /threaddump – dumps the thread information of the underlying JVM

4.4. Health Indicators

Just like in the previous version, we can add custom indicators easily. Opposite to other APIs, the abstractions for creating custom health endpoints remain unchanged. However, a new interface ReactiveHealthIndicator has been added to implement reactive health checks.

Let’s have a look at a simple custom reactive health check:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class DownstreamServiceHealthIndicator implements ReactiveHealthIndicator {
    @Override
    public Mono<Health> health() {
        return checkDownstreamServiceHealth().onErrorResume(
          ex -> Mono.just(new Health.Builder().down(ex).build())
        );
    }
    private Mono<Health> checkDownstreamServiceHealth() {
        // we could use WebClient to check health reactively
        return Mono.just(new Health.Builder().up().build());
    }
}

A handy feature of health indicators is that we can aggregate them as part of a hierarchy. Hence, following the previous example, we could group all downstream services under a downstream-services category. This category would be healthy as long as every nested service was reachable.

Composite health checks are present in 1.x through CompositeHealthIndicator. Also, in 2.x we could use CompositeReactiveHealthIndicator for its reactive counterpart.

Unlike in Spring Boot 1.x, the endpoints.<id>.sensitive flag has been removed. To hide the complete health report, we can take advantage of the new management.endpoint.health.show-details. This flag is false by default.

4.5. Metrics in Spring Boot 2

In Spring Boot 2.0, the in-house metrics were replaced with Micrometer support. Thus, we can expect breaking changes. If our application was using metric services such as GaugeService or CounterService they will no longer be available.

Instead, we’re expected to interact with Micrometer directly. In Spring Boot 2.0, we’ll get a bean of type MeterRegistry autoconfigured for us.

Furthermore, Micrometer is now part of Actuator’s dependencies. Hence, we should be good to go as long as the Actuator dependency is in the classpath.

Moreover, we’ll get a completely new response from the /metrics endpoint:

1
2
3
4
5
6
7
8
9
{
  "names": [
    "jvm.gc.pause",
    "jvm.buffer.memory.used",
    "jvm.memory.used",
    "jvm.buffer.count",
    // ...
  ]
}

As we can observe in the previous example, there are no actual metrics as we got in 1.x.

To get the actual value of a specific metric, we can now navigate to the desired metric, i.e., /actuator/metrics/jvm.gc.pause and get a detailed response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
  "name": "jvm.gc.pause",
  "measurements": [
    {
      "statistic": "Count",
      "value": 3.0
    },
    {
      "statistic": "TotalTime",
      "value": 7.9E7
    },
    {
      "statistic": "Max",
      "value": 7.9E7
    }
  ],
  "availableTags": [
    {
      "tag": "cause",
      "values": [
        "Metadata GC Threshold",
        "Allocation Failure"
      ]
    },
    {
      "tag": "action",
      "values": [
        "end of minor GC",
        "end of major GC"
      ]
    }
  ]
}

As we can see, metrics now are much more thorough. Including not only different values but also some associated meta-data.

4.6. Customizing the /info Endpoint

The /info endpoint remains unchanged. As before, we can add git details using the Maven or Gradle respective dependency:

1
2
3
4
<dependency>
    <groupId>pl.project13.maven</groupId>
    <artifactId>git-commit-id-plugin</artifactId>
</dependency>

Likewise, we could also include build information including name, group, and version using the Maven or Gradle plugin:

1
2
3
4
5
6
7
8
9
10
11
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>build-info</goal>
            </goals>
        </execution>
    </executions>
</plugin>

4.7. Creating a Custom Endpoint

As we pointed out previously, we can create custom endpoints. However, Spring Boot 2 has redesigned the way to achieve this to support the new technology-agnostic paradigm.

Let’s create an Actuator endpoint to query, enable and disable feature flags in our application:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Component
@Endpoint(id = "features")
public class FeaturesEndpoint {
    private Map<String, Feature> features = new ConcurrentHashMap<>();
    @ReadOperation
    public Map<String, Feature> features() {
        return features;
    }
    @ReadOperation
    public Feature feature(@Selector String name) {
        return features.get(name);
    }
    @WriteOperation
    public void configureFeature(@Selector String name, Feature feature) {
        features.put(name, feature);
    }
    @DeleteOperation
    public void deleteFeature(@Selector String name) {
        features.remove(name);
    }
    public static class Feature {
        private Boolean enabled;
        // [...] getters and setters
    }
}

To get the endpoint, we need a bean. In our example, we’re using @Component for this. Also, we need to decorate this bean with @Endpoint.

The path of our endpoint is determined by the id parameter of @Endpoint, in our case, it’ll route requests to /actuator/features.

Once ready, we can start defining operations using:

  • @ReadOperation – it’ll map to HTTP GET
  • @WriteOperation – it’ll map to HTTP POST
  • @DeleteOperation – it’ll map to HTTP DELETE

When we run the application with the previous endpoint in our application, Spring Boot will register it.

A quick way to verify this would be checking the logs:

1
2
3
4
5
6
7
8
9
10
11
[...].WebFluxEndpointHandlerMapping: Mapped "{[/actuator/features/{name}],
  methods=[GET],
  produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}"
[...].WebFluxEndpointHandlerMapping : Mapped "{[/actuator/features],
  methods=[GET],
  produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}"
[...].WebFluxEndpointHandlerMapping : Mapped "{[/actuator/features/{name}],
  methods=[POST],
  consumes=[application/vnd.spring-boot.actuator.v2+json || application/json]}"
[...].WebFluxEndpointHandlerMapping : Mapped "{[/actuator/features/{name}],
  methods=[DELETE]}"[...]

In the previous logs, we can see how WebFlux is exposing our new endpoint. Would we switch to MVC, It’ll simply delegate on that technology without having to change any code.

Also, we have a few important considerations to keep in mind with this new approach:

  • There are no dependencies with MVC
  • All the metadata present as methods before (sensitive, enabled…) no longer exists. We can, however, enable or disable the endpoint using @Endpoint(id = “features”, enableByDefault = false)
  • Unlike in 1.x, there is no need to extend a given interface anymore
  • In contrast with the old Read/Write model, now we can define DELETE operations using @DeleteOperation

4.8. Extending Existing Endpoints

Let’s imagine we want to make sure the production instance of our application is never a SNAPSHOT version. We decided to do this by changing the HTTP status code of the Actuator endpoint that returns this information, i.e., /info.If our app happened to be a SNAPSHOT. We would get a different HTTP status code.

We can easily extend the behavior of a predefined endpoint using the @EndpointExtension annotations, or its more concrete specializations @EndpointWebExtension or @EndpointJmxExtension:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component
@EndpointWebExtension(endpoint = InfoEndpoint.class)
public class InfoWebEndpointExtension {
    private InfoEndpoint delegate;
    // standard constructor
    @ReadOperation
    public WebEndpointResponse<Map> info() {
        Map<String, Object> info = this.delegate.info();
        Integer status = getStatus(info);
        return new WebEndpointResponse<>(info, status);
    }
    private Integer getStatus(Map<String, Object> info) {
        // return 5xx if this is a snapshot
        return 200;
    }
}

4.9. Enable All Endpoints

In order to access the actuator endpoints using HTTP, we need to both enable and expose them. By default, all endpoints but /shutdown are enabled.  Only the /health and /info endpoints are exposed by default.

We need to add the following configuration to expose all endpoints :

1
management.endpoints.web.exposure.include=*

To explicitly enable a specific endpoint (for example /shutdown), we use:

1
management.endpoint.shutdown.enabled=true

To expose all enabled endpoints except one (for example /loggers), we use:

1
2
management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=loggers

5. Summary

In this article, we talked about Spring Boot Actuator. We started defining what Actuator means and what it does for us.

Next, we focused on Actuator for the current Spring Boot version, 1.x. discussing how to use it, tweak it an extend it.

Then, we discussed Actuator in Spring Boot 2. We focused on what’s new, and we took advantage of WebFlux to expose our endpoint.

Also, we talked about the important security changes that we can find in this new iteration. We discussed some popular endpoints and how they have changed as well.

Lastly, we demonstrated how to customize and extend Actuator.

转载于:https://www.cnblogs.com/davidwang456/articles/10268315.html

Spring Boot Actuator相关推荐

  1. 警惕 Spring Boot Actuator 引发的安全问题

    前言 一年一度的 HW 行动开始了,最近也是被各种安全漏洞搞的特别闹心,一周能收到几十封安全团队扫描出来的漏洞邮件,这其中有一类漏洞很容易被人忽视,但影响面却极广,危害也极大,我说出它的名字你应该也不 ...

  2. 朱晔和你聊Spring系列S1E7:简单好用的Spring Boot Actuator

    本文会来看一下Spring Boot Actuator提供给我们的监控端点Endpoint.健康检查Health和打点指标Metrics等所谓的Production-ready(生产环境必要的一些)功 ...

  3. actuator的原理_使用Spring Boot Actuator监视Java应用程序

    actuator的原理 朋友不允许朋友写用户身份验证. 厌倦了管理自己的用户? 立即尝试Okta的API和Java SDK. 数分钟之内即可在任何应用程序中对用户进行身份验证,管理和保护. 您是否曾与 ...

  4. 如何将 Spring Boot Actuator 的指标信息输出到 InfluxDB 和 Prometheus

    来源:SpringForAll社区 Spring Boot Actuator是Spring Boot 2发布后修改最多的项目之一.它经过了主要的改进,旨在简化定制,并包括一些新功能,如支持其他Web技 ...

  5. spring boot actuator工作原理之http服务暴露源码分析

    spring boot actuator的官方文档地址:https://docs.spring.io/spring-boot/docs/current/reference/html/productio ...

  6. 使用Spring Boot Actuator、Jolokia和Grafana实现准实时监控--转

    原文地址:http://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&mid=2247483789&idx=1&sn=ae11f04780 ...

  7. spring Boot Actuator使用

    spring Boot Actuator使用 Spring Boot 的 Actuator 提供了很多生产级的特性,比如监控和度量Spring Boot 应用程序. 官网参考文档Spring Boot ...

  8. springboot有什么好的方案实现 数据实时更新吗?_使用Spring Boot Actuator、Jolokia和Grafana实现准实时监控...

    Spring Boot Actuator通过/metrics端点,以开箱即用的方式为应用程序的性能指标与响应统计提供了一个非常友好的监控方式. 由于在集群化的弹性环境中,应用程序的节点可以增长.扩展, ...

  9. spring boot actuator 入门荔枝

    1.创建maven 项目, 通过 http://start.spring.io/ 构建项目结构 2. 3个核心文件内容 ReadinglistApplicatoin.java  package com ...

最新文章

  1. Oracle数据库入门——初级系列教程
  2. django05:ORM示例--person 增删改查
  3. java 调试 工具_Java调试器–权威的工具列表
  4. 【转】CT中的“层“与“排“的区别
  5. Docker教程(三) Docker网络访问和数据管理
  6. 连接MySQL错误:Can't connect to MySQL server (10060)
  7. Spark源码分析之四:Stage提交
  8. 区块链隐私保护:技术和相关项目
  9. 金蝶K3系统物料主数据后台表对应字段一览
  10. 使用大白鲨进行木马攻击实验并实现爆破目标机账号密码以远程连接
  11. 1500个工作计划总结PPT模板免费下载网址
  12. 各种实用航测遥感数据数据免费获取,速来领取!
  13. 无线资源管理 IEEE802.11K
  14. debussy和modelsim联合仿真配置
  15. android 魔力锁屏源码,打造最炫手机锁屏桌面 10款安卓魔力锁屏主题推荐
  16. ROS学习第二讲(1)
  17. Python +SQL Server 框架及返回结果处理
  18. smalltalk 上手
  19. 黑马程序员rocketmq第一章
  20. Java码农进阶之路~流程控制-循环结构whileforbreak与continue

热门文章

  1. java条件配置,三、使用JAVA必备条件—环境配置
  2. hocon配置文件_GitHub - Himmelt/Hocon: Simple Hocon Configuration Library [轻量 Hocon 配置库]...
  3. 一键清除bios密码_电脑忘记开机密码?怎么才能开机使用
  4. 客户端界面实现及登录功能实现
  5. arduino向串口发送数据时掉包_[技术]清楚简单,一种串口触摸屏的开发流程和方法介绍...
  6. javascript onsubmit返回false仍然能提交_JavaScript对象-Get和Put
  7. Win10 插入耳机无声问题 解决办法
  8. 二分法细节学习-mid
  9. Pytorch学习-torch.max()和min()深度解析
  10. linux 公钥登录