20

I am starting to learn Spring Boot. I am struggling to find an example with multiple RestControllers, which indicates to me that I may be doing something wrong. I am trying a very simple example: The goal is to make calls like the following:

localhost:8080/
localhost:8080/employees/bob
localhost:8080/departments

I can only get localhost:8080/ to display. The other calls return response: This application has no explicit mapping for /error, so you are seeing this as a fallback.

com.demo.departments
Department.java
DepartmentController.java

com.demo.employees
Employee.java
EmployeeController.java

com.demo
BootDemoApplication.java

Code:

package com.demo.departments
@RestController
@RequestMapping("/departments")
public class DepartmentController {


@RequestMapping("")
public String get(){
    return "test..";

}

@RequestMapping("/list")
public List<Department> getDepartments(){
    return null;

}

}
--------------------------------------------------------------------
package com.demo.employees
@RestController
@RequestMapping("/employees")
public class EmployeeController {

Employee e =new Employee();

@RequestMapping(value = "/{name}", method = RequestMethod.GET, produces = "application/json")
public Employee getEmployeeInJSON(@PathVariable String name) {

 e.setName(name);
 e.setEmail("employee1@genuitec.com");

 return e;

}
}
-----------------------------------------------------------------------

package com.demo
@RestController
@SpringBootApplication

public class BootDemoApplication {

public static void main(String[] args) {
    SpringApplication.run(BootDemoApplication.class, args);
}

@RequestMapping("/")
String home(){
    return "<html> This is the home page for Boot Demo.</html>";
}
user1529412
  • 3,616
  • 7
  • 26
  • 42
  • I don't think you need the preceeding "/" on the top level controller mappings. – ChiefTwoPencils May 22 '16 at 05:57
  • I tried it, it didn't make a difference. – user1529412 May 22 '16 at 06:06
  • This should work (tested it out). You didn't provide any controller for `http://localhost:8080/` though, so perhaps the mistake is there. – g00glen00b May 22 '16 at 12:16
  • 1
    You are returning JSON, but aren't requesting it (the browser expect HTML) so no method for handling the request is found. – M. Deinum May 22 '16 at 14:16
  • Yes I am, Employee is a POJO. Its not calling any of the code in the other 2 classes in the debugger. It complains about mapping, not about results. – user1529412 May 22 '16 at 15:14
  • Do you see any other errors in logs or in the console? If you run via gradle, try adding `-i` option – Nikem May 22 '16 at 15:18
  • I run it as spring boot app from eclipse. It builds fine, it maps localhost:8080 fine too and return the html message. The other two controllers that produce the mapping error, when I do localhost:8080/employees/bob or localhost:8080/departments. Does anyone have a link for a similar example I can follow? I am just looking to do localhost/foo & localhost/bar, where foo and bar are separate controller classes in different packages. – user1529412 May 22 '16 at 15:34
  • @user1529412 Your example works... I created a new Spring boot project, added spring-boot-starter-web and only copy pasted the classes you mentioned above. It works. – g00glen00b May 23 '16 at 05:48
  • @g00glen00b, I am not sure how you getting it to work. When I type in http://localhost:8181/employees/bob into the browser, I get "This application has no explicit mapping for /error, so you are seeing this as a fallback.", but if I do "localhost:8181", I get the correct html response message. – user1529412 May 24 '16 at 01:51
  • I just made a new project and pasted all your code in it, it works: https://www.dropbox.com/s/acis5aem4lbpd73/demo.zip?dl=0 – g00glen00b May 24 '16 at 05:47
  • @g00glen00b, I am curious, how are you running the code, I exported it to Eclipse and ran it as spring boot project , still same problem. I think eclipse may be the problem. – user1529412 May 25 '16 at 02:31

8 Answers8

26

I'm trying Spring Boot and got same problem, and just fixed it, I post my solution here because I think it maybe helpful for someone.

First, put application class ( which contain main method) at the root of controllers's package:

com.example.demo
              |
              +-> controller
              |      |
              |      +--> IndexController.java
              |      +--> LoginController.java
              |
              +-> Application.java

Application.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Spring will scan all the components of sub-packages of demo package

IndexController.java (return index.html view)

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping(value = {""})
public class IndexController {

    @GetMapping(value = {""})
    public ModelAndView index() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("index");
        return modelAndView;
    }

}

LoginController.java (return login.html view)

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping(value = {"/login"})
public class LoginController {
    @GetMapping(value = {""})
    public ModelAndView login() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("login");
        return modelAndView;
    }
}

And now I can enter Index view : http://localhost:8080/demo/ and Login view : http://localhost:8080/demo/login

VietDD
  • 1,048
  • 2
  • 12
  • 12
13

Apparently Controllers in different packages can't be seen with @springbootApplication notation in the main class. The solution explained here, https://kamwo.me/java-spring-boot-mvc-ontroller-not-called/.

user1529412
  • 3,616
  • 7
  • 26
  • 42
4

For Spring-boot 1.3.x and up, passing a base package to SpringBootApplication should work:

@SpringBootApplication(scanBasePackages = {"com.demo"})
public class DemoBootApplication {
    // code
}

This worked for me on a similar application using spring-boot 1.4.0. For earlier versions of spring-boot, it appears you'll have forego using SpringBootApplication and instead use the following to get same effect as above:

@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.demo"})
public class DemoBootApplication {
    // code
}

I found this in the comments on this blog post.

Ian Riley
  • 457
  • 2
  • 8
  • 17
4

Make sure that the @SpringBootApplication class is in a package which is a level above all other packages that contain @RestControllers, or in the same package.

Janac Meena
  • 3,203
  • 35
  • 32
1

ComponentScan annotation works in most cases.

See below example, you could apply similar.
package com.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@ComponentScan(basePackages = {"com.demo"})
@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
}
}
Red Boy
  • 5,429
  • 3
  • 28
  • 41
0

Try this

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {

    public static void main(String[] args) {

        Object[] sources = new Object[2];
        sources[0] = Controller1.class;
        sources[1] = Controller2.class;
        SpringApplication.run(sources, args);
    }

}
0

I'm not sure if this is the right way to do it, but when I changed my 2nd Controllers annotation from @Controller to @RestController it started working.

jovanchohan
  • 143
  • 2
  • 3
-1

Try below:-

@ComponentScan
@Configuration
@EnableAutoConfiguration
public class BootDemoApplication {

public static void main(String[] args) {

    SpringApplication.run(BootDemoApplication.class);
}
}

@RestController
@RequestMapping(value = "test", produces =    MediaType.APPLICATION_JSON_VALUE)
public class TestController {

@RequestMapping(method = RequestMethod.GET)
public String test() {
    return "from test method";
}

}
Akhil
  • 1,184
  • 1
  • 18
  • 42