-2

My application contains the following classes among others:
SpringMainApplication:

@SpringBootApplication
@ComponentScan(basePackages = {"com.foo"})
class com.foo.appl.SpringMainApplication {
... some code...
}

An interface that should be used to autowire a field:

interface com.foo.bar.ClassToAutowire {
}

And another class that uses said interface for a field:

@Component
class com.foo.appl.pack.ImplementationClass {

@Autowired
ClassToAutowire autoClass;

@Scheduled(fixedRate = 60000)
public void startStuff() {
  // do something...
  }

}

But the field won't autowire:

Field autoClass in com.foo.appl.pack.ImplementationClass required a bean of type 'com.foo.bar.ClassToAutowire' that could not be found.

Action:

Consider defining a bean of type 'com.foo.bar.ClassToAutowire' in your configuration.

I guess Spring doesn't like my package-structure?

com.foo.bar.ClassToAutowire
com.foo.appl.SpringMainApplication
com.foo.appl.pack.ImplementationClass

Does the @SpringBootApplication have to be in the root package and all components must be in subpackages? If so, how do I solve my "problem", because the ClassToAutowire comes from an imported JAR.

When changing the basePackge to com.foo.bar the application starts, but then the scheduled method won't run.

Thanks

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
eiz
  • 15
  • 1
  • 2
  • Put your `SpringMainApplication` in `com.foo` or add a `@ComponentScan("com.foo")` onto your `SpringMainApplication`. – M. Deinum Aug 22 '17 at 07:55
  • While putting `SpringMainApplication` in `com.foo` solves my problem, I would like to keep my package-structure as is... `@ComponentScan("com.foo")` is already added to the SpringMainApplication. – eiz Aug 22 '17 at 09:34
  • 1
    It is a best practice to your your starting point in the top-level package. As stated in my comment if you really don't want that (which has several disadvantages especially when starting to use more frameworks) add `@ComponentScan("com.foo")` but this will for instance not auto detect Spring Data repositories, JPA entities etc. you will need a lot of additional configuration which otherwise you get for free. – M. Deinum Aug 22 '17 at 09:36

3 Answers3

1

When using Spring Boot it by default does component scanning. This component-scanning is done starting in the same package as the @SpringBootApplication annotated class is in. In your case that is com.foo.appl however this does not cover com.foo.bar.

The best practice is to put your @SpringBootApplication annotated class in the most top-level package you can find, in your case that would be com.foo. This will scan all packages beneath it and will include the proper components.

You could also add @ComponentScan("com.foo") to your @SpringBootApplication annotated class to let it start scanning at a different package or (@SpringBootApplication(basePackage="com.foo").

If there aren't anymore components in the dependency jar you could also add a @Bean method to create an instance of the desired class.

@Bean
public ClassToAutowire classToAutowire() {
    return new ClassToAutowire();
}

The drawback of the second approach is that when using things like Spring Data or JPA you will also manually have to configure those (adding things like @EnableJpaRepositories and @EntityScan). This will grow when using/adding different frameworks, this isn't the case when you put the class in a top-level package as all packages are considered.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
0

Does the @SpringBootApplication have to be in the root package and all components must be in subpackages? No, @SpringBootApplication can be placed anywhere, but you will need to provide correct path in @ComponentScan

If implementation of ClassToAutowire class comes from external package, you will need to import spring configuration from that jar. This means define configuration class annotated with @Configuration and then import this configuration in your ImplementationClass using @Import annotation. Another woulb using spring auto-configuration described here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html

LubošCZ
  • 91
  • 6
0

You should implement interface com.foo.bar.ClassToAutowire and annotate the class with @Component

@Component Indicates that an annotated class is a "component". Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning. reference @Component documentation