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.
Related
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.
I am building a REST provider with Spring Boot. It is ephemeral by design i.e. the data can be lost when the application is killed. So, I decided to use an ArrayList to make CRUD operations on: it should be like a singleton -created with the app and used along the way.
I have this rest controller, with the service layer autowired:
#RestController
public class MyController {
#Autowired
private MyServiceInterface myService;
#GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public List<MyEntity> retrieveAll() {
return myService.getAll();
}
#PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public MyEntity create(#RequestBody MyEntity entity) {
return myService.insert(entity);
}
}
and the MyService implementing MyServiceInterface is as follows:
#Service
public class MyService implements MyServiceInterface {
// This is my ArrayList to live while the Spring Boot app runs
private List<MyEntity> myList = new ArrayList<MyEntity>();
#Override
public MyEntity insert(MyEntity entity) {
this.myList.add(entity);
return entity;
}
#Override
public List<MyEntity> getAll() {
return this.myList;
}
}
So, is it fine to use a humble private myList object in the serive class as shown above, or should I take a different approach (inject that myList after assigning it as a #Bean, add #Configuration or whatsoever Spring stuff)?
EDIT: Not to sail away from my concern, my primary point is not discussing databases instead of lists, but how to declare a variable used by multiple methods of a Spring Bean.
HelloController.java
#RestController
class HelloController {
#GetMapping(value = "{id}/hello")
public ModelAndView listAPI(#PathVariable("id") String profileId) {
ModelAndView mav = new ModelAndView();
return mav;
}
}
HelloControllerTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = HelloConfigTest.class)
class HelloControllerTest {
#Inject
private WebApplicationContext webApplicationContext;
#Inject
private Foo mockFoo
#InjectMocks
HelloController helloController;
private MockMvc mockMvc;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void testHello() throws Exception {
mockMvc.perform(
get("/{id}/campaigns", "id1"))
.andExpect(status().isOk()));
}
}
// I have another test that directly calls the controller method.
// So I need #InjectMocks to get an instance of the controller
#Test
public void test2() {
when(mockFoo.getX()).thenReturn(true);
helloController.saveAPI();
}
HelloConfigTest.java
#Configuration
#ComponentScan("com.test.controller")
class HelloConfigTest {
#Bean
public mockFoo() {
return Mockito.mock(Foo.class);
}
}
The response that I get here is 404 and I expect 200.
But it works and I get 200 if I change #GetMapping to #RequestMapping(value="{id}/hello", method=RequestMethod.GET)
Am I missing anything here ?
Your configuration is extremely bare bones
#Configuration
#ComponentScan("com.test.controller")
class HelloConfigTest {
It doesn't register any Spring MVC infrastructure beans, either implicitly or explicitly.
When MockMvc, internally, creates a TestDispatcherServlet to test your #Controller class, it has to defer to some default Spring MVC infrastructure types.
Among these infrastructure types is HandlerMapping which is
to be implemented by objects that define a mapping between requests and handler objects.
The default implementation used by the TestDispatcherSerlet is DefaultAnnotationHandlerMapping (an old class) which looks for #RequestMapping specifically, it doesn't recursively do a meta-annotation lookup. Your #GetMapping annotated method is therefore not found and not registered as a handler.
If, instead, you configure your application context with #EnableWebMvc
#Configuration
#ComponentScan("com.test.controller")
#EnableWebMvc
class HelloConfigTest {
Spring will implicitly register a RequestMappingHandlerMapping, which does do this "recursive" lookup for the annotation hierarchy (called merging). Since #GetMapping is annotated with #RequestMapping, the annotated handler method will be found and registered.
As for the #InjectMocks, note that the instance referenced by the field is different from the one used to handle the request performed by the MockMvc object. The former is managed by Mockito, the latter by Spring.
I'm Working on a Spring web App with Hibernate and Spring Mvc, And I'm wondering why Autowiring works only inside of the controller
this is a simple example :
#Controller
#RequestMapping(value="SW/excel")
public class ExcelController
{
#Autowired
private BlablaService blablaService;
#RequestMapping({""})
public ModelAndView indexPage()
{
List<Blabla> blablas=BlablaService.getAllBlablas();
}
}
This code is working fine for me, it returns the list of Blablas I have in my Database.
but when I work with my BlablaService outside of the controller, it doesn't work and here is and example
#Controller
#RequestMapping(value="SW/excel")
public class ExcelController
{
#RequestMapping({""})
public ModelAndView indexPage()
{
BlablaLister lister= new ExcelExporter();
List<Blabla> blablas=lister.getList();
}
}
And here is the Excel Exporter:
Class BlablaLister {
#Autowired BlablaService blablaService;
public List<Blabla> getList()
{
return blablaService.getAllBlablas;
}
}
But I always get , NullPointerException, the getAllBlablas returns Null whenever used in the a class out of the controller.
Your BlablaLister have to be initiated via spring in order for autowiring to work
In order for the autowiring to work Spring must know about the object - either by having it instantiated in the config file or by using one of the annotations that instantiate a bean. To make it work you probably just need to add the #Component annotation to identify it as a spring-managed bean.
#Component
Class BlablaLister {
#Autowired BlablaService blablaService;
public List<Blabla> getList()
{
return blablaService.getAllBlablas;
}
}
I have a service bean capable of getting / setting property values from persistent layer (eg: database). Something like this:
#Service
public ConfigService {
public String getConfig(String key);
}
The problem is for each controller class I write I have to autowire and populate my model with the property key/values:
#Controller
#RequestMapping("/foo")
public FooController {
#Autowired private ConfigService configService;
#RequestMapping("/login")
public String login(Model model) {
model.addAttribute("site.name", configService.getConfig("site.name"));
//...
}
}
Is there any way I can automatically get the value of this property on my spring JSP view? I don't want to have to inject this to my model object for each controller class I write.
The closest I can get so far is using Spring ResourceBundleMessageSource bean and <spring:message> tags, however I am constrained to using properties file, can't store it in database.
I found another way of doing this. Use a #ControllerAdvice class combined with #ModelAttribute method. Something like this:
#ControllerAdvice
public class ConfigAdvice {
#Autowired private ConfigService configService;
#ModelAttribute
public void populateConfig(Model model) {
for(Config config : configService.getAll()) {
model.addAttribute(config.getKey(), config.getValue());
}
}
}
The #ModelAttribute annotated populateConfig() method above will run prior to any #RequestMapping method on all other controller classes.
One drawback is config key can't contain any dot characters.
So far this looks to be my best option. Please let me know if there's a better way.