Spring #RefreshScope is not working with #Component - spring-boot

I've the component class and pom.xml dependencies like below. The properties are never set and staying as null.
#Component
#RefreshScope
public class SecurityProperties1 {
#Value("${ad.url}")
public String adUrl;
#Value("${ad.manager.dn}")
public String managerDN;
#Value("${ad.manager.password}")
public String managerPassword;
#Value("${ad.search.base}")
public String searchBase;
#Value("${ad.user.filter}")
public String userFilter;
}
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>1.2.1.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-commons -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>1.1.4.RELEASE</version>
</dependency>
Also,
My Property source is like below
#Component
public class CustomPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
#Override
protected String resolvePlaceholder(String placeholder, Properties props) {
return DynamicProperty.getProperty(placeholder);
}
#Override
protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) {
return DynamicProperty.getProperty(placeholder);
}
}

I had same problem. My solution: I added proxymode = default annotation
#Component
#RefreshScope(proxyMode = DEFAULT)
public class MyClass {
#Value("${test.value}")
private String testValue;
}

For newer version if somebody is facing this issue :
Make sure you have spring-cloud-starter-bootstrap dependency in classpath and also add spring.application.name property in your bootstrap.properties file
And annotated each class that is getting property from config server with #RefreshScope

Related

Generate YAML format response in springboot

I want to generate YAML format type response using Spring boot. can you please help me here to get it out?
Ensure you have the following dependency on the classpath:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>${jackson.version}</version>
</dependency>
Then define your own HttpMessageConverter:
class MappingJackson2YamlHttpMessageConverter extends AbstractJackson2HttpMessageConverter {
MappingJackson2YamlHttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, MediaType.parseMediaType("application/x-yaml"));
}
}
Expose it as a Spring #Bean:
#Configuration
public class JacksonYamlConfig {
#Bean
public MappingJackson2YamlHttpMessageConverter yamlHttpMessageConverter() {
YAMLMapper mapper = new YAMLMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
return new MappingJackson2YamlHttpMessageConverter(mapper);
}
}
And finally configure your controller method to produce YAML:
#GetMapping(produces = "application/x-yaml")
public ResponseEntity<Foo> getFoo() {
...
}

SpringBoot application monitoring Adding Timed annotation cause to error

I'm implementing micrometer to spring web project. While trying to add #Timed annotation. Prior to add #Timed i'm supposed to create TimedSpect bean. But it says could not autowire no bean of MeterRegistry type found
#Configuration
public class MetricsCofiguration {
#Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
Not sure if you still need this answer but this is what i tried and its working fine.
#Configuration
#EnableAspectJAutoProxy
public class TimedConfiguration {
#Autowired
MeterRegistry registry;
#Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
Make sure you have below starter dependency in your pom.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Enable and disable endpoints at runtime with Spring boot

Let's say I have the following controller:
#RestController
public class MyController {
#GetMapping("v1/remain")
public MyObject getRemain() {
// ...
}
}
How can I enable or disable this endpoint at runtime dynamically with Spring boot? Also, is it possible to change this without having to restart the application?
You can either use #ConditionalOnExpression or #ConditionalOnProperty
#RestController
#ConditionalOnExpression("${my.property:false}")
#RequestMapping(value = "my-end-point", produces = MediaType.APPLICATION_JSON_VALUE)
public class MyController {
#RequestMapping(value = "endpoint1", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> endpoint1(
return new ResponseEntity<>("Hello world", HttpStatus.OK);
}
}
Now if you want the above controller to work, you need to add following in application.properties file.
my.controller.enabled=true
Without the above statement, it will behave like the above controller don't exist.
Similiarly,
#ConditionalOnProperty("my.property")
behaves exactly same as above; if the property is present and "true", the component works, otherwise it doesn't.
To dynamically reload beans when a property changes, you could use Spring boot actuator + Spring cloud so that you have access to the /actuator/refresh endpoint.
This can be done by adding the following dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
The latter does require that you add the BOM for Spring cloud, which is:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Now you can enable the /actuator/refresh endpoint by setting the following property:
management.endpoints.web.exposure.include=refresh
This will allow you to send a POST call to /actuator/refresh, which will return an array of all changed properties.
By using the /actuator/refresh endpoint, it also allows you to use the #RefreshScope annotation to recreate beans. However, there are a few limitations:
#RefreshScope recreates the bean without re-evaluating conditionals that might have changed due to the refresh. That means that this solution doesn't work with #RefreshScope, as seen in the comment section of this question.
#RefreshScope doesn't work nicely with filters either, as seen in this issue.
That means you have two options:
Add the #RefreshScope to the controller and do the conditional logic by yourself, for example:
#RefreshScope
#RestController
#RequestMapping("/api/foo")
public class FooController {
#Value("${foo.controller.enabled}")
private boolean enabled;
#GetMapping
public ResponseEntity<String> getFoo() {
return enabled ? ResponseEntity.of("bar") : ResponseEntity.notFound().build();
}
}
This means you would have to add this condition to all endpoints within your controller. I haven't verified if you could use this with aspects.
Another solution is to not use #RefreshScope to begin with, and to lazily fetch the property you want to validate. This allows you to use it with a filter, for example:
public class FooFilter extends OncePerRequestFilter {
private Environment environment;
public FooFilter(Environment environment) {
this.environment = environment;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if ("true".equalsIgnoreCase(environment.getProperty("foo.controller.enabled"))) {
filterChain.doFilter(request, response);
} else {
response.setStatus(HttpStatus.NOT_FOUND.value());
}
}
}
You'll have to register the filter as well, for example by using:
#Bean
public FilterRegistrationBean<FooFilter> fooFilter(Environment environment) {
FilterRegistrationBean<FooFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new FooFilter(environment));
bean.addUrlPatterns("/api/foo");
return bean;
}
Please note, this approach only fetches the property dynamically from the Environment. Refreshing the Environment itself still requires you to use the /actuator/refresh endpoint.

Spring boot & Swagger 2 UI & custom requestmappinghandlermapping - mapping issue

I have own RequestMappingHandlerMapping and I am using springfox-swagger-ui. After adding my custom mapping, I am not able to achieve swagger ui at http://localhost:8080/swagger-ui.html.
Any ideas?
This is my configuration.
#Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
#Override
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new ApiVersionRequestMappingHandlerMapping("v");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/webjars/**")
.addResourceLocations("(META-INF/resources/webjars");
}
}
Here's my pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
When you override WebMvcConfigurationSupport, you are also overriding spring Boot's mvc auto configuration (WebMvcAutoConfiguration). Therefore, resources that need spring boot's configuration will not work. This is not a problem specific to swagger.
You can find more info about this here:
https://github.com/spring-projects/spring-boot/issues/5004
As the github issue suggests, there will be changes on this in the future to make it easier. Currently there are some workarounds, as suggested there.
A quick and dirty way of doing this is by copying and pasting the WebMvcAutoConfiguration class into your own class, returning your own HandlerMapping from the requestMappingHandlerMapping() method of EnableWebMvcConfiguration and registering the copy of the WebMvcAutoConfiguration as an auto configuration class. You can see instructions here:
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html
Make sure you place your copy of the WebMvcAutoConfiguration at some package which is not component scanned and picked up automatically. It should just be registered as explained in the above link.
Also make sure you set the order of you custom HandlerMapping to 0 before returning it from the requestMappingHandlerMapping() method, like so:
#Bean
#Primary
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
// Must be #Primary for MvcUriComponentsBuilder to work
ApiVersionRequestMappingHandlerMapping handlerMapping = new ApiVersionRequestMappingHandlerMapping("v");
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
PathMatchConfigurer configurer = getPathMatchConfigurer();
if (configurer.isUseSuffixPatternMatch() != null) {
handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
}
if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
}
if (configurer.isUseTrailingSlashMatch() != null) {
handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
}
if (configurer.getPathMatcher() != null) {
handlerMapping.setPathMatcher(configurer.getPathMatcher());
}
if (configurer.getUrlPathHelper() != null) {
handlerMapping.setUrlPathHelper(configurer.getUrlPathHelper());
}
return handlerMapping;
}
It works for me.
Overriding addResourceHandlers instead of registering auto-configuration.
Github Source
#Configuration
#EnableWebMvc
#EnableSwagger2
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry
.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
Overriding requestMappingHandlerMapping() of WebMvcConfigurationSupport will turn off spring boot's auto configuration. For adding custom MVC Components you may use WebMvcRegistrations. Like, for providing custom RequestMappingHandlerMapping, We may override getRequestMappingHandlerMapping(), with custom RequestMappingHandlerMapping, ofWebMvcRegistrationsAdapter and provide it through webMvcRegistrationsHandlerMapping(). As,
#Configuration
class CustomRequestMappingHandlerMapping {
#Bean
public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new ApiVersionRequestMappingHandlerMapping("v");
}
};
}
}
In Spring Boot 2.0.0, there is a simpler way to achieve this.
Create an instance of WebMvcRegistrations interface as a bean and override appropriate method to return the customized version of that object. Spring boot will read and use that instance.
In this case only the getRequestMappingHandlerMapping() needs to be overridden and a custom implementation returned
Above information is from a follow through based on the links provided by #Nazaret K.
More information at https://github.com/spring-projects/spring-boot/issues/5004
It can be solved by using WebMvcConfigurationSupport with adding resource handlers for swagger:
#Configuration
public class MvcConfiguration extends WebMvcConfigurationSupport {
#Value("${spring.application.name}")
private String applicationName;
//...irrelevant code here
#Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
I finally find it!
The right configuration is this:
#Configuration
public class VersioningMappingHandlerConfig {
#Bean
public ApiVersionRequestMappingHandlerMapping customMappingHandlerMapping() {
ApiVersionRequestMappingHandlerMapping handler = new ApiVersionRequestMappingHandlerMapping("v", 1, 1);
handler.setOrder(-1);
return handler;
}
}
Note: there is no extends WebMvcConfigurationSupport and bean name is customMappingHandlerMapping

Making Aspectj work on a Spring servlet bean

I am trying to get an aspectprofiler working on a Jersey servlet registered in a spring project. The aspectprofiler is loaded, but don't notice when methods within the Jersey servlet are run.
#EnableAutoConfiguration
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class App {
public static void main(final String[] args) {
final SpringApplicationBuilder sab = new SpringApplicationBuilder(ConsolidatedCustomerMasterApp.class);
sab.run(args);
}
#Bean
public ServletRegistrationBean jerseyServlet() {
final ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyInitialization.class.getName());
return registration;
}
#Bean
public AspectProfiler profiler() {
return new AspectProfiler();
}
}
...
public class JerseyInitialization extends ResourceConfig {
public JerseyInitialization() {
packages("com.example.package");
}
...
package com.example.package;
//imports
#Path("/test")
public class RestService {
#GET
#Path("test")
#Produces(MediaType.TEXT_PLAIN)
public String test() {
return "Something";
}
}
...
#Aspect
public class AspectProfiler {
private static final DefaultApplicationProfiler PROFILER = new DefaultApplicationProfiler(
Arrays.<ProfilerOperator> asList(
new StatsdProfilerOperator(),
new LoggingProfilerOperator())
);
private static final String REST_MATCHER =
"execution(* com.example.package..*.*(..))";
#Around(REST_MATCHER)
public Object around(final ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("test");
return PROFILER.around(joinPoint);
}
}
On top of making the Jersey resource classes Spring #Components (and #ComponentScaning for them), you also need to make the ResourceConfig a Spring #Component also. You can see in the Spring Boot JerseyAutoConfigurer that it autowires the ResourceConfig, which it uses for the ServletContainer registration.
One thing to also note is that it creates its own ServletRegistrationBean
public ServletRegistrationBean jerseyServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(
new ServletContainer(this.config), this.path);
addInitParameters(registration);
registration.setName("jerseyServlet");
return registration;
}
When you declare your own, you are overriding this one. You are not adding any special functionality that is not already provided, so just leave the default. Any Jersey specific configurations can be added in the application.properties file or through code configurations.
As for dependencies, I'll just assume you have all the right dependencies. The following are what I used to test
<!-- all 1.2.7.RELEASE -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
See Also:
spring-boot-sample-jersey - from project samples
ยง26.2 JAX-RS and Jersey - from Spring Boot docs

Resources