Spring MVC #Controller and profiles - java

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

Related

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.

Can you use scoped proxies with Spring Boot 2 #ConfigurationProperties?

Using Spring Boot 1.5.12, we create scoped proxies for #ConfigurationProperties beans. We do this so that we can effectively have property values that are scoped to the user/session/etc. We use a BeanFactoryPostProcessor to register a scoped proxy created by ScopedProxyUtils.createScopedProxy and change the scope of the target bean definition to our custom scope.
This worked great in Spring Boot 1.5.12. However, in Spring Boot 2, the introduction of the Binder API has made this stop working. This is because when Spring Boot binds #ConfigurationProperties in its ConfigurationPropertiesBindingPostProcessor, it uses Bindable.of(type).withExistingValue(bean) passing in a type (e.g. org.example.Foo) and the bean which is an instance of ScopedProxyFactoryBean. Bindable checks that bean is an instance of type which it's not and things blow up.
Does anyone know of a general way to accomplish this? Specifically, to replace the #ConfigurationProperties bean definition with a proxy while still allowing the properties to bind to the instance? I've considered delaying the creation of the proxy until after the target has already been bound by ConfigurationPropertiesBindingPostProcessor (i.e. in my own BeanPostProcessor). However, at this point the bean is instantiated and my proxy can only replace the bean. It can't really leave the original bean in the context as a target. So I'm struggling with how to do this using a BeanPostProcessor.
EDIT: I've created a simple example project that demonstrates the issue (with the ability to check out code that works on Spring Boot 1 and fails on Spring Boot 2).

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

Autowired Dependency Injection with Spring Security

I have a spring webapp with annotation driven configuration.
All Controllers, Repositories are autowired.
When integrating Spring Security I defined a separate security-app.xml. I created a Service called LoginUserService which implements UserDetailsService. Now the method loadUserByUsername() method of this class gets invoked for authentication.
This class has an autowired dependency for UserRepository. Now this autowired dependency turns out to be null. To fix this I enable annotation driven configuration and add the package name for the repository class in component scan configuration.
This solution is also discussed here
spring security with custom user details
But now the problem is that the UserRepository has an EntityManager field with #PersistenceContext annotation. For the spring security configuration it is able to locate the UserRepository but not able to locate the entity manager. Should I create a new EntityManagerFactory here? I guess that will create two persistence units in my application?
How can I inject an autowired dependency to UserRepository created with the original servlet xml?
Update
This is briefly discussed here:
https://stackoverflow.com/a/7078395/161628
But I guess a canonical detailed answer will be more useful to me.
Update
How about using ApplicationContext to get the UserRepository at runtime?
if (userRepository == null) {
userRepository = ApplicationContextProvider.getApplicatonContext().getBean(UserRepository.class);
}
Why is Spring's ApplicationContext.getBean considered bad?
EDIT: Beans that you declare in your config for your DispatcherServlet are not going to be available to any beans you declare or component scan in your contextConfigLocation config files. So in this case, if you're setting up your JPA config in your config file that you load for your DispatcherServlet there is no way to wire that in to beans your declare in your security config. You need to move any "core" bean config like that (datasource config, db connection pool config, JPA/Hibernate config, repository/service component scanning, etc.) into a config file that you load via the contextConfigLocation. Then that stuff will be available both to your security beans and your MVC beans. I think generally the idea is to only load MVC specific beans in your DispatcherServlet config (e.g. Controllers, views, request handlers, request scoped beans, etc.). That way you ensure you have a clean separation between MVC code and non-MVC code, with only a one-way dependency from the MVC code to the "core" code, and no dependencies on MVC code in your "core" code. This helps make your code more modular, and makes it easier to reuse your "core" code in other ways, specifically in unit tests.
(Original comment text was asking about how the security config is loaded, if it's in the contextConfigLocation or somewhere else.)

What is #Service in Spring MVC

If I use #Service on a service class, do I need to make a service class bean in my servlet xml file or do I have to do both?
You don't have to declare a bean in your context file if you:
1) Annotate the class with:
#Component, #Service, #Controller or #Repository
2) Include the context:component-scan element in your context file like this:
<context:component-scan base-package="your.package" />
Hope that helps.
Last time I looked (Spring 2.5) #Service was a marker annotation subclassed from #Component, but with no additional behaviour. Which means that beans tagged with #Service become candidates for auto detection if you are using annotation-based configuration via classpath scanning.
As per the docs, the intention is that this annotation might include service layer specific functionality in future Spring releases. It can also act as an AOP point cut for all of your service layer components.

Categories

Resources