SpringBoot ComponentScan issue with multi-module project - java

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

Related

Spring scanBasePackages not working in gradle multi-module build

I have a multi-module build using Gradle. There are 2 INDEPENDENT (not using packages from project 1 to project 2) but related spring projects in it. 1 of the projects is working as expected but 2nd project is not scanning any beans either in the same package or in sub packages.
Below are the things that I have tried (including the comments). None of the things worked except ImportAutoConfiguration and mentioning the classes which I wanted to import.
#SpringBootApplication(scanBasePackages = {"io.comany.logs.functionality"})
//#ImportAutoConfiguration( classes = {Test.class, DecorationManager.class, Consumer.class})
//#ComponentScan(basePackages = {"io.comany.logs.functionality"})
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
I have verified that my package names are correct. The main class is present in the package io.comany.logs.functionality and the classes I want to scan are in the pacakge io.comany.logs.functionality.kafka
Edit : Copied the same project and created another standalone project, it still doesn't work. So the problem should ideally not be with gradle config.

Loading external jars in spring boot

How do we load additional jar at runtime along with boot jar.
Primary jar: Main.jar
Additional jar: Support.jar
Main project is a gradle boot project.
Support project is NOT a gradle project but is given compile time dependencies to the required jars.
Contents of Support project:
#RestController
#RequestMapping("/test")
public class CustomService implements WebMvcConfigurer {
#RequestMapping(value = "/hello", method = RequestMethod.GET)
public #ResponseBody String get() {
return "Done!!";
}
}
What i tried:
java -cp Support.jar:Main.jar -Dloader.path=Support.jar -Xbootclasspath/p:alpn-boot-8.1.11.v20170118.jar -Dloader.main=com.abc.app.MyApplication org.springframework.boot.loader.PropertiesLauncher
The boot starts up fine but the endpoint is not registered.
NOTE:
I had mentioned annotation scanning.
#SpringBootApplication
#ComponentScan("com.abc")
public class MyApplication {
....
}
Also the Main.jar will be run from various places by various users. Each user might provide his own version of Support.jar. So, hardcoding the dependency into the gradle file of Main project is not feasible.
Try adding #ComponentScan(basePackages=full.name.of.customservice.package) to your spring application configuration, or make CustomService the same package as your #SpringApplication class
try using this - org.xeustechnologies.jcl.JarClassLoader from https://github.com/kamranzafar/JCL
JCL is a configurable, dynamic and extensible custom classloader that loads java classes directly from Jar files and other sources.

Should the service beans defined in the jar library be instantiated by the jar library configuration or by the client war application configuration?

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

manipulating gs spring project

I'm trying to use a spring getting started project, and I have a problem I cannot figure out: when I move the restController from the default "hello" package to another one (say com.mydomain.controllers) I get a 404 error page. Any ideas how to solve this?
PS: I'm using intellij + gradle
In this case, this controller is located in a package that's not scanned by Spring. In the Application class:
package hello;
#SpringBootApplication
public class Application {
//...
}
The #SpringApplication annotation is a convenience annotation - see the full explanation in the guide itself.
If you want to scan other locations and customize more your configuration, you can change your application class to this:
package hello;
#Configuration
#EnableAutoConfiguration
#ComponentScan({"hello", "com.mydomain.controllers"})
public class Application {
//...
}

Loading configuration classes present in Spring MVC

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.

Categories