#ComponentScan in Spring (not Boot) doesn't scans subpackages - spring

My project is situated in package home.bank_application. There are a lot of subpackages, such as 'controller', 'dao', 'service', 'config' etc...
When I annotated my WebConfigClass as #ComponentScan(basePackages = "home.bank_application") my subpackages aren't scaned.
When I annotated my WebConfigClass as #ComponentScan(basePackages = {"home.bank_application.controller", "home.bank_application.dao","home.bank_application.service", "home.bank_application.config"}), all these packages are scaned fine.
But when I annotated my WebConfigClass as #ComponentScan(basePackages = "home.bank_application") my subpackages aren't scaned.
What is the problem?

I found the solution of this problem.
#ComponentScan(basePackages = "home.bank_application")
and
#ComponentScan(basePackages = "home.bank_application.*")
NOT the same things.
All problem was in * in the end of path!!!

Related

Spring boot not scanning components and configurations

I have a multi-module project:
TOP_LEVEL
|-> core
|-> assetManager
'-> requestManager
So, I have a core module which has Application class in the core module.
In my assetManger build.gradle I compile(project(:core))
The application main class is in package : com.test.companydomain.core of the core module.
This Application class is annotated with
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.test.companydomain"})
#EntityScan(basePackages = {"com.test.companydomain", "com.test.companydomain.assetManager"})
#EnableJpaRepositories
class Application {
}
There is a class ClientUtils in assetManager module in the package : com.test.domain.assetManager.server.common.utils;
annotated with :
#Slf4j
#Configuration
#Component("clientUtils")
There are beans that I am creating in this class and It uses other configuration classes for autowiring and creating beans.
The beans are not getting generated as of now from this ClientUtils class.
What can be a possible issue with this?
The error i see is
APPLICATION FAILED TO START
Description:
Field locationService in com.test.companydomain.assetManager.server.vendor.converter.ExternalVendorPojoConversionHelper required a bean of type
'com.test.companydomain.assetManager.server.common.utils.client.LocationService' that could not be found.
This class LocationService is also annotated with #Component for spring to create its bean.
In your application , the main class is present in the package com.test.companydomain.core and by default springboot scans all classes and packages under the current package of the main application for autowiring beans. So , you have provided the annotation #ComponentScan to explicitly tell spring to scan other packages as well.But your util class is in the package com.test.domain.assetManager.server.common.utils , which is not included in the #ComponentScan annotation, so it is not taken up for component scanning.
Could you try adding the package com.test.domain to the component scan in main class like :
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.test.companydomain","com.test.domain"})
#EntityScan(basePackages = {"com.test.companydomain", "com.test.companydomain.assetManager","com.test.domain"})
#EnableJpaRepositories
class Application {
}
If you are using Spring Boot, you should consider using #SpringBootApplication annotation and configure base packages to scan with scanBasePackages instead of #ComponentScan:
package com.test.companydomain.core;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages = "com.test.companydomain")
#EntityScan({"com.test.companydomain", "com.test.companydomain.assetManager"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
By default, Spring Boot scans only package of the class annotated with #SpringBootApplication and its sub-packages.
Also, #Configuration is meta-annotated with #Component, so #Configuration classes are candidates for component scanning and should not be explicitly annotated with #Component:
package com.test.domain.assetManager.server.common.utils;
#Configuration
public class ClientUtils {
#Autowired
private ClientProperties properties;
#Bean
public TestClient testClient() {
return new TestClient(properties); //example
}
}
and
package com.test.companydomain.assetManager.server.common.utils.client;
#Component
public class LocationService {
//...
}

#ComponentScan in application class breaks #WebMvcTest and #SpringBootTest

I am creating tests with #WebMvcTest annotation and found that if I have a #ComponentScan annotation in the application class it will break the expected behavior of the tests.
According to the WebMvcTest javadoc:
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. #Controller, #ControllerAdvice, #JsonComponent Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not #Component, #Service or #Repository beans)."
The problem is that with #ComponentScan it is instantiating beans annotated with #Service. If instead of #ComponentScan I specify the scan base packages in the #SpringBootApplication annotation everything works as expected.
Another problem happens when I specify the controller class in the #WebMvcTest annotation. When there is a #ComponentScan annotation in the application class it will load all controllers instead of loading only the specified one.
Is this a bug in Spring Boot?
I want to use #ComponentScan because of the excludeFilters attribute which is not available in the #SpringBootApplication annotation.
A workaround I have found is to create a separate class with #Configuration annotation and move the #ComponentScan there.
Found the reason for this odd behavior.
This is the declaration of the #SpringBootApplication annotation:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan(excludeFilters = {
#Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public #interface SpringBootApplication {
As you can see the #ComponentScan annotation inside the #SpringBootApplication has the excludedFilters attribute specified.
When I added the #ComponentScan annotation directly in my application class I did not specify the default excludedFilters and that is why the behavior was different.

Spring Boot + Kotlin annotation error(s)

I have a Spring Boot 2.0.0.M2 (with WebFlux) application written in Kotlin.
I'm used to define/declare "annotations" for test cases in order to avoid some boilerplate configuration; something like:
import java.lang.annotation.ElementType
import java.lang.annotation.Inherited
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
...
#Inherited
#Target(ElementType.TYPE)
#AutoConfigureWebTestClient // TODO: FTW this really does?!
#Retention(RetentionPolicy.RUNTIME)
//#kotlin.annotation.Target(AnnotationTarget.TYPE)
//#kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
#ActiveProfiles(profiles = arrayOf("default", "test"))
#ContextConfiguration(classes = arrayOf(Application::class))
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
annotation class SpringWebFluxTest
...then in my tests I use it like:
#SpringWebFluxTest
#RunWith(SpringRunner::class)
class PersonWorkflowTest {
private lateinit var client: WebTestClient
...
The issue is that I can't achieve the same using the annotations provided by Kotlin:
#kotlin.annotation.Target(AnnotationTarget.TYPE)
#kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
I'm getting a this annotation is not applicable to target 'class' inside PersonWorkflowTest. If I use the Java ones everything is fine, but on the other hand I get these warnings ― which what I'm really trying to get rid of :)
...
:compileTestKotlin
w: /home/gorre/Workshop/kotlin-spring-boot-reactive/src/test/kotlin/io/shido/annotations/SpringWebFluxTest.kt: (18, 1): This annotation is deprecated in Kotlin. Use '#kotlin.annotation.Target' instead
w: /home/gorre/Workshop/kotlin-spring-boot-reactive/src/test/kotlin/io/shido/annotations/SpringWebFluxTest.kt: (20, 1): This annotation is deprecated in Kotlin. Use '#kotlin.annotation.Retention' instead
kotlin has it own #kotlin.annotation.Retention and #kotlin.annotation.Target annotations. take a while to look the kotlin annotation documentation please.
Edit
I have test it in springframework that no problem. Note that have a distinction in #Target between java and kotlin.
the kotlin kotlin.annotation.#Target(AnnotationTarget.CLASS) is translated to:
#java.lang.annotation.Target(ElementType.TYPE);
and the kotlin kotlin.annotation.#Target(AnnotationTarget.TYPE) is translated to:
#java.lang.annotation.Target;
which means AnnotationTarget.TYPE don't supported for java. it just used in kotlin. so your own annotation should be like this:
//spring-test annotations
#ActiveProfiles(profiles = arrayOf("default", "test"))
#ContextConfiguration(classes = arrayOf(Application::class))
//spring-boot annotations
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureWebTestClient
//kotlin annotations
#Target(AnnotationTarget.CLASS)
#Retention(AnnotationRetention.RUNTIME)// can remove it default is RUNTIME
annotation class SpringWebFluxTest;

ComponentScan.basePackageClasses vs ComponentScan.basePackages to register a single Spring webMVC Controller?

I want to add a single specific controller class to my Spring WebApplicationContext. I ran across the following example: (its in Scala, but is adapted from here: using ComponentScan or context:component-scan with only one class)
#Configuration
#ComponentScan(
basePackages = Array("com.example.controllers"),
useDefaultFilters = false,
includeFilters = Array(
new ComponentScan.Filter(`type` = FilterType.ASSIGNABLE_TYPE,
value = Array(classOf[com.example.controllers.MyController]))))
class MyConfig {
}
This works nicely (but is very verbose).
But Spring's #ComponentScan also has basePackageClasses
#Configuration
#ComponentScan( basePackageClasses=Array(classOf[com.example.controllers.MyController]))
class MyConfig {
}
Of basePackageClasses, Spring's docs says:
Type-safe alternative to basePackages() for specifying the packages to
scan for annotated components.
However, while the first ComponentScan correctly adds only com.example.controllers.MyController, but the second cause all of my #Controller to be scanned and added! Why? What's the use of basePackageClasses?
Example like: https://github.com/mikaelhg/springmvc-example/blob/master/src/main/java/mikaelhg/example/ExampleConfiguration.java
suggest that basePackageClasses can be used to load a single component.
Update:
As an aside, replacing:
#Configuration
#ComponentScan(
basePackages = Array("com.example.controllers"),
useDefaultFilters = false,
includeFilters = Array(
new ComponentScan.Filter(`type` = FilterType.ASSIGNABLE_TYPE,
value = Array(classOf[com.example.controllers.MyController]))))
class MyConfig {
}
with
#Configuration
class MyConfig {
#Bean
var myController = new com.example.controllers.MyController
}
it seems that MyController never gets gets connected to the servlet (the behavior changed to 404-NotFound) -- when added to a WebApplicationContext.
The full javadoc for that attribute reads
Type-safe alternative to basePackages() for specifying the packages to
scan for annotated components. The package of each class specified
will be scanned.
Consider creating a special no-op marker class or interface in each
package that serves no purpose other than being referenced by this
attribute.
By type-safe, you can't make any mistakes with the String value of the name of the package. If you specify an incorrect class, it will fail at compile time.
When you specify basePackageClasses, Spring will scan the package (and subpackages) of the classes you specify. This is a nice trick with no-op classes/interfaces like Controllers, Services, etc. Put all your controllers in one package containing the Controllers class and specify your Controllers class in the basePackageClasses. Spring will pick them all up.
You still need to specify the filters.

Filter specific packages in #ComponentScan

I want to switch from XML based to Java based configuration in Spring. Now we have something like this in our application context:
<context:component-scan base-package="foo.bar">
<context:exclude-filter type="annotation" expression="o.s.s.Service"/>
</context:component-scan>
<context:component-scan base-package="foo.baz" />
But if I write something like this...
#ComponentScan(
basePackages = {"foo.bar", "foo.baz"},
excludeFilters = #ComponentScan.Filter(
value= Service.class,
type = FilterType.ANNOTATION
)
)
... it will exclude services from both packages. I have the strong feeling I'm overlooking something embarrassingly trivial, but I couldn't find a solution to limit the scope of the filter to foo.bar.
You simply need to create two Config classes, for the two #ComponentScan annotations that you require.
So for example you would have one Config class for your foo.bar package:
#Configuration
#ComponentScan(basePackages = {"foo.bar"},
excludeFilters = #ComponentScan.Filter(value = Service.class, type = FilterType.ANNOTATION)
)
public class FooBarConfig {
}
and then a 2nd Config class for your foo.baz package:
#Configuration
#ComponentScan(basePackages = {"foo.baz"})
public class FooBazConfig {
}
then when instantiating the Spring context you would do the following:
new AnnotationConfigApplicationContext(FooBarConfig.class, FooBazConfig.class);
An alternative is that you can use the #org.springframework.context.annotation.Import annotation on the first Config class to import the 2nd Config class. So for example you could change FooBarConfig to be:
#Configuration
#ComponentScan(basePackages = {"foo.bar"},
excludeFilters = #ComponentScan.Filter(value = Service.class, type = FilterType.ANNOTATION)
)
#Import(FooBazConfig.class)
public class FooBarConfig {
}
Then you would simply start your context with:
new AnnotationConfigApplicationContext(FooBarConfig.class)

Resources