Confusing behavior of #service in spring - java

#Service and #Controller annotations are used for automatic bean detection using classpath scan in Spring framework.
So I tried below four use cases but I am bit confused with case 4 as it gives me 404 error.
use case 1: #Controller & class level #RequestMapping
#Controller
#RequestMapping(value = "/home")
public class MyController
{
...
}
Result:
http://localhost:8080/MyApp/home/helloWorld/va ---> Hello va
use case 2: #Service & class level #RequestMapping
#Service
#RequestMapping(value = "/home")
public class MyController
{
...
}
Result:
http://localhost:8080/MyApp/home/helloWorld/va ---> Hello va
use case 3: #Controller & no class level #RequestMapping
#Controller
public class MyController
{
...
}
Result:
http://localhost:8080/MyApp/helloWorld/va ---> Hello va
use case 4: #Service & no class level #RequestMapping
#Service
public class MyController
{
...
}
Result:
http://localhost:8080/MyApp/helloWorld/va ---> error 404
code:
#Service
#RequestMapping(value = "/home")
public class MyController
{
#RequestMapping(value = "/helloWorld/{Name}", method = RequestMethod.GET)
public #ResponseBody String HelloWorld(#PathVariable("Name") String name)
{
return "Hello "+name;
}
}
So in short when using #Service, if I dont use #RequestMapping at class level am getting 404 error.

A bean is considered a request handler if it has either #Controller or #RequestMapping at class level.

Related

Why I can replace #RestContoller with #Component and it still works?

Can someone explain why replacement of RestController annotation with Component has no any visible effect in my case?
My controller:
#RestController
#RequestMapping("/api/employees")
public class EmployeeController {
#Autowired
EmployeeService employeeService;
#PostMapping("")
public Employee saveEmployee(#Valid #RequestBody Employee employee) {
return employeeService.save(employee);
}
...
This works in the same way:
#Component
#ResponseBody
#RequestMapping("/api/employees")
public class EmployeeController {
...
The not-in-depth description of individual annotations would be:
#RequestMapping registers your class for servlet mapping.
#Component registers your class for dependency injection.
#ResponseBody will add the return value of the method to the HTTP response body.
1st case - #RestController registers your class for DI and adds #ResponseBody annotation to it, #RequestMapping registers class for servlet mapping.
2nd case - #Component registers your class for DI and you added a manual #ResponseBody annotation to it, #RequestMapping registers class for servlet mapping.
Both of the cases above do the same thing, so that's why it 'just works'.

Spring MVC RequestMapping not working on RestController

I want to have a RestController-class with the base-mapping "/user" (so the different functions will have paths like "/user/add", "/user/remove" etc or use POST/GET etc)
This is the part that I don't understand and can't get to work:
#RestController
public class UserController {
#GetMapping("/user")
public Response login(Principal principal){
//some output
}
}
Expected behavior for this case would be that I can access my output under "/user". This works as expected.
Now if I modify it to the following (since all functions in this controller should have a path starting with "/user" this would be cleaner)
#RestController
#RequestMapping("/user")
public class UserController {
#GetMapping("/")
public Response login(Principal principal){
//some output
}
}
I get a 404-Error page and can't access "/user" anymore
All examples I have found use the same syntax (or sometimes #RequestMapping(path="/user") but that didn't work as well) and I don't know why it doesn't work.
Can someone tell me where my mistake is?
If you use this code:
#RestController
#RequestMapping("/user")
public class UserController {
#GetMapping("/")
public Response login(Principal principal){
//some output
}
}
Then your url should have "/" at the end like "http://localhost:8080/user/"
I would just throw away "/" symbol from #GetMapping("/") and left like this:
#RestController
#RequestMapping("/user")
public class UserController {
#GetMapping
public Response login(Principal principal){
//some output
}
}
And if you need map get or post you can use like this:
#RestController
#RequestMapping("/user")
public class UserController {
#GetMapping("/add")
public SampleObj getJson() {
return new SampleObj();
}
}
It should work.

How to ignore level #RequestMapping() and direct call method level #RequestMapping()?

How to do I ignore the class level #RequestMapping("/home") and directly call the method level #RequestMapping("/users") in Spring?
#Controller
#RequestMapping("/home")
#RequestMapping("/method1")
public void method1(){
...
}
#RequestMapping("/users")
public void listUsers(){
...
}
I want to call http://localhost:8080/users to invoke listUsers() method.
You cannot bypass requestmapping defined at class level. for If so why you want a class level mapping then... you can instead do something like this in the method level request mapping
#Controller
#RequestMapping("/home/method1")
public void method1(){
...
}
#RequestMapping("/users")
public void listUsers(){
...
}
In that case you may try this...
#Controller
#RequestMapping({ "/home", "/users" })
#RequestMapping("/method1")
public void method1(){
...
}
#RequestMapping(method="RequestMethod.GET")
public void listUsers(){
...
}
Change #RequestMapping("/users") for #RequestMapping(method=RequestMethod.GET)
From my understanding, what you expect is a class-level RequestMapping. The method-level RequestMapping should be under the path of class-level's.
I'll give you some examples:
#RestController
#RequestMapping("/home")
public class HomeController {
// Full path of following endpoint: /home/parents.
#RequestMapping(value = "/parents", method = RequestMethod.GET)
public ResponseEntity<List<People>> getParents() {
return ResponseEntity.ok(methodToGetParents());
}
For the path of "users" you should do it in another class (controller):
#RestController
#RequestMapping("/users")
public class UsersController {
// Full path of following endpoint: /users.
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<People>> getUsers() {
return ResponseEntity.ok(methodToGetUsers());
}

Spring MVC Using Autowired Object Outside Controller Class

i'm using Spring MVC for my project. Is it possible to use the same autowired object on another class?
I have two controllers, one for navigation and one for the operations.
this is my sample navigation controller:
#Controller
public class NavigationController {
#Autowired
private DepartmentDAO deptDao;
#RequestMapping("/")
public ModelAndView panel() {
ModelAndView mv = new ModelAndView("panel");
Department dept = deptDao.getDept();
mv.addObject("department",dept);
return mv;
}
}
then my operations controller:
#Controller
public class OperationController {
#RequestMapping(value = "/save.do", method = RequestMethod.POST)
public ModelAndView saveEvent(){
ModelAndView mv = new ModelAndView("next");
Event event = new EventCreator().createEvent();
//some logic here
return mv;
}
}
And this is my business delegate class :
public class EventCreator {
public Event createEvent(){
//logic here
//I need to use the deptDao here.
}
}
Thank You So Much!
You can simply Autowire DepartmentDAO in the EventCreator class, like how your autowired it in NavigationController class. Make sure that you annotate the EventCreator class with #Component and include it in the package where component scanning is done so that spring will autowire the DepartmentDao in your EventCreator class.
#Component
public class EventCreator {
#Autowired
private DepartmentDAO deptDao;
public Event createEvent(){
//logic here
//I need to use the deptDao here.
//deptDao.getAllDepartments();
}
}
You can #Autowired a spring bean object in another spring bean object
Assumption
I assume that you have declared DepartmentDAO with #Repository
annotation as you haven't include code of DepartmentDAO in your
question
There are two ways to solve your problem ,
one is very well explain by #TimeTravel annotate EventCreator with
#Component with create spring bean and you can easy autowire
DepartmentDAO in the EventCreator class
As You have two controller class which makes it spring beans as they are annotated with #Controller, what you can do you can Autowire
departmentDAO in OperationController class and pass instance of
DepartmentDAO as argument in EventCreator class constructor
#Controller
public class OperationController {
#Autowired
private DepartmentDAO deptDao;
#RequestMapping(value = "/save.do", method = RequestMethod.POST)
public ModelAndView saveEvent(){
ModelAndView mv = new ModelAndView("next");
Event event = new EventCreator(deptDao).createEvent();
//some logic here
return mv;
}
}
public class EventCreator {
private DepartmentDAO deptDao=null;
public EventCreator(DepartmentDAO deptDaoAsArg){
deptDao=deptDaoAsArg;
}
public Event createEvent(){
//logic here
//I need to use the deptDao here.
}
}

Spring MVC multiple controllers of the same class

I want to have one controller class, but 4 instances of it, each of instance will have own datasource and controller path, everything else (methods, validations rules, views names) will be the same;
So i need something like this :
class MyController{
private MyService service;
#RequestMapping("somework")
public String handleRequest(){
........
}
....................
}
Configuration class :
#Configuration
#EnableWebMvc
public class AppConfiguration {
#Controller // assuming it exists to get the
#RequestMapping('con1') // desired result
MyController controller1(){
MyController con = new MyController();
con.setService(service1Bean);
return con;
}
#Controller // assuming it exists to get the
#RequestMapping('con2') // desired result
MyController controller2(){
MyController con = new MyController();
con.setService(service2Bean);
return con;
}
...............................
}
No, you can't do this.
First, annotations are a set in stone at compile time. They are constant meta data that you cannot modify. So even though, they are accessible at run time through reflection, you cannot modify them.
Second, the #Controller annotation call only be used to annotate types. You cannot use it on a method. There is no corresponding annotation in Spring MVC that does what you want in your example. (You could always write your own.)
Finally, the Spring MVC stack registers your #Controller beans' methods as handlers mapping them to the various URL patterns you provide. If it tries to register a pattern that has already been registered, it fails because duplicate mappings are not allowed.
Consider refactoring. Create a #Controller class for each path you want but move the logic to a #Service bean which you can customize to use whatever data source you need.
You may achieve what you want by implementing an abstract superclass of
your controller, with constructor parameters for your service.
Then you should write derive your controllers from the abstract superclass,
with a constructor, where you inject your concrete service implementation:
public abstract class MyBaseController {
private MyService service;
public MyBaseController(final MyService service) {
this.service = service;
}
...
#RequestMapping("method1")
public ... method1( ... ) {
...
}
}
#Controller
#RequestMapping("con1")
public MyController1 extends MyBaseController {
#Autowired
public MyController1(#Qualifier("con1") final MyService service) {
super(service);
}
}
#Controller
#RequestMapping("con2")
public MyController2 extends MyBaseController {
#Autowired
public MyController1(#Qualifier("con2") final MyService service) {
super(service);
}
}
#Configuration
public class MyConfiguration {
#Bean(name = "con1")
public MyService serviceCon1() {
return ...;
}
#Bean(name = "con2")
public MyService serviceCon2() {
return ...;
}
}

Categories

Resources