Here are my dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
When my Spring Boot application is registered with Eureka, I can define a RestTemplate bean like this:
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
And in my services I can make requests to other services using their registered spring.application.name:
restTemplate.getForEntity("http://application1/test", String.class);
How do I define where http://application1/ is located with Eureka disabled?
eureka.client.enabled=false
Current Implementation test:
#Configuration
public class RibbonConfig {
#Bean
public ServerList<Server> serverServerList() {
return new ConfigurationBasedServerList();
}
}
#Configuration
public class WebConfig {
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
#Component
public class TestService implements CommandLineRunner {
#Autowired
private RestTemplate restTemplate;
#Override
public void run(String... args) throws Exception {
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://application1/test", String.class);
System.out.println(responseEntity);
}
}
#RibbonClient(value = "application1", configuration = RibbonConfig.class)
#SpringBootApplication
public class Demo5Application {
public static void main(String[] args) {
SpringApplication.run(Demo5Application.class, args);
}
}
bootstrap.yml
eureka:
client:
enabled: false
application1:
ribbon:
list-of-servers: http://localhost:8081/
you can define a new RibbonClient configuration for your service this way:
#Configuration
#RibbonClient(name = "application1", configuration = Application1RibbonClientConfiguration.class)
public class Application {
...
}
class Application1RibbonClientConfiguration{
#Bean
public ServerList<Server> ribbonServerList() {
return new ConfigurationBasedServerList();
}
}
you can skip all the configuration above if you don't have eureka on classPath
next in your properties file you can list all the servers location like this:
application1.ribbon.listOfServers=..,..,..
Related
I encountered a problem with my Ribbon application. Here is my code:
#SpringBootApplication
#EnableDiscoveryClient
#RestController
#RibbonClient(name= "bye", configuration=RibbonConfig.class )
public class RibbonAppApplication {
#Autowired
private RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(RibbonAppApplication.class, args);
}
#GetMapping
public String getService() {
return restTemplate.getForObject("http://bye",String.class);
}
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
And my RibbonConfig.class:
#Configuration
public class RibbonConfig {
#Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl(false,"/health");
}
#Bean
public IRule ribbonRule(IClientConfig config) {
return new AvailabilityFilteringRule();
}
}
However, I got the following error:
Parameter 0 of method ribbonPing in practice.zuul.zach.ribbonapp.RibbonConfig required a bean of type 'com.netflix.client.config.IClientConfig' that could not be found.
Action:
Consider defining a bean of type 'com.netflix.client.config.IClientConfig' in your configuration.
Is there any ways to solve it?
problem solved when i add this line in RibbonAppApplication class
#SpringBootApplication(scanBasePackages={"com.netflix.client.config.IClientConfig"})
I have an unit test on Spring Boot:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = Application.class)
public class CustomerControllerIT {
private RestTemplate restTemplate = new RestTemplate();
#Test
public void findAllCustomers() throws Exception {
ResponseEntity<List<Customer>> responseEntity = restTemplate.exchange(
"http://localhost:8080/Customer", HttpMethod.GET, null,
new ParameterizedTypeReference<List<Customer>>() {
});
List<Customer> list = responseEntity.getBody();
Assert.assertEquals(list.size(), 0);
}
}
If I launch test on started application - tests ok
If I try to launch only IT, there is connection refused error
My application.properties is same for single start.
For tests and located in resources and testResources.
Application.class is:
#ComponentScan({"mypackage"})
#EntityScan(basePackages = {"mypackage.model"})
#EnableJpaRepositories(basePackages = {"mypackage.persistence"})
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You must run a test with a running server ,
If you need to start a full running server, you can use random ports:
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
An available port is picked at random each time your test runs
You need this maven dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
Example:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class TestRest {
#Autowired
private TestRestTemplate restTemplate;
#Test
public void findAllCustomers() throws Exception {
ResponseEntity<List<Customer>> responseEntity = restTemplate.exchange(
"/Customer", HttpMethod.GET, null,
new ParameterizedTypeReference<List<Customer>>(){});
List<Customer> list = responseEntity.getBody();
Assert.assertEquals(list.size(), 0);
}
}
Please read the documentation for more reference
For running #SpringBootTest and JUnit5 (Jupiter) with static port you can use:
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class MyUnitTests {
#Test
void checkActuator() throws Exception {
final String url = "http://localhost:8080/actuator/health";
#SuppressWarnings("rawtypes")
ResponseEntity<Map> re = new RestTemplate().getForEntity(url, Map.class);
System.out.println(re);
assertEquals(HttpStatus.OK, re.getStatusCode());
re.getStatusCode();
}
}
If you're using JUnit 4:
Change: #ExtendWith(SpringExtension.class)
To: #RunWith(SpringRunner.class)
And then add the port property to your application.yml (inside folder src/main/resources):
server:
port: 8080
Or if have an application.properties:
server.port=8080
Reference:
How to configure port for a Spring Boot application
https://www.baeldung.com/spring-boot-change-port
Test the SpringBoot application startup
I have a simple aspect like below
#Aspect
public class PersistentAspect {
#AfterReturning("#annotation(org.aspect.PersistentOperation)")
public void log(JoinPoint jp) {
System.out.println("aspect call");
}
}
and an AppConfig like below
public class AppConfig {
private Integer num;
private String text;
public Integer getNum() {
return num;
}
#PersistentOperation
public void setNum(Integer num) {
this.num = num;
}
public String getText() {
return text;
}
#PersistentOperation
public void setText(String text) {
this.text = text;
}
}
And configuration class like below
#EnableWs
#Configuration
public class WsConfig extends WsConfigurerAdapter {
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
AppConfig config = config();
interceptors.add(new CustomValidatingInterceptor(schema(), null));
}
#Bean
public AppConfig config() {
AppConfig config = null;
config = new AppConfig();
return config;
}
#Bean
public PersistentAspect persistentAspect() {
PersistentAspect persistentAspect = new PersistentAspect();
return persistentAspect;
}
}
If I use below in the addInterceptors
AppConfig config = config();
The Aspect will not work. The obvious solution I have is to change the code to
AppConfig config = new AppConfig();
Now what I want to understand is, is there a config in which AppConfig config = config(); could still be made working. I assume that when spring initiates the Bean, it can make an AOP proxy of the AppConfig, and when I initiate the bean it interferes with that process somehow. What is the spring/spring-boot way of handling this?
Below is the pom.xml, so basically latest Spring 5.0.5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Edit-1
Before posting the I had already adding a #EnableAspectJAutoProxy, but that had not helped.
The git repo to try the issue is on below
https://github.com/tarunlalwani/spring-aop-so-50084308
Your AppConfig bean (which you initialize, correctly, using a method annotated with #Bean) needs to be wrapped in a proxy that then executes the aspect logic.
You have to enable this behavior by adding the #EnableAspectJAutoProxy annotation to your WsConfig class.
By the way: it is totally correct to use
AppConfig config = config();
in your addInterceptors method. Spring will return the bean that was created by the config method.
i try to integrate swagger into my spring boot project but i receive always an error :
"Error creating bean with name 'modelMapperImpl': Failed to introspect bean class [springfox.documentation.swagger2.mappers.ModelMapperImpl] " but when i remove #configuration from swaggerConfig swagger will not be detected ,here is my code :
WebConfig:
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE",
"PATCH");
}
}
SwaggerConfig
#EnableSwagger2
#Configuration
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
Pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
Try this approach.
#Configuration
#ConfigurationProperties(prefix = "mycompany.cors")
public class CorsSettings {
final private Logger log = LoggerFactory.getLogger(CorsSettings.class);
private List<String> origins = new ArrayList<>();
public CorsSettings() {
log.debug("construct CorsSettings");
}
public List<String> getOrigins() {
return this.origins;
}
#Bean
public WebMvcConfigurer corsConfigurer() {
if (origins != null) {
log.debug("corsOrgins=" + origins);
} else {
log.error("corsOrgins=null");
}
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins(origins.toArray(new String[origins.size()]));
}
};
}
}
Next in Config
#Configuration
public class SwaggerConfig {
ApiInfo apiInfo() {
return new ApiInfoBuilder().title("My Swagger API").description("This is a my swagger server")
.license("").licenseUrl("https://opensource.org/licenses/MIT").termsOfServiceUrl("").version("1.0.0")
.contact(new Contact("My name", "://www.mycompany.com", "myemail#mycompany.com"))
.build();
}
#Bean
public Docket customImplementation() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("com.mycompany.path")).build()
.apiInfo(apiInfo());
}
}
Then in application.yml
mycompany:
cors:
origins:
- ${origin1:http://localhost:8080}
- ${origin2:http://localhost:8080}
- ${origin3:http://localhost:8080}
- ${origin4:http://localhost:8080}
I am having a wrapper that autowire ribbon aware RestTemplate and then I add a interceptor-
#Component
public class MyRestTemplate {
#Autowired
private RestTemplate restTemplate;
#PostConstruct
public void init(){
restTemplate.setInterceptors(Collections.singletonList(new
MyRestTemplateRequestInterceptor(applicationName)));
}
}
If I #AutoWire RestTemplate in some other component, will I get same instance of RestTemplate with this incterceptor? My observation is the autowired resttemplate in other component also adds this interceptor. Please help to validate the understanding.
Yyou will get the same instance of RestTemplate. When the bean MyRestTemplate is constructor (rather after it's construction), you are adding the MyRestTemplateRequestInterceptor to the RestTemplate interceptor.
So this interceptor will be applied to RestTemplate.
If you are looking to apply an interceptor for a specific feign client you can create your own configuration:
#Configuration
#IgnoreDuringScan
public class FeignClientConfiguration {
#Autowired
private Environment environment;
#Bean
public Encoder feignEncoder() {
return new GsonEncoder();
}
#Bean
public ErrorDecoder feignDecoder() {
return new GsonDecoder();
}
#Bean
public RequestInterceptor requestHeaderInterceptor() {
return new RequestInterceptor() {
#Override
public void apply(RequestTemplate requestTemplate) {
//impl
}
};
}
#Bean
public Feign.Builder feingBuilder() {
return Feign.builder().encoder(feignEncoder()).decoder(feignDecoder()).errorDecoder(new YourErrorDecoder())
.requestInterceptor(requestHeaderInterceptor);
}
}
Then just apply the configuration to your feign client:
#FeignClient(name = "myFeign", url = "url/...", configuration = {
FeignClientConfiguration.class
})
public interface YourFeignClient {
//....
}