Spring #Order annotation not loading in correct order - java

I am using Spring Boot embedded tomcat.
In my application I have three configuration classes and I have used the #Order annotation to control the loading order of classes. When I run the application in embedded Tomcat it's working fine, but in standalone Tomcat (WAR) it's not loading in the correct order.
My classes are like below :
#Order(1) public Class WebConfig
#Order(2) public Class SwaggerConfig
#Order(3) public Class PlanoutConfig

#Order defines the sort order for annotated components, not for configuration classes.
I suppose that in embedded Tomcat mode, you benefit from a side effect.
If your classes are configuration classes, that is, classes
annotated with #Configuration, the spring boot documentation states that
you should favor #AutoconfigureOrder over #Order.
44.1 Understanding auto-configured beans
If you want to order certain auto-configurations that shouldn’t have
any direct knowledge of each other, you can also use
#AutoconfigureOrder. That annotation has the same semantic as the
regular #Order annotation but provides a dedicated order for
auto-configuration classes.
AutoConfigureOrder
public #interface AutoConfigureOrder
Auto-configuration specific variant of Spring Framework's Order
annotation. Allows auto-configuration classes to be ordered among
themselves without affecting the order of configuration classes passed
to AnnotationConfigApplicationContext.register(Class...).
You could so write :
#AutoConfigureOrder(0) public Class WebConfig {...}
#AutoConfigureOrder(1) public Class SwaggerConfig {...}
#AutoConfigureOrder(2) public Class PlanoutConfig {...}

Related

How the default run() function in springboot maven project works

I am new to the Springboot framework, and I have some questions about the defualt run() function in the application.java file of the springboot maven project.
I'm trying to build a REST api with spring maven project. In specific, if I have a #Restcontroller class, when and how does the default run() function call an instantiated object of it? And how are the annotated classes like #Service, #RestController and #SpringBootApplication linked together when running the application? (like, what is the process of execution of all these components at runtime?) I know the controller class is used to host APIs, but what should I put in the #Service classes?
Thanks!
#SpringBootApplication annotation in the main application class combines the #EnableAutoConfiguration, #Configuration and the #ComponentScan annotations.
When run, the static run() method starts the Spring Boot application:
#ComponentScan scans the main class package and subpackages for different #Component classes such as controllers, services and repos and registers them as beans in the ApplicationContext.
#EnableAutoConfiguration auto configures your application based on the included jars in classpath.
As far as components go:
#RestController as you said exposes the application's endpoints using #RequestMapping method annotations.
#Service holds the business logic and calls repository methods.
#Repository is data access object that connects to the databases.

difference between #SpringBootTest(classes = SomeController.class) and #WebMvcTest(SomeController.class)

I understand that using #SpringbootTest I raise whole spring contex during test, or In my case using #SpringBootTest(classes = SomeController.class) I raise only one bean -> SomeController. If this controller have some dependencies I need to mock them up. Using annotation #WebMvcTest(SoneController.class) I will (based on my knowledge) achieve the same goal.
Question is: Are there any differences between those two annotations used in provided example?
There's a clear difference between #SpringBootTest(classes = SomeController.class) and #WebMvcTest(SomeController.class).
#SpringBootTest(classes = SomeController.class) - starts a server (i.e like Tomcat) + spring application context with the component SomeController.class. In addition to the controller, you should normally specify the context configuration to successfully start the whole app (For ex: when you don't specify the classes, it falls back to #SpringBootApplication).
#WebMvcTest(SomeController.class) - only starts the web layer of the application with SomeController.class.
What's the difference?
#SpringBootTest tests are usually integration tests, you start the full spring-boot application and test against that black box. You can still tweak the application startup by providing configuration, properties, web server type etc in the annotation parameters.
But #WebMvcTest(SomeController.class) is usually a unit test for your controller. These are lightweight and fast. The dependencies like #Service classes are mocked in such tests.
This is a good starting point - https://spring.io/guides/gs/testing-web/
There are several subtle differences between these two ways.
But you will discover a part of them only randomly when you will encounter problems such as bean initialization exception during the spring boot context init or a NullPointerException rising during the test execution.
To make things simpler, focus on intention.
When you write that :
#SpringBootTest(classes = SomeController.class)
you will make Spring to init only the SomeController bean instance.
Is it desirable to test a controller ?
Probably no since you need a way to invoke the controller with a controller approach.
For that a MockMvc instance would help.
With WebMvcTest you get that bean additionally loaded in the test context.
So that way is preferable :
#WebMvcTest(SomeController.class)
public class SomeControllerTest{
#Autowired
private MockMvc mvc;
...
}
Of course you could get a similar behavior with #SpringBootTest and some additional classes but it will be just an overhead : the #WebMvcTest specialized annotation is enough.
At last why make the reading of the test class harder for your follower ?
By weaving a contrived way of using spring boot test annotation, chances are good to come there.
I think for answering your question enough just read the Javadoc for both of these annotations:
1. #WebMvcTest
Annotation that can be used for a Spring MVC test that focuses only on Spring MVC components.
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. #Controller, #ControllerAdvice, #JsonComponent, Converter/GenericConverter, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not #Component, #Service or #Repository beans).
By default, tests annotated with #WebMvcTest will also auto-configure Spring Security and MockMvc (include support for HtmlUnit WebClient and Selenium WebDriver). For more fine-grained control of MockMVC the #AutoConfigureMockMvc annotation can be used.
#SpringbootTest
Annotation that can be specified on a test class that runs Spring Boot based tests. Provides the following features over and above the regular Spring TestContext Framework:
Uses SpringBootContextLoader as the default ContextLoader when no specific #ContextConfiguration(loader=...) is defined.
Automatically searches for a #SpringBootConfiguration when nested #Configuration is not used, and no explicit classes are specified.
Allows custom Environment properties to be defined using the properties attribute.
Allows application arguments to be defined using the args attribute.
Provides support for different webEnvironment modes, including the ability to start a fully running web server listening on a defined or random port.
Registers a TestRestTemplate and/or WebTestClient bean for use in web tests that are using a fully running web server.

How to test that Spring Boot does *not* start if datasource creation failed

Is there a way to write unit tests to make sure spring boot API doesn't get started if a certain bean is failed to create. eg: failing to create datasource bean.
This code should do it for you:
#RunWith(SpringRunner.class)
#SpringBootTest
public class AnyAppNameApplicationTests {
#Test
public void contextLoads() {
}
}
From the docs:
Annotation that can be specified on a test class that runs Spring Boot
based tests. Provides the following features over and above the
regular Spring TestContext Framework:
Uses SpringBootContextLoader as the default ContextLoader when no specific #ContextConfiguration(loader=...) is defined.
Automatically searches for a #SpringBootConfiguration when nested #Configuration is not used, and no explicit classes are specified.
Allows custom Environment properties to be defined using the properties attribute.
Provides support for different webEnvironment modes, including the ability to start a fully running web server listening on a defined or
random port.
Registers a TestRestTemplate and/or WebTestClient bean for use in web tests that are using a fully running web server.
If you are using JUnit 4, don’t forget to also add
#RunWith(SpringRunner.class) to your test, otherwise the annotations
will be ignored. If you are using JUnit 5, there’s no need to add the
equivalent #ExtendWith(SpringExtension) as #SpringBootTest and the
other #…Test annotations are already annotated with it.

Using #Profile in spring boot

I have spring boot application (1.1.5.RELEASE) and enabling my profiles via the configuration protperty spring.profiles.active=MyProfile
The profile gets activated correctly which I can see by beans from that profile being created.
Then I have a #Controller used as follows:
#Controller
#RequestMapping("/someUrl")
#Profile("MyProfile")
public class MyController {
...
}
This controller is not instantiated and URL used in the controller are not mapped. In the same package I have another controllers which are not limited by #Profile and these get instsantiated and mapped as expected.
So is using #Profile annotation on controller something which is not compatible with spring boot? Is there other approach I should be using?
Edit: It seems to be a bug after all as if I include -Dspring.profiles.active=MyProfile as JVM property the controller gets instantiated :'(
Edit2: So here comes the interesting part:
If you define spring.profiles.active in application.properties which is loaded by default from classpath thne it works
when you rename the file to test.properties and include it via #PropertySource("classpath:test.properties") it stops working. Will raise a bug against it.
Edit 3: As promised: https://github.com/spring-projects/spring-boot/issues/1417
Thanks!
I've tracked this down to what I believe to be a bug in Spring. See SPR-12111 for more details.
You can definitely annotate a controller with #Profile in Spring Boot, just as you are doing above. MyController gets instantiated if MyProfile is active. Are you sure that "MyProfile" is the active profile? Are you setting the spring.profiles property?
http://docs.spring.io/spring/docs/3.2.x/javadoc-api/org/springframework/context/annotation/Profile.html
The #Profile annotation may be used in any of the following ways:
as a type-level annotation on any class directly or indirectly annotated with #Component, including #Configuration classes
as a meta-annotation, for the purpose of composing custom stereotype annotations

Spring MVC #Controller and profiles

I have a spring mvc controller and I want to enable it only in some profiles (for example development and test).
I know I can use the profile attribute of the beans element in xml configuration to limit the scope of my beans, but I'm using convenient annotations for the controllers now.
Can I bind the annotated controller to given profile somehow?
Or do I have to use the "old way" (of implementing and declaring controller) without annotations and use the beans element in xml configuration?
Will the annotated controllers mix well with the "old ones"?
EDIT: another way, which comes to my mind, is to check the profile in runtime from autowired Environment instance, but this denies the inversion of control
Is this what you mean ?
#Controller
#Profile("test")
public class CacheController {
}
javadoc

Categories

Resources