I have a Spring MVC application with multiple separate modules which all have their own JavaConfig #Configuration class. My goal is to load all spring configurations that are present in the war. Depending on options passed to the build command some modules and thus configurations may not be present and so #Import isn't an option as it would throw ClassDefNotFound.
In the spring documentation it says
#Configuration is meta-annotated with #Component, therefore #Configuration classes are candidates for component scanning (typically using Spring XML's element) and therefore may also take advantage of #Autowired/#Inject at the field and method level (but not at the constructor level).
#Configuration classes may not only be bootstrapped using component scanning, but may also themselves configure component scanning using the #ComponentScan annotation:
However the main application class entry point looks like this
#Configuration
#EnableWebMvc
#EnableAutoConfiguration
#ComponentScan(basePackages="com.svims.common.web.config")
public class Application extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And there is another class with the #Configuration annotation in the com.svims.common.web.config package that isn't found or the beans inside this configuration just aren't loaded.
I have tried adding this to the main application class
#ComponentScan(basePackages="com.svims.common.web.config",
includeFilters = {#ComponentScan.Filter( Configuration.class ) })
To ensure the scan is configured to find these types of classes but it still doesn't work.
I can only assume that Spring MVC bootstraps in a way as to ignore these configurations but I can't find any reference to this in the documentation.
Does anyone know what is going on or any suggestions on how I might do this?
Always check the packages being scanned again and again.
Related
I have a spring boot app, which has a library as a dependency. In this library I have several #Component and #Configuration classes, which are not scanned by Spring Boot app. I would like to add them to component scan, but I am not able to
How can this be achieved correctly? I think adding #ComponentScan to MainApp class, annotated with #SpringBootApplication will override the default config
Thanks!
#SpringBootApplication Annotation is the combination for #Configuration, #EnableAutoConfiguration and #ComponentScan
We can also use basePackages for scan based on requirement.
For example common package is com.example so directory will be com -> example and sub packages. So for the project there will be different packages for the different modules like controller, service, dto, repository etc...
If we want to use any package for the component scan then we can use like below script.
Hierarchy will be :
com.example.controller
com.example.service
com.example.repository
So basePackages will be look like this :
#ComponentScan(basePackages = "com.example")
Because com.example is only the path/package which is common for all other packages. So we can use like this.
What is the difference between the #ComponentScan and #EnableAutoConfiguration annotations in Spring Boot? Is it necessary to add these? My application works very well without these annotations. I just want to understand why we have to add them.
What is the difference between the #ComponentScan and
#EnableAutoConfiguration annotations in Spring Boot?
#EnableAutoConfiguration annotation tells Spring Boot to "guess" how you will want to configure Spring, based on the jar dependencies that you have added. For example, If HSQLDB is on your classpath, and you have not manually configured any database connection beans, then Spring will auto-configure an in-memory database.
#ComponentScan tells Spring to look for other components, configurations, and services in the specified package. Spring is able to auto scan, detect and register your beans or components from pre-defined project package. If no package is specified current class package is taken as the root package.
Is it necessary to add these?
If you need Spring boot to Auto configure every thing for you #EnableAutoConfiguration is required. You don't need to add it manually, spring will add it internally for you based on the annotation you provide.
Actually the #SpringBootApplication annotation is equivalent to using #Configuration, #EnableAutoConfiguration and #ComponentScan with their default attributes.
See also:
Using the #SpringBootApplication annotation
Auto-configuration
One of the main advantages of Spring Boot is its annotation driven versus traditional xml based configurations, #EnableAutoConfiguration automatically configures the Spring application based on its included jar files, it sets up defaults or helper based on dependencies in pom.xml.
Auto-configuration is usually applied based on the classpath and the defined beans. Therefore, we donot need to define any of the DataSource, EntityManagerFactory, TransactionManager etc and magically based on the classpath, Spring Boot automatically creates proper beans and registers them for us. For example when there is a tomcat-embedded.jar on your classpath you likely need a TomcatEmbeddedServletContainerFactory (unless you have defined your own EmbeddedServletContainerFactory bean). #EnableAutoConfiguration has a exclude attribute to disable an auto-configuration explicitly otherwise we can simply exclude it from the pom.xml, for example if we donot want Spring to configure the tomcat then exclude spring-bootstarter-tomcat from spring-boot-starter-web.
#ComponentScan provides scope for spring component scan, it simply goes though the provided base package and picks up dependencies required by #Bean or #Autowired etc, In a typical Spring application, #ComponentScan is used in a configuration classes, the ones annotated with #Configuration. Configuration classes contains methods annotated with #Bean. These #Bean annotated methods generate beans managed by Spring container. Those beans will be auto-detected by #ComponentScan annotation. There are some annotations which make beans auto-detectable like #Repository , #Service, #Controller, #Configuration, #Component.
In below code Spring starts scanning from the package including BeanA class.
#Configuration
#ComponentScan(basePackageClasses = BeanA.class)
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class Config {
#Bean
public BeanA beanA(){
return new BeanA();
}
#Bean
public BeanB beanB{
return new BeanB();
}
}
#EnableAutoConfiguration in spring boot tells how you want to configure spring, based on the jars that you have added in your classpath.
For example, if you add spring-boot-starter-web dependency in your classpath, it automatically configures Tomcat and Spring MVC.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
You can use #EnableAutoConfiguration annotation along with #Configuration annotation.
It has two optional elements,
exclude : if you want to exclude the auto-configuration of a class.
excludeName : if you want to exclude the auto-configuration of a class using fully qualified name of class.
Examples:
#Configuration
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}
#EnableAutoConfiguration(excludeName = {"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#SpringBootApplication is a newer version of #EnableAutoConfiguration which was introduced in Spring Boot 1.2.
#SpringBootApplication is a combination of three annotations,
#Configuration - for java based configuration classes.
#ComponentScan - to enable component scanning, all the packages and
subpackages will be auto-scanned which are under the root package on
which #SpringBootApplication is applied.
#EnableAutoConfiguration - to enable auto-configuration of the
classes bases on the jars added in classpath.
#ComponentScan enables component scanning so that web controller classes and other components that you create will be automatically
discovered and registered as beans in spring's application context.
You can specify the base packages that will be scanned for auto-discovering and registering of beans.
One of the optional element is,
basePackages - can be used to state specific packages to scan.
Example,
#ComponentScan(basePackages = {"com.example.test"})
#Configuration
public class SpringConfiguration { }
In my spring boot project i have to use a external library which it has define beans in spring context. So in my Application class i have add below which are the base package for bothe my project and the external library,
#SpringBootApplication(exclude = SecurityAutoConfiguration.class)
#EnableHypermediaSupport(type = { EnableHypermediaSupport.HypermediaType.HAL })
#EnableSwagger2
#ComponentScan(basePackages = {"com.mylibrary.test", "com.otherlibrary.springtool"})
//#EnableDiscoveryClient
public class Application extends RepositoryRestMvcConfiguration {
}
But the beans in other library such as #Configuration are not initialize?
#ComponentScan works for the classes that are annotated with #Component, #Repository or #Service. Make sure classes in "com.otherlibrary.springtool" are annotated with above annotation to be found or else you have to declare them as Spring beans with #Bean annotation. Hope it helps.
I had something similar when I was trying to use open feign interfaces coming from an external lib and then I had to add the #EnableFeignClients(basePackages = {"lib.pckg"}), because the Feign had to create the beans for me, not the Spring IoC.
It would be good if you provide some log error.
I have a library as a jar packaging Maven project which offers services.
The #Configuration class to instantiate service beans:
#Configuration
public class JpaService {
#Bean
public UserRoleServiceImpl userRoleService() {
return new UserRoleServiceImpl();
}
#Bean
public UserServiceImpl userService() {
return new UserServiceImpl();
}
}
I reckoned I needed to have the beans instantiation outside of the jar archive.
So I had a copy of this class in the test part of the project, and another copy in another war packaging Maven project using the library.
But what if I instantiated the services in the jar library itself. I would need to only do it once, be it for testing or for all client projects using it.
UPDATE: Two questions...
Should all component scanning only be done from the war ? Or should the jar service components be scanned from the jar ?
And what if two components (one in the jar and one in the war) have the same class name in the same package ?
I dont think i fully understand your question, but if you are aiming to add beans to your application context that is outside the jar then what you have to do is use the #ComponentScan annotation, and specify the package you want to scan, the package can be in a different jar, the only thing required is that you anotate the clases you want to include with #Service, #Componenet or even #Configuration
example:
#Configuration
#ComponentScan(basePackages={"com.somepackacge.controller",
...
you can include as much packages as you like.
By the way dont copy your clases from one place to the other, maintining that will be a headache in the futute, if you want to include your configuration in your tests you can always do :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyConfigClass.class)
Where MyConfigClass is the class u used before with the component scan
Hope it helps
I have a myapp parent pom type maven project with myapp-core and myapp-web modules. myapp-core module is added as dependency to myapp-web.
All the classes in myapp-core module reside in root package com.myapp.core and all classes in myapp-web module reside in root package com.myapp.web
The main Application.java is also in com.myapp.web package. As my core module root package is different I am including common base package "com.myapp" for ComponentScan as follows:
#Configuration
#ComponentScan(basePackages="com.myapp")
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Now the surprising thing is if I run this app using Run As -> Spring Boot App it is working fine. But if I run it as Run As -> Java Application it is failing with error saying it can't found beans defined in myapp-core module.
If I move my Application.java to com.myapp package it is working fine.
It should work even if i run it as Java Application also, right?
After enabling debug log level for spring and going through extensive logs I found that scanning for various components like JPA Repositories, JPA Entities etc are depending on the Application.java's package name.
If the JPA Repositories or Entities are not in sub packages of Application.java's package then we need to specify them explicitly as follows:
#Configuration
#ComponentScan(basePackages="com.sivalabs.jcart")
#EnableAutoConfiguration
#EnableJpaRepositories(basePackages="com.sivalabs.jcart")
#EntityScan(basePackages="com.sivalabs.jcart")
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
With the above additional #EnableJpaRepositories, #EntityScan I am able to run it using Run As -> Java Application.
But still not sure how it is working fine when Run As -> Spring Boot App!!
Anyway I think it is better to move my Application.java to com.myapp package rather than fighting with SpringBoot!
I have the same problem. Only adding the #EnableJpaRepositories annotation can solve the issue. I tried to define basePackages in #SpringBootApplication, to no avail.
I think the package of the Application class is fed to the scanning process of JpaRepositories, but other packages defined in #SpringBootApplication are ignored.
It looks like a bug/improvement of Spring Boot.
I had a similar issue with Redis repositories that was fixed in a similar way:
#Configuration
#EnableConfigurationProperties({RedisProperties.class})
#RequiredArgsConstructor
#EnableRedisRepositories(basePackages = {"com.example.another"})
public class RedisConfig {
private final RedisConnectionFactory redisConnectionFactory;
#Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
template.setConnectionFactory(redisConnectionFactory);
template.afterPropertiesSet();
return template;
}
}