How to split Service/Controller layer classes into interface and impl? - java

I have the following controller class in my Spring boot project, split into interface and implementation:
public interface UserAccountController {
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(#RequestBody UserAccountEntity account,
HttpServletResponse response) throws IOException;
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public String create(#Valid #RequestBody UserAccountEntity userAccount,
HttpServletResponse response, BindingResult result);
}
#RestController
#RequestMapping("/api/authentication")
public class UserAccountControllerImpl implements UserAccountController {
#Autowired
private UserAccountService userAccountService;
#Override
public String login(#Valid #RequestBody UserAccountEntity account,
HttpServletResponse response) throws IOException {
//...
}
#Override
public String create(#Valid #RequestBody UserAccountEntity userAccount,
HttpServletResponse response, BindingResult result) {
//....
}
}
When I move RestController and RequestMapping annotations to the interface, it doesn't work. But annotating the methods on the interface work. How are these two annotations different?

#RestController inherits from #Controller, which inherits from #Component thus leading to the creation of a Spring Bean in your application context.
#RequestMapping inherits from #Mapper and is used to mark Rest- or Web-Controller methods as handler methods.
As to why Spring is implemented to disallow inheritance on the first and allow it on the second, I can only speculate:
I think your example constitutes a useful usecase for #Mapping inheritance, since you could have several RestControllers with different url prefixes, but other than that the same endpoints.
Making #Component annotations inheritable could lead to involuntarily created Spring Beans, since clients might fail to notice the annotation when implementing the interface.

Related

Annotation on interface in Spring Boot

My interface:
package com.demo.dependency;
#RestController
#RequestMapping(value = "#{'${api.baseUrl}'}")
public interface BaseController<Response> {
#PostMapping(value = "#{'${api.interface}'}")
#ResponseBody
public Response process(#RequestBody #Valid Request request) throws Exception;
}
Implementation:
package com.demo.application;
public class BarController implements BaseController<BarResponse> {
#Override
public BarResponse process(Request request) throws Exception {
// do something
}
}
I'm new to Spring Boot. I wonder whether these annotations can work properly in implementation class:
#RestController and #RequestMapping on interface class
#PostMapping and #ResponseBody on interface method
#RequestBody and #Valid on method parameters
#{'${api.baseUrl}'} and #{'${api.interface}'} to read configure from application.properties
My spring boot version is 2.2.6.
It seems that my SpringApplication from package com.demo.application failed to auto scan BarController. However, this answer says that "the annotation should apply to all subclasses", including #Service. Is anything wrong with my code?
Thanks in advance.

How to write unit test case with mockito for this controller class

This is my controller class. Now that i want to write unit test cases for the below controller class using mockito
can anyone help me out from this?
#Controller
public class LoginController {
#Autowired
#Qualifier("skillService")
private SkillService skillService;
#Autowired
private SkillReferenceData skillReferenceData;
#Autowired
private EmployeeValidator employeeValidator;
#RequestMapping(value = "/loginview.html", method = RequestMethod.GET)
#PreAuthorize("hasAuthority('ROLE_ANONYMOUS')")
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse respresultonse) throws Exception {
ModelAndView model = new ModelAndView("login");
return model;
}
#RequestMapping("/login.htm")
protected ModelAndView onSubmit(#ModelAttribute("userVB") UserVB userVB,
BindingResult result, HttpServletRequest request,
HttpServletResponse response) throws Exception {
return new ModelAndView("login");
}
}
Create an instance of your Controller class by:
#InjectMocks
LoginController loginController;
By using this annotation you can also access and mock your private variables like
skillService, skillReferenceData, employeeValidator by using:
#Mock(name = "skillService")
SkillService mockSkillService = createMock(SkillService.class);
Don't forget to initialize your Mockito annotations by adding MockitoAnnotations.initMocks(this); before your unit tests.
Finally, you can mock your constructors by using:
Mockito.when(new ModelAndView(any(String.class).thenReturn(null)));

Add dynamically-created Spring bean instance to application context for autowired dependency

I need to create a new EmployeeInfoCache instance (not a singleton) from info in the HttpServletRequest that is used to get info from an external app. I then want to give this object as a dependency to non-web-layer objects (where it will be set for all #Autowired references). EmployeeInfoCache itself has no web-layer dependencies (e.g. HttpServletRequest).
Can this be done? I thought about writing a spring interceptor which does the following but I don't know what to do to put an object in the spring context such that it will be used to resolve all #Autowired dependencies.
e.g.
public class MyInterceptor extends HandlerInterceptorAdapter
{
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception
{
- use info from HttpServletRequest to make calls to external app
- create EmployeeInfoCache object w/ this info
- add EmployeeInfoCache to spring application context where it will be used for resolution of #Autowired
}
}
And the remaining code:
// Assume don't have 'Component' or a similar annotation?
public class EmployeeInfoCache
{
...
}
// REST controller that calls the business logic method
#Controller
MyController
{
#Autowired
private MyBusinessObjectInterface myBusinessObject;
#RequestMapping(...)
public #ResponseBody MyResult myMethod(#RequestBody MyObject myObject, HttpServletRequest request, HttpServletResponse response)
{
myBusinessObject.doIt();
}
}
// Non-web-layer code that uses EmployeeInfoCache
#Service(...)
MyBusinessObject implements MyBusinessObjectInterface
{
// I want the EmployeeInfoCache instance created in MyInterceptor to be autowired here
#Autowired
private EmployeeInfoCache employeeInfoCache;
#Override
public void doIt()
{
employeeInfoCache.getName();
}
}
It sounds like you want to use a factory pattern. Have a Spring bean which is the factory method that returns the Cache.
http://kh-yiu.blogspot.in/2013/04/spring-implementing-factory-pattern.html

Spring #Controller annotation and Java based configuration

I have a Spring Java configuration and I'd like to build 2 instances of the same controller
#Bean(name = CONTROLLER_A)
public MyController getMyAController() {
return new MyController(new A());
}
#Bean(name = CONTROLLER_B)
public MyController getMyBController() {
return new MyController(new B());
}
public class MyController {
...
#RequestMapping(method = RequestMethod.GET)
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response){
...
}
}
There is no way to annotate the methods as #Controller and without it, Spring doesn't consider that instance as Controller instances (the exception handling doesn't work properly).
Is there a way to get the controller work properly w/o using xml configuration?
EDIT: the only way I can make it works is by extending AbstractController, but I don't really want to use inheritance.

Gwt Controller With Annotation Approach

I already integrated GWT with Spring MVC by implementing the Controller which is called
by DispatcherServlet using SimpleUrlHandlerMapping.
public class GwtRpcController extends RemoteServiceServlet implements Controller,
ServletContextAware {
#Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
super.doPost(request, response);
return null;
}
}
I want to use the new approach with the annotation #Controller, like below:
#Controller
public class GwtRpcController extends RemoteServiceServlet implements
ServletContextAware {
}
In this case there will be no handleRequest method, where should I do super.doPost(request, response); ?
See annotated web mvc controllers in spring 2.5
You should not to do doPost/doGet manually, just define controller method with corresponding parameters, return value and annonations for url mapping.

Categories