12

I am trying to implement simple demo MVC app with Spring Boot but I get 404 error while executing the application. The uri is `http://localhost:8080/' which is to display all the rows in a table called circle.

  • Spring Boot : 1.3.3.RELEASE
  • Java Version : 1.8.0_65
  • Database : Apache Derby 10.12.1.1

Maven Java Project:

Maven Java Project Structure

Application.java

package com.nomad.dubbed.app;

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);
    }

}

CircleController.java

package com.nomad.dubbed.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.nomad.dubbed.dao.CircleService;
import com.nomad.dubbed.model.Circle;

@RestController
@RequestMapping("/")
public class CircleController {
    @Autowired
    private CircleService circleService;

    @RequestMapping(method=RequestMethod.GET)
    public List<Circle> getAll() {
        return circleService.getAll();
    }

}

CircleRepository.java

package com.nomad.dubbed.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.nomad.dubbed.model.Circle;

@Repository
public interface CircleRepository extends JpaRepository<Circle, Integer> {

}

CircleService.java

package com.nomad.dubbed.dao;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.nomad.dubbed.model.Circle;

@Service
public class CircleService {
    @Autowired
    private CircleRepository circleRepository;

    @Transactional(propagation=Propagation.REQUIRED)
    public List<Circle> getAll(){
        return circleRepository.findAll();
    }

}

Circle.java

package com.nomad.dubbed.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="circle")
public class Circle {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;
    private String name;

    public Circle(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

application.properties

spring.datasource.url=jdbc:derby://localhost:1527/db
spring.datasource.driverClassName=org.apache.derby.jdbc.ClientDriver

logging.level.org.springframework.web:DEBUG
logging.level.org.hibernate:DEBUG

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.nomad.dubbed</groupId>
  <artifactId>spring-boot-mvc</artifactId>
  <version>0.0.1-SNAPSHOT</version>


    <properties>
        <derby-client.version>10.11.1.1</derby-client.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
        <relativePath />
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-remote-shell</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derbyclient</artifactId>
            <version>${derby-client.version}</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>spring-boot-mvc</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Database is up and running, there are 5 rows in the table circle:

enter image description here

The default uri (/beans, /health..) works fine but the implemented controller is not recognized. There is no error of such displayed in the console, below is the dump of logs printed in console after I send the request.

2016-05-03 14:17:26.594 DEBUG 659 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/]
2016-05-03 14:17:26.596 DEBUG 659 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /
2016-05-03 14:17:26.596 DEBUG 659 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Did not find handler method for [/]
2016-05-03 14:17:26.597 DEBUG 659 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping  : Matching patterns for request [/] are [/**]
2016-05-03 14:17:26.597 DEBUG 659 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping  : URI Template variables for request [/] are {}
2016-05-03 14:17:26.597 DEBUG 659 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapping [/] to HandlerExecutionChain with handler [ResourceHttpRequestHandler [locations=[ServletContext resource [/], class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver@6c13019c]]] and 1 interceptor
2016-05-03 14:17:26.597 DEBUG 659 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/] is: -1
2016-05-03 14:17:26.597 DEBUG 659 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2016-05-03 14:17:26.597 DEBUG 659 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Successfully completed request
2016-05-03 14:17:26.597 DEBUG 659 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/error]
2016-05-03 14:17:26.600 DEBUG 659 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /error
2016-05-03 14:17:26.600 DEBUG 659 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)]
2016-05-03 14:17:26.600 DEBUG 659 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/error] is: -1
2016-05-03 14:17:26.601 DEBUG 659 --- [nio-8080-exec-3] o.s.w.s.v.ContentNegotiatingViewResolver : Requested media types are [text/html, text/html;q=0.8] based on Accept header types and producible media types [text/html])
2016-05-03 14:17:26.601 DEBUG 659 --- [nio-8080-exec-3] o.s.w.s.v.ContentNegotiatingViewResolver : Returning [org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$SpelView@2f5f8d71] based on requested media type 'text/html'
2016-05-03 14:17:26.601 DEBUG 659 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Rendering view [org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$SpelView@2f5f8d71] in DispatcherServlet with name 'dispatcherServlet'
2016-05-03 14:17:26.601 DEBUG 659 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Successfully completed request 

Rest App Browser

gkc123
  • 512
  • 1
  • 7
  • 24

11 Answers11

27

use a different url for your controller. "/" in spring-boot maps to static resources located in META-INF/resources and src/main/resources/static/ .

edit: forget above and do the following in your application class:

Application.java

package com.nomad.dubbed.app;

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

@SpringBootApplication
@ComponentScan("com.nomad.dubbed")
public class Application  {

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

}

your rest controller is not discovered by spring-boots component scan. according to this doc http://docs.spring.io/spring-boot/docs/current/reference/html/… spring scans the packages below the package where the class with the @SpringBootApplication annotation resides. your controller is located in a parallel package.

Lutz Müller
  • 574
  • 3
  • 8
  • @lume I changed it to `/circles`, still same error. `@RequestMapping(value="/circles", method=RequestMethod.GET, produces={"application/json","application/xml"}) @ResponseBody public List getAll() { return circleService.getAll(); }` – gkc123 May 03 '16 at 18:49
  • 2
    damn, strange. another thought: spring-boot logs all mapped urls on startup. could you post the startup log messages too? – Lutz Müller May 03 '16 at 18:56
  • 5
    i think i found your problem: your rest controller is not discovered by spring-boots component scan. according to this doc https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-structuring-your-code.html spring scans the packages below the package where the class with the @SpringBootApplication annotation resides. your controller is located in a parallel package. – Lutz Müller May 03 '16 at 19:02
  • I think so too. This is just a simple Rest Controller, it should be mapped unless I am doing something wrong. – gkc123 May 03 '16 at 19:07
  • @lume it fails to compile with addition of `@ComponentScan("com.nomad.dubbed")`. Error: ` org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circleController': Injection of autowired dependencies failed` – gkc123 May 03 '16 at 19:26
  • this means spring tries to instantiate the controller now. yay! the "injection fails" means there is a problem with the beans to be injected. maybe the repository bean beacuse the JpaEntity needs a default constructor? could you provide a stacktrace? – Lutz Müller May 03 '16 at 19:32
  • org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circleController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.nomad.dubbed.dao.CircleService com.nomad.dubbed.controller.CircleController.circleService; nested exception is org.springframework.beans.factory.BeanCreationException: – gkc123 May 03 '16 at 19:54
  • Error creating bean with name 'circleService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.nomad.dubbed.model.CircleRepository com.nomad.dubbed.dao.CircleService.circleRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: – gkc123 May 03 '16 at 19:54
  • No qualifying bean of type [com.nomad.dubbed.model.CircleRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} – gkc123 May 03 '16 at 19:54
  • does it say anything about a failure creating the repository bean? for an example using spring-data and spring-boot, see here: https://spring.io/guides/gs/accessing-data-jpa/ . also could you mark my answer as correct, since it solves your original problem? – Lutz Müller May 03 '16 at 20:09
  • thank you for pointing to the right direction. I finally had it working with your help by putting all the classes in one package. However, `@ComponentScan("com.nomad.dubbed")` failed to compile the java project. I have to figure out why spring boot failed to recognize the controller with the original package structure. – gkc123 May 03 '16 at 20:54
  • move `Application` to `com.nomad.dubbed`. boom, problem solved. – runderworld Oct 07 '20 at 03:30
12

We should not use @ComponentScan annotation with @SpringBootApplication, as that's not the right practice. @SpringBootApplication is a combination of 3 annotations: @ComponentScan, @EnableAutoConfiguration and @Configuration.

Main class which has the @SpringBootApplication annotation should be in parent/super package.

e.g. - com.spring.learning is a parent package and its children are com.spring.learning.controller, com.spring.learning.service, com.spring.learning.pojo

Hence it scans its package and sub packages.

This is the best practice – project layout or structure is a prominent concept in Spring Boot.

Sébastien Le Callonnec
  • 26,254
  • 8
  • 67
  • 80
  • this is a correct answer, but unfortunately it's written in a way that fails to grab the reader's attention. Perhaps bolding your answer **move `Application` up to `com.nomad.dubbed` package** and then putting the lengthy explanation below it. – runderworld Oct 07 '20 at 03:31
  • your answer really make sense. I was facing same problem . Once read your answer and apply that by changing package . It works fine. Make sure controller package should be under base package. – Muhammad Bilal Mar 01 '23 at 11:50
5

This what happens behind.

@SpringBootApplication annotation is a combination of @Configuration @EnableAutoConfiguration @ComponentScan.

@ComponentScan without arguments tells the framework to find components/beans in the same package and its sub-packages.

Your Application class which is annotated with @SpringBootApplication is in the package com.nomad.dubbed.app. So it scans that package and its sub-packages under it (like com.nomad.dubbed.app.*). But your CircleController is inside package com.nomad.dubbed.controller which is not scanned by default. Your repositories too fall outside the default scan packages, so they too will not be discovered by spring framework.

So what to do now?, you have two options.

Option 1

Move the Application class to the top directory(package). In your case com.nomad.dubbed package. Then, since all controllers and other repositories are in sub-packages, they will be discovered by the framework.

Option 2

Use @ComponentScan annotation with basePackages argument, along with the @SpringBootApplication in your Application class like below.

@SpringBootApplication
@ComponentScan(basePackages="com.nomad.dubbed")
public class Application  {

    public static void main(String[] args){
        SpringApplication.run(Application.class, args);
    }
}
Johna
  • 1,836
  • 2
  • 18
  • 29
2

please check that the your controller classes are in sub packages.

For example, if the main class is com.myapp package, then controller class is either in the same package or is in sub package like com.myapp.controllers. Spring Framework will scan root package and then all its sub packages. In this case things will just work and you would not need to use @ComponentScan.

If you place the main class in com.myapp and other beans/controllers which you want to be autowired you place in a different package like com.beans which is not a sub package of com.myapp, then you will experience an issue when beans could not be found.

Thanks! Bageeradha

BAGEERADHA
  • 21
  • 1
1

The issue is Spring boot scans the package where the file with main method is located.

In my case: demo.example package. So, the controllers should be created inside this package only to be scanned. enter image description here

When the main file was under com.example.demo package, it was not identifying StudentController.

Nisha Sinha
  • 150
  • 1
  • 5
0

Could you try adding the @ResponseBody Annotation

@RequestMapping(method=RequestMethod.GET)
@ResponseBody
    public List<Circle> getAll() {
        return circleService.getAll();
    }
rjdkolb
  • 10,377
  • 11
  • 69
  • 89
jstuartmilne
  • 4,398
  • 1
  • 20
  • 30
  • I modified the code as you have suggested, no luck. I still get `Did not find handler method for [/]` error. @ACV application path is / `@RequestMapping(value="/", method=RequestMethod.GET, produces={"application/json","application/xml"}) @ResponseBody public List getAll() { return circleService.getAll(); }` – gkc123 May 03 '16 at 18:43
  • As a comment sugest try taking smaller steps. Does returning a list of Strings work? – jstuartmilne May 03 '16 at 19:04
0

I have to research more why spring - boot failed to recognize controller with the original package structure. I dumped all the java classes into one package and finally got the demo project running.

Modified Java Project Structure:

Modified Java Project Structure

The CircleController.java class was also modified. I have all the records deleted from the circle table without mentioning specific Request Method, method=RequestMethod.GET.

package com.nomad.dubbed.app;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CircleController {
    @Autowired
    private CircleService circleService;

    @RequestMapping(value="/circles", method=RequestMethod.GET)
    public List<Circle> getAll() {
        return circleService.getAll();
    }

}
gkc123
  • 512
  • 1
  • 7
  • 24
0

I had the same problem and I added @ComponentScan(basePackages = "package.name") in the Application class. After that my rest controller was recognized.

package com.nomad.dubbed.app;

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

@SpringBootApplication
@ComponentScan(basePackages = "com.spring.basepkg")
public class Application  {

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

}
mani
  • 1,091
  • 1
  • 7
  • 3
0

In my opinion, this visibility problem comes when we leave the component scan to Spring which has a particular way of looking for the classes using standard convention. In this scenario as the Starter class(Application)is in com.nomad.dubbed.app package, putting Controller one level below will help Spring to find the classes using the default component scan mechanism. Putting CircleController under com.nomad.dubbed.app.controller should solve the issue.

Chinmay Biswal
  • 303
  • 5
  • 9
0

I had a similar issue. Adding the annotation @SpringBootApplication(scanBasePackages={"com.nomad.dubbed"}) on the Application class worked for me.

Pooja
  • 63
  • 1
  • 7
0

actually springboots scans all of its components under you core package like example:

package com.nomad.dubbed.app;

if you add controllers, services, dao packages under com.nomad.dubbed.app.controllers, com.nomad.dubbed.app.services, com.nomad.dubbed.app.dao.

then you can easily run your rest controller, but if you add all packages parallel to your core springboot package like com.nomad.dubbed.controllers, com.nomad.dubbed.services.

then you need to scan for @ComponentScan({"com.nomad.dubbed.controllers","com.nomad.dubbed.services"})

if you choose to go for componentscan then you have to also scan for springboot application package as well.

so best way is create all the packages under spring boot application dubbed.app.xyz...

gnat
  • 6,213
  • 108
  • 53
  • 73
Aks00747
  • 1
  • 1