I need to measure method-metrics using micrometer #Timed annotation. As it doesn't work on arbitrary methods; i added the configuration of #TimedAspect explicitly in my spring config. Have referred to this post for exact config
Note: have tried adding a separate config class JUST for this, as well as including the TimedAspect bean as part of my existing configuration bean
How to measure service methods using spring boot 2 and micrometer
Yet, it unfortunately doesn't work. The Bean is registred and the invocation from config class goes thru successfully on startup. Found this while debugging. However, the code in the #Around never seems to execute.
No error is thrown; and im able to view the default 'system' metrics on the /metrics and /prometheus endpoint.
Note: This is AFTER getting the 'method' to be invoked several times by executing a business flow. I'm aware that it probably doesn't show up in the metrics if the method isn't invoked at all
Versions: spring-boot 2.1.1, spring 5.3, micrometer 1.1.4, actuator 2.1
Tried everything going by the below posts:
How to measure service methods using spring boot 2 and micrometer
https://github.com/izeye/sample-micrometer-spring-boot/tree/timed-annotation
https://github.com/micrometer-metrics/micrometer/issues/361
Update: So, the issue seems to be ONLY when the Timed is on an abstract method, which is called via another method. Was able to reproduce it via a simple example. Refer to the #Timed("say_hello_example") annotation. It simply gets ignored and doesnt show up when i hit the prometheus endpoint.
Code:
Abstract Class
public abstract class AbstractUtil {
public abstract void sayhello();
public void sayhellowithtimed(String passedVar) {
System.out.println("Passed var =>"+passedVar);
System.out.println("Calling abstract sayhello....");
sayhello();
}
}
Impl Class
#Component
#Scope("prototype")
public class ExampleUtil extends AbstractUtil {
public static final String HELLO = "HELLO";
#Timed("dirwatcher_handler")
public void handleDirectoryWatcherChange(WatchEvent event){
System.out.println("Event kind:" + event.kind() + ". File affected: " + event.context());
}
#Timed("say_hello_example")
#Override
public void sayhello() {
System.out.println(HELLO);
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
A simple DirWatcher implementation class...
package com.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Scope;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.file.*;
#Component
#Scope("prototype")
public class StartDirWatcher implements ApplicationListener<ApplicationStartedEvent> {
#Value("${directory.path:/apps}")
public String directoryPath;
#Autowired
private ExampleUtil util;
private void monitorDirectoryForChanges() throws IOException, InterruptedException {
WatchService watchService = FileSystems.getDefault().newWatchService();
Path path = Paths.get(directoryPath);
path.register(
watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
util.handleDirectoryWatcherChange(event);
util.sayhellowithtimed("GOD_OF_SMALL_THINGS_onAPPEvent");
}
key.reset();
}
}
#Override
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
try {
monitorDirectoryForChanges();
} catch (Throwable e) {
System.err.println("ERROR!! "+e.getMessage());
e.printStackTrace();
}
}
}
The Spring Boot Application Class
package com.example;
import io.micrometer.core.aop.TimedAspect;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#EnableAspectJAutoProxy
#ComponentScan
#Configuration
#SpringBootApplication
public class ExampleStarter{
#Bean
MeterRegistryCustomizer<PrometheusMeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags("app.name", "example.app");
}
#Bean
TimedAspect timedAspect(MeterRegistry reg) {
return new TimedAspect(reg);
}
public static void main(String[] args) {
SpringApplication.run(ExampleStarter.class, args);
}
}
The main pom.xml file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.metrics.timed.example</groupId>
<artifactId>example-app</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
</dependencies>
I use spring boot 2.2.6.RELEASE and this MetricConfig works for me
#Configuration
public class MetricConfig {
#Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags("application", "my app");
}
#Bean
TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
In application.yml
management:
endpoints:
web:
exposure:
include: ["health", "prometheus"]
endpoint:
beans:
cache:
time-to-live: 10s
#Timed use AOP(Aspect oriented programming) concept, in which proxy doesn't pass on to the second level of the method.
you can define the second level of method in new bean/class. this way #Timed will work for second level of method call.
I had the same problem, in my case I realised that the metric got visible under actuator/metrics only after the method had been called at least once.
Unlike with manually created timers/counters, where they get visible directly after startup.
We have our Spring Boot services behind an API Gateway. With an earlier version of Springfox - 2.1.2 we had no issues in loading the swagger-ui.html page. This worked with Spring Boot 1.4.3.RELEASE. From then, we have upgraded to Boot 1.5.7 and upgraded Springfox to 2.8.0.
Now if we load the page we get an alert box with the following long message.
Unable to infer base url. This is common when using dynamic servlet
registration or when the API is behind an API Gateway. The base url is
the root of where all the swagger resources are served. For e.g. if
the api is available at http://example.org/api/v2/api-docs then the
base url is http://example.org/api/. Please enter the location
manually
I got some hints searching online, but it does not seem those situations apply to us. For one, if I simply revert back the versions, it starts working again through the same API Gateway.
Tracking the traffic, it seems calls to three XHR resources made by the .html page is causing issues. These are returning 401 from our API gateway. And the reason they return 401 is because the cookies are not passed along.
The three calls are:
https://base_address/base_context/swagger-resources/configuration/ui
https://base_address/base_context/swagger-resources/configuration/security
https://base_address/base_context/swagger-resources
If I load these URLs as pure browser requests - they work - because cookies are sent.
I doubt if CORS applies since the HTML is being served from the same address as the swagger JSON and actual service calls.
Any idea why this may be happening? Anybody faced similar issues? Suggestions for workaround? Thanks much in advance.
Add in the security config -- following URLS that are skipped for authentication ::
private static final String[] AUTH_WHITELIST = {
"/swagger-resources/**",
"/swagger-ui.html",
"/v2/api-docs",
"/webjars/**"
};
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(AUTH_WHITELIST);
}
Adding below annotation at the spring boot class resolved this issue for me.
#EnableSwagger2
I am using swagger version
<version>2.9.2</version>
SEE EDIT BELOW
Do you use spring security?
If yes, probably you skip some resources like this (right?):
"/swagger-resources/**",
"/swagger-ui.html",
"/v2/api-docs",
"/webjars/**"
Try to change it "/swagger-resources/**" to "**/swagger-resources/**".
My specific security config for swagger is:
private static final String[] AUTH_LIST = {
// -- swagger ui
"**/swagger-resources/**",
"/swagger-ui.html",
"/v2/api-docs",
"/webjars/**"
};
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers(AUTH_LIST).authenticated()
.and()
.httpBasic().authenticationEntryPoint(swaggerAuthenticationEntryPoint())
.and()
.csrf().disable();
}
#Bean
public BasicAuthenticationEntryPoint swaggerAuthenticationEntryPoint() {
BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
entryPoint.setRealmName("Swagger Realm");
return entryPoint;
}
If you need/want I can send a sample project to GitHub to you know more about my security/swagger configs.
EDIT 2018/04/10
This problem is caused by a wrong version in springfox. See this issue on github to solve the problem.
To posterity:
In pom.xml
...
<repositories>
<repository>
<id>swagger</id>
<name>swagger</name>
<url>http://oss.jfrog.org/artifactory/oss-snapshot-local</url>
</repository>
</repositories>
...
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.1-SNAPSHOT</version>
</dependency>
...
Class that extends WebSecurityConfigAdapter:
#Configuration
public class WebSecurityConfigEntryPointApplication extends WebSecurityConfigurerAdapter {
private static final List<String> AUTH_LIST = Arrays.asList(
"/swagger-resources/**",
"/swagger-ui.html**",
"/webjars/**",
"favicon.ico");
#Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**").authorizeRequests().anyRequest().authenticated()
.and()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(swaggerAuthenticationEntryPoint(), new CustomRequestMatcher(AUTH_LIST))
.and()
.httpBasic()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.csrf().disable();
}
#Bean
public BasicAuthenticationEntryPoint swaggerAuthenticationEntryPoint() {
BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
entryPoint.setRealmName("Swagger Realm");
return entryPoint;
}
private class CustomRequestMatcher implements RequestMatcher {
private List<AntPathRequestMatcher> matchers;
private CustomRequestMatcher(List<String> matchers) {
this.matchers = matchers.stream().map(AntPathRequestMatcher::new).collect(Collectors.toList());
}
#Override
public boolean matches(HttpServletRequest request) {
return matchers.stream().anyMatch(a -> a.matches(request));
}
}
}
RestAuthenticationEntryPoint:
#Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
This happened to me, I was using SpringBoot 1.5.16 and Springfox 2.9.1.
In my application.properties, I had defined server.servlet-path=/api, but, somehow, the swagger-ui was ignoring the value defined. I've tried so many different way to make this work, and finally I found a workaround:
#Configuration
#EnableSwagger2
public class SwaggerConfiguration extends WebMvcConfigurationSupport {
#Bean
public Docket apiMonitoramento() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("REST API")
.description("Servicesx")
.build();
}
#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 was accessing http://localhost:8080/context/swagger-ui.html, but with that configuration the correct URL is: http://localhost:8080/context/api/swagger-ui.html
In my case, the cause of the problem was having:
#ComponentScan(basePackageClasses = {ApplicationRoot.class })
twice in two java files.
after removing the extra one, the problem went away.
Upgrade springfox-swagger2 and springfox-swagger-ui dependencies to 2.9.2 and also ensure the basePackage is given properly
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors
.basePackage("org.abc.xyz.controller"))
.paths(PathSelectors.regex("/.*"))
.build().apiInfo(apiEndPointsInfo());
I don't use spring security happened this question. My Project Use Maven Multiple Module, When access to the localhost:8080/swagger-ui.html happend this question, First I add #EnableSwagger2 in the SwaggerConf class ,Last I move #EnableSwagger to SpringBoot Application class ,this question is solved.
First:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api(){
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.zuoyan."))
.paths(PathSelectors.any())
.build();
}
}
Finally:
#SpringBootApplication(scanBasePackages = {"com.zuoyan.springboot.appmissionhall"})
#EnableSwagger2
public class ApplicationStartUpApplication {
public static void main(String[] args) {
SpringApplication.run(ApplicationStartUpApplication.class, args);
}
}
If you do not specify any special component scan options you will face this problem if you put the class with the #EnableSwagger2 annotation in a package that is not in the hierarchy of your Spring Boot Application class (#SpringBootApplication).
Assume your Spring Boot Application class in "de.oopexpert.app", then putting #EnableSwagger2 annotated class in ...
de.oopexpert.app will work
de.oopexpert.app.config will work
de.oopexpert.config will NOT work
You may adapt your component scan options by adding #ComponentScan(basePackages = {"de.oopexpert"}) to specify a different root of the hierarchy.
The solution from https://stackoverflow.com/a/56716898/13347514 by adding #EnableSwagger2WebMvc and #Import(SpringDataRestConfiguration.class) to the Main Application Class fixes my problem:
#SpringBootApplication
#EnableSwagger2WebMvc
#Import(SpringDataRestConfiguration.class)
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
In many cases it is due to Java Version incompatibility. Many times it doesn't work with Java 11, try using Java 8
Just clear your browser cache. It worked for me.
My Swagger Docket Bean Config file:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#Configuration
#EnableSwagger2
public class DocketBean implements WebMvcConfigurer {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.swagger.demo"))
.paths(PathSelectors.any())
.build();
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// enabling swagger-ui part for visual documentation
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
Swagger Dependencies in POM:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
The swagger-ui url :
http://localhost:8080/swagger-ui.html
try with port 8080 - worked for me after i changed it to 8080
I added #EnableSwagger2WebMvc to the App class to fix it. I am using Spring boot 2.3.0.BUILD-SNAPSHOT and io.springfox 3.0.0-SNAPSHOT. SpringFoxConfig class stays the same.
package com.telixia.educare.academy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
#EnableSwagger2WebMvc
#SpringBootApplication
public class AcademyApplication {
public static void main(String[] args) {
SpringApplication.run(AcademyApplication.class, args);
}
}
This could also be caused by the springfox-swagger-ui and springfox-swagger2 versions mismatch in the pom.xml, for example, if you updated one but forgot to update another:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
You need to make sure springfox-swagger-ui and springfox-swagger2 have the same version.
Firstly make sure these 2 dependencies are added and then annotate your main SpringBootApplication class with #EnableSwagger2 and then your problem will be solved.
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
I was facing the same issue with a basic Spring MVC application (no Spring Security).
I replaced
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.
addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
.resourceChain(false);
}
with
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.
addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("**/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
and it did the trick
In my case, adding the springfox-spring-webmvc dependency resolved the issue:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-spring-webmvc</artifactId>
<version>2.10.5</version>
</dependency>
I had the same problem, but just adding #EnableSwagger2 on top of the main App class fixed it.
example :
#SpringBootApplication
#EnableSwagger2
public class AcademyApplication {
public static void main(String[] args) {
SpringApplication.run(AcademyApplication.class, args);
}
#Configuration
class RestTemplateConfig {
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
}
In my case, My project use Maven multiple module without spring security.
SpringBoot v2.2.7.RELEASE
Swagger2 & Swagger-ui v2.9.2
My solution is change the value of #Profile(), enter the right name of the configuration profile you create in the project resourse directory.
First:
#Configuration
#EnableSwagger2
#Profile({ "dev", "test" })
public class SwaggerConfig {
private ApiInfo getApiInfo(String title, String desc) {
return new ApiInfoBuilder().title(title)
.description(desc)
.version(DOC_VERSION)
.termsOfServiceUrl(URL)
.build();
}
}
Last:
#Configuration
#EnableSwagger2
// #Profile({ "dev", "test" })
#Profile({ "kf-oracle", "kf-mysql" })
public class SwaggerConfig {
private ApiInfo getApiInfo(String title, String desc) {
return new ApiInfoBuilder().title(title)
.description(desc)
.version(DOC_VERSION)
.termsOfServiceUrl(URL)
.build();
}
}
I had the same issue,i had written #EnableSwagger2 annotation at class level in SwaggerConfig file but i forgot to place #Configuration at class level in SwaggerConfig class. Adding that annotation solved my issue.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#EnableSwagger2
#Configuration
public class swaggerConfig {
// swagger configuration
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("mention your package name here")).build();
}
}
If you have the same issue again then do #ComponentScan(basePackages = "mention your root package name here") in your Application.java file
First, add the Swagger dependency to pom.xml
Swagger Dependency in POM
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
After that, it's time to configure the Swagger 3rd party that we installed. Therefore follow the order below.
1/Make a package and set name Config
2/Make a class SwaggerConfig
3/After it set below code to that class
#Configuration
public class SwaggerConfig {
#Bean
public Docket SwaggerApi(){
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
4/checked that class #Configuration and #Bean annotations
5/Need to enable Swagger2 in your main method class
6/use #EnableSwagger2 annotation to Enable Swagger 2
After that go to your browser and get swagger UI to see our UI. For it use this code http://localhost:8081/swagger-ui.html#/ check your server.port
If any doubt coming cheng your spring-boot-starter-parent pom.xml to low version.
Example:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
your project will run without error and smoothly.
Try the following in "*.yml" file
swagger:
enable: true
or try "swagger.enable=true" in "*.properties"
hi i found solution for this swagger popup as we can see a input box inside a swagger
you need to add /web or what ever url pattern configured in your code
example in popup input text field you need to add like this
popup with error
solution
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
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
I am trying to do a full annotations (no xml) implementation of Spring. The autowired members are not being populated. From my research, there are 3 things to do:
Set up a config file that manages the bean
Use #Autowired to get the bean to the file
Instantiate an application context to get the ball rolling
It is difficult to find a complete example of this which uses annotations only, so I don't have much to reference. Most examples use at least some xml.
There is no error message, so I don't have any idea where the problem is. The value is just null. Here are my files:
Trivial.java
public class Trivial {
public TrivialBean trivialBean;
#Autowired
public void setTrivialBean(TrivialBean trivialBean) {
this.trivialBean = trivialBean;
}
public static void main(String...args) {
ApplicationContext context
= new AnnotationConfigApplicationContext(
TrivialConfig.class);
new Trivial().go();
}
private void go() {
System.out.println("trivialBean: " + trivialBean);
}
}
TrivialBean.java
public class TrivialBean {
public String foo = "TEST TEST TEST";
#Override
public String toString() {
return foo;
}
}
TrivialConfig.java
#Configuration
public class TrivialConfig {
#Bean
public TrivialBean trivialBean() {
return new TrivialBean();
}
}
I would expect this to output trivialBean: TEST TEST TEST, but is just outputs trivialBean: null
For the #Autowired in Trivial to work, you need to have Trivial instantiated by Spring. new Trivial() won't work. For your sample to work, I think you need the following:
Configure Trivial as a bean.
Change new Trivial() to context.getBean(Trivial.class).
However, note that it is considered bad practice to use context.getBean under normal circumstances.
Regular autowiring in annotation-based container configuration
In order for autowiring to work, the lifecycle of the instance of Trivial has to be managed by the Spring container.
Example
TrivialBean.java is the same
public class TrivialBean {
public String foo = "TEST TEST TEST";
#Override
public String toString() {
return foo;
}
}
TrivialConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class TrivialConfig {
#Bean
public TrivialBean trivialBean() {
return new TrivialBean();
}
#Bean
public Trivial trivial() {
return new Trivial();
}
}
Trivial.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Trivial {
public TrivialBean trivialBean;
#Autowired
public void setTrivialBean(TrivialBean trivialBean) {
this.trivialBean = trivialBean;
}
public static void main(String... args) {
ApplicationContext context = new AnnotationConfigApplicationContext(TrivialConfig.class);
Trivial trivial = context.getBean(Trivial.class);
trivial.go();
}
private void go() {
System.out.println("trivialBean: " + trivialBean);
}
}
Output
trivialBean: TEST TEST TEST
Please consult Spring documentation for more information on Annotation-based container configuration.
AspectJ compile-time weaving and #Configurable
It is possible to autowire TrivialBean instance into Trivial instance created by new.
spring-aspects.jar contains an annotation-driven aspect that allows dependency injection for objects created outside of the control of the container. However, it should not be used in new Spring-based projects. It is intended to be used for legacy projects, where for some reason some instances are created outside of the Spring container.
Example for Spring 4.2.0 (the latest at the moment), AspectJ 1.8.6 (the latest at the moment), Maven and Java 1.8.
Additional dependencies on spring-aspects and aspectjrt
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.6</version>
</dependency>
Compile time weaving via AspectJ Maven plugin
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<encoding>UTF-8</encoding>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<Xlint>warning</Xlint>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
TrivialBean.java is the same
public class TrivialBean {
public String foo = "TEST TEST TEST";
#Override
public String toString() {
return foo;
}
}
TrivialConfig.java
#EnableSpringConfigured is analogous to <context:spring-configured>. It signals the current application context to apply dependency injection to classes that are instantiated outside of the Spring bean factory.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.aspectj.EnableSpringConfigured;
#Configuration
#EnableSpringConfigured
public class TrivialConfig {
#Bean
public TrivialBean trivialBean() {
return new TrivialBean();
}
}
Trivial.java
#Configurable applies Spring-driven configuration to Trivial
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
#Configurable
public class Trivial {
public TrivialBean trivialBean;
#Autowired
public void setTrivialBean(TrivialBean trivialBean) {
this.trivialBean = trivialBean;
}
public static void main(String... args) {
ApplicationContext context = new AnnotationConfigApplicationContext(TrivialConfig.class);
Trivial trivial = new Trivial();
trivial.go();
}
private void go() {
System.out.println("trivialBean: " + trivialBean);
}
}
Output
trivialBean: TEST TEST TEST
It works! Please consult Spring documentation for more information on AspectJ and #Configurable.