Filter properties with a particular prefix
This commit improves the configprops endpoint to allow filtering properties based on a particular prefix See gh-24718
This commit is contained in:
parent
0f9fb13141
commit
ad7c69a9cd
|
@ -6,15 +6,15 @@ The `configprops` endpoint provides information about the application's `@Config
|
|||
|
||||
|
||||
[[configprops-retrieving]]
|
||||
== Retrieving the @ConfigurationProperties Bean
|
||||
== Retrieving All @ConfigurationProperties Beans
|
||||
|
||||
To retrieve the `@ConfigurationProperties` beans, make a `GET` request to `/actuator/configprops`, as shown in the following curl-based example:
|
||||
To retrieve all of the `@ConfigurationProperties` beans, make a `GET` request to `/actuator/configprops`, as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}/configprops/curl-request.adoc[]
|
||||
include::{snippets}/configprops/all/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}/configprops/http-response.adoc[]
|
||||
include::{snippets}/configprops/all/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
|
@ -26,3 +26,26 @@ The following table describes the structure of the response:
|
|||
|
||||
[cols="2,1,3"]
|
||||
include::{snippets}/configprops/response-fields.adoc[]
|
||||
|
||||
|
||||
[[configprops-retrieving-by-prefix]]
|
||||
== Retrieving @ConfigurationProperties Beans By Prefix
|
||||
|
||||
To retrieve the `@ConfigurationProperties` beans mapped under a certain prefix, make a `GET` request to `/actuator/configprops/{prefix}`, as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}/configprops/prefixed/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}/configprops/prefixed/http-response.adoc[]
|
||||
|
||||
NOTE: The `{prefix}` does not need to be exact, a more general prefix will return all beans mapped under that prefix stem.
|
||||
|
||||
[[configprops-retrieving-by-prefix-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the application's `@ConfigurationProperties` beans.
|
||||
The following table describes the structure of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::{snippets}/configprops/prefixed/response-fields.adoc[]
|
|
@ -18,7 +18,9 @@ package org.springframework.boot.actuate.autoconfigure.context.properties;
|
|||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||
import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint;
|
||||
import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpointWebExtension;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -30,6 +32,7 @@ import org.springframework.context.annotation.Configuration;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Chris Bono
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
@ -49,4 +52,12 @@ public class ConfigurationPropertiesReportEndpointAutoConfiguration {
|
|||
return endpoint;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(ConfigurationPropertiesReportEndpoint.class)
|
||||
public ConfigurationPropertiesReportEndpointWebExtension configurationPropertiesReportEndpointWebExtension(
|
||||
ConfigurationPropertiesReportEndpoint configurationPropertiesReportEndpoint) {
|
||||
return new ConfigurationPropertiesReportEndpointWebExtension(configurationPropertiesReportEndpoint);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,13 +36,31 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||
* {@link ConfigurationPropertiesReportEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Chris Bono
|
||||
*/
|
||||
class ConfigurationPropertiesReportEndpointDocumentationTests extends MockMvcEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
void configProps() throws Exception {
|
||||
this.mockMvc.perform(get("/actuator/configprops")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("configprops",
|
||||
.andDo(MockMvcRestDocumentation.document("configprops/all",
|
||||
preprocessResponse(limit("contexts", getApplicationContext().getId(), "beans")),
|
||||
responseFields(fieldWithPath("contexts").description("Application contexts keyed by id."),
|
||||
fieldWithPath("contexts.*.beans.*")
|
||||
.description("`@ConfigurationProperties` beans keyed by bean name."),
|
||||
fieldWithPath("contexts.*.beans.*.prefix")
|
||||
.description("Prefix applied to the names of the bean's properties."),
|
||||
subsectionWithPath("contexts.*.beans.*.properties")
|
||||
.description("Properties of the bean as name-value pairs."),
|
||||
subsectionWithPath("contexts.*.beans.*.inputs").description(
|
||||
"Origin and value of the configuration property used when binding to this bean."),
|
||||
parentIdField())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void configPropsFilterByPrefix() throws Exception {
|
||||
this.mockMvc.perform(get("/actuator/configprops/spring.resources")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("configprops/prefixed",
|
||||
preprocessResponse(limit("contexts", getApplicationContext().getId(), "beans")),
|
||||
responseFields(fieldWithPath("contexts").description("Application contexts keyed by id."),
|
||||
fieldWithPath("contexts.*.beans.*")
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.HashMap;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
|
@ -55,6 +56,7 @@ import org.springframework.beans.BeansException;
|
|||
import org.springframework.boot.actuate.endpoint.Sanitizer;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
import org.springframework.boot.context.properties.BoundConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBean;
|
||||
|
@ -90,6 +92,7 @@ import org.springframework.util.StringUtils;
|
|||
* @author Stephane Nicoll
|
||||
* @author Madhura Bhave
|
||||
* @author Andy Wilkinson
|
||||
* @author Chris Bono
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Endpoint(id = "configprops")
|
||||
|
@ -114,15 +117,21 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
|
|||
|
||||
@ReadOperation
|
||||
public ApplicationConfigurationProperties configurationProperties() {
|
||||
return extract(this.context);
|
||||
return extract(this.context, (bean) -> true);
|
||||
}
|
||||
|
||||
private ApplicationConfigurationProperties extract(ApplicationContext context) {
|
||||
@ReadOperation
|
||||
public ApplicationConfigurationProperties configurationProperties(@Selector String prefix) {
|
||||
return extract(this.context, (bean) -> bean.getAnnotation().prefix().startsWith(prefix));
|
||||
}
|
||||
|
||||
private ApplicationConfigurationProperties extract(ApplicationContext context,
|
||||
Predicate<ConfigurationPropertiesBean> beanFilterPredicate) {
|
||||
ObjectMapper mapper = getObjectMapper();
|
||||
Map<String, ContextConfigurationProperties> contexts = new HashMap<>();
|
||||
ApplicationContext target = context;
|
||||
while (target != null) {
|
||||
contexts.put(target.getId(), describeBeans(mapper, target));
|
||||
contexts.put(target.getId(), describeBeans(mapper, target, beanFilterPredicate));
|
||||
target = target.getParent();
|
||||
}
|
||||
return new ApplicationConfigurationProperties(contexts);
|
||||
|
@ -169,10 +178,14 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
|
|||
mapper.setSerializerFactory(factory);
|
||||
}
|
||||
|
||||
private ContextConfigurationProperties describeBeans(ObjectMapper mapper, ApplicationContext context) {
|
||||
private ContextConfigurationProperties describeBeans(ObjectMapper mapper, ApplicationContext context,
|
||||
Predicate<ConfigurationPropertiesBean> beanFilterPredicate) {
|
||||
Map<String, ConfigurationPropertiesBean> beans = ConfigurationPropertiesBean.getAll(context);
|
||||
Map<String, ConfigurationPropertiesBeanDescriptor> descriptors = new HashMap<>();
|
||||
beans.forEach((beanName, bean) -> descriptors.put(beanName, describeBean(mapper, bean)));
|
||||
|
||||
Map<String, ConfigurationPropertiesBeanDescriptor> descriptors = beans.values().stream()
|
||||
.filter(beanFilterPredicate::test)
|
||||
.collect(Collectors.toMap((bean) -> bean.getName(), (bean) -> describeBean(mapper, bean)));
|
||||
|
||||
return new ContextConfigurationProperties(descriptors,
|
||||
(context.getParent() != null) ? context.getParent().getId() : null);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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.boot.actuate.context.properties;
|
||||
|
||||
import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ApplicationConfigurationProperties;
|
||||
import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ContextConfigurationProperties;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
|
||||
|
||||
/**
|
||||
* {@link EndpointWebExtension @EndpointWebExtension} for the
|
||||
* {@link ConfigurationPropertiesReportEndpoint}.
|
||||
*
|
||||
* @author Chris Bono
|
||||
* @since 2.4
|
||||
*/
|
||||
@EndpointWebExtension(endpoint = ConfigurationPropertiesReportEndpoint.class)
|
||||
public class ConfigurationPropertiesReportEndpointWebExtension {
|
||||
|
||||
private final ConfigurationPropertiesReportEndpoint delegate;
|
||||
|
||||
public ConfigurationPropertiesReportEndpointWebExtension(ConfigurationPropertiesReportEndpoint delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public WebEndpointResponse<ApplicationConfigurationProperties> configurationProperties(@Selector String prefix) {
|
||||
ApplicationConfigurationProperties configurationProperties = this.delegate.configurationProperties(prefix);
|
||||
boolean foundMatchingBeans = configurationProperties.getContexts().values().stream()
|
||||
.map(ContextConfigurationProperties::getBeans).anyMatch((beans) -> !beans.isEmpty());
|
||||
return (foundMatchingBeans) ? new WebEndpointResponse<>(configurationProperties, WebEndpointResponse.STATUS_OK)
|
||||
: new WebEndpointResponse<>(WebEndpointResponse.STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.boot.actuate.context.properties;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ApplicationConfigurationProperties;
|
||||
import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ContextConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConfigurationPropertiesReportEndpoint} when filtering by prefix.
|
||||
*
|
||||
* @author Chris Bono
|
||||
*/
|
||||
class ConfigurationPropertiesReportEndpointFilteringTests {
|
||||
|
||||
@Test
|
||||
void filterByPrefixSingleMatch() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner().withUserConfiguration(Config.class)
|
||||
.withPropertyValues("foo.primary.name:foo1", "foo.secondary.name:foo2", "only.bar.name:solo1");
|
||||
contextRunner.run((context) -> {
|
||||
ConfigurationPropertiesReportEndpoint endpoint = context
|
||||
.getBean(ConfigurationPropertiesReportEndpoint.class);
|
||||
ApplicationConfigurationProperties applicationProperties = endpoint.configurationProperties("only.bar");
|
||||
assertThat(applicationProperties.getContexts()).containsOnlyKeys(context.getId());
|
||||
ContextConfigurationProperties contextProperties = applicationProperties.getContexts().get(context.getId());
|
||||
assertThat(contextProperties.getBeans().values()).hasSize(1).first().hasFieldOrPropertyWithValue("prefix",
|
||||
"only.bar");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void filterByPrefixMultipleMatches() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner().withUserConfiguration(Config.class)
|
||||
.withPropertyValues("foo.primary.name:foo1", "foo.secondary.name:foo2", "only.bar.name:solo1");
|
||||
contextRunner.run((context) -> {
|
||||
ConfigurationPropertiesReportEndpoint endpoint = context
|
||||
.getBean(ConfigurationPropertiesReportEndpoint.class);
|
||||
ApplicationConfigurationProperties applicationProperties = endpoint.configurationProperties("foo.");
|
||||
assertThat(applicationProperties.getContexts()).containsOnlyKeys(context.getId());
|
||||
ContextConfigurationProperties contextProperties = applicationProperties.getContexts().get(context.getId());
|
||||
assertThat(contextProperties.getBeans()).containsOnlyKeys("primaryFoo", "secondaryFoo");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void filterByPrefixNoMatches() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner().withUserConfiguration(Config.class)
|
||||
.withPropertyValues("foo.primary.name:foo1", "foo.secondary.name:foo2", "only.bar.name:solo1");
|
||||
contextRunner.run((context) -> {
|
||||
ConfigurationPropertiesReportEndpoint endpoint = context
|
||||
.getBean(ConfigurationPropertiesReportEndpoint.class);
|
||||
ApplicationConfigurationProperties applicationProperties = endpoint.configurationProperties("foo.third");
|
||||
assertThat(applicationProperties.getContexts()).containsOnlyKeys(context.getId());
|
||||
ContextConfigurationProperties contextProperties = applicationProperties.getContexts().get(context.getId());
|
||||
assertThat(contextProperties.getBeans()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(Bar.class)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
ConfigurationPropertiesReportEndpoint endpoint() {
|
||||
return new ConfigurationPropertiesReportEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "foo.primary")
|
||||
Foo primaryFoo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "foo.secondary")
|
||||
Foo secondaryFoo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Foo {
|
||||
|
||||
private String name = "5150";
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "only.bar")
|
||||
public static class Bar {
|
||||
|
||||
private String name = "123456";
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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.boot.actuate.context.properties;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.web.test.WebEndpointTest;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ConfigurationPropertiesReportEndpoint} exposed by Jersey,
|
||||
* Spring MVC, and WebFlux.
|
||||
*
|
||||
* @author Chris Bono
|
||||
*/
|
||||
class ConfigurationPropertiesReportEndpointWebIntegrationTests {
|
||||
|
||||
private WebTestClient client;
|
||||
|
||||
@BeforeEach
|
||||
void prepareEnvironment(ConfigurableApplicationContext context, WebTestClient client) {
|
||||
TestPropertyValues.of("com.foo.name=fooz", "com.bar.name=barz").applyTo(context);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@WebEndpointTest
|
||||
void noFilters() {
|
||||
this.client.get().uri("/actuator/configprops").exchange().expectStatus().isOk().expectBody()
|
||||
.jsonPath("$..beans[*]").value(hasSize(greaterThanOrEqualTo(2))).jsonPath("$..beans['fooDotCom']")
|
||||
.exists().jsonPath("$..beans['barDotCom']").exists();
|
||||
}
|
||||
|
||||
@WebEndpointTest
|
||||
void filterByExactPrefix() {
|
||||
this.client.get().uri("/actuator/configprops/com.foo").exchange().expectStatus().isOk().expectBody()
|
||||
.jsonPath("$..beans[*]").value(hasSize(1)).jsonPath("$..beans['fooDotCom']").exists();
|
||||
}
|
||||
|
||||
@WebEndpointTest
|
||||
void filterByGeneralPrefix() {
|
||||
this.client.get().uri("/actuator/configprops/com.").exchange().expectStatus().isOk().expectBody()
|
||||
.jsonPath("$..beans[*]").value(hasSize(2)).jsonPath("$..beans['fooDotCom']").exists()
|
||||
.jsonPath("$..beans['barDotCom']").exists();
|
||||
}
|
||||
|
||||
@WebEndpointTest
|
||||
void filterByNonExistentPrefix() {
|
||||
this.client.get().uri("/actuator/configprops/com.zoo").exchange().expectStatus().isNotFound();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
ConfigurationPropertiesReportEndpoint endpoint() {
|
||||
return new ConfigurationPropertiesReportEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ConfigurationPropertiesReportEndpointWebExtension endpointWebExtension(
|
||||
ConfigurationPropertiesReportEndpoint endpoint) {
|
||||
return new ConfigurationPropertiesReportEndpointWebExtension(endpoint);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "com.foo")
|
||||
Foo fooDotCom() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "com.bar")
|
||||
Bar barDotCom() {
|
||||
return new Bar();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Foo {
|
||||
|
||||
private String name = "5150";
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Bar {
|
||||
|
||||
private String name = "6160";
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue