SpringBoot Autowired Exception from external JAR - java

I have a SpringBoot Application that works with another external JAR. When I try to #Autowired classes from the JAR , the SpringApp fails to load. I got "org.springframework.beans.factory.NoSuchBeanDefinitionException". I know this has to do with SpringBoot not searching where the project classes are, but I can't figure out how to correctly tell where to look. I've added #ComponentScan, it worked with other internal classes, but not with this External JAR.
This is the class where I use the JAR methods:
package ar.com.tr.latam.care.ws.service.impl;
#Configuration
#ComponentScan({
"ar.com.tr.arz.expurgo", "ar.com.tr.arz.expurgo.logger", "ar.com.tr.arz.expurgo.logger.dao", "ar.com.tr.arz.expurgo.logger.impl","ar.com.tr.arz.expurgo.logger.model"})
#Import({ AppConfig.class, AplicacionCriteriosExpurgoServiceImpl.class, ClasificacionServiceImpl.class, ExcelReaderServiceImpl.class, ConversionServiceImpl.class})
#Service
public class ArzServiceImpl implements ArzService {
#Autowired
private ExcelReaderService excelReaderService;
#Autowired
private ConversionService conversionService;
#Autowired
private AppConfig appConfig;
...
}
And my Init.java class:
package ar.com.tr.latam.care;
#Configuration
#ComponentScan({ "ar.com.tr.latam.care.controller",
"ar.com.tr.latam.care.filtro", "ar.com.tr.latam.care",
"ar.com.tr.latam.care.model.log", "ar.com.tr.latam.care.logger.impl", })
#EnableJpaRepositories
#SpringBootApplication
public class Init extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Init.class);
}
/**
* Main method.
*
* #param args String[].
* #throws Exception Exception.
*/
public static void main(String[] args) throws Exception {
SpringApplication.run(Init.class, args);
}
}
The JAR structure starts with "ar.com.tr.arz.expurgo" , but I can't find how to properly tell Springboot to check for those files there.
The error is "Could not autowire field: private ar.com.tr.arz.expurgo.services.ClasificacionService"
I've also tried adding the external JAR classes in the #ComponentScan Init class, but it also didn't work.
Any suggestions?

I was able to solve it.
First I remove the #ComponentScan from the #Service, and only called it in my Init.java class (Start of the application)
THen I changed my Init packagein order to be on an upper level of the jar classes :
My Init.java was under package ar.com.tr.latam.care, the External JAR package classes were under: package ar.com.tr.expurgo. So, I changed my Init.java to be in package ar.com.tr, and every worked!

Related

ApplicationContextAware is not working in spring-boot

I'm trying to use Brave Tracer Inside the non component class.
This code is working fine in main project, setting context and getting context both are printing. But when I generate jar file and import it to different project 2 and run it, Only Getting context is printing and getting null error.
I'm new to spring-boot
Initial problem was I wanted to Autowired Tracer to my non-component class, I google it to solve this problem, I got this result in google. If any one have any other solution for this kind of problem.
Open to suggestion
Thank you.
// Project 1
// This is the main code, Generated a Jar file
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext appContext) {
System.out.println("Setting context");
ctx = appContext;
}
public static ApplicationContext getApplicationContext() {
System.out.println("Getting context");
return ctx;
}
}
// Project 2
// imported project 1 jar file to this project
// Added jar file below main package
// for simplicity i have used this in main class which is component class
#SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
// using this lines in non-component class
ApplicationContext contextUtils = ApplicationContextUtils.getApplicationContext();
contextUtils.getApplicationName();
}
}
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "org.springframework.context.ApplicationContext.getApplicationName()" because "contextUtils" is null
at com.omniauth.omniauth.OmniAuthApplication.main(OmniAuthApplication.java:26)
You are getting the NullPointerException because the code in the other project is calling getApplicationContext before the ApplicationContextUtils has been loaded.
In fact, the ApplicationContextUtils component may not be loaded at all, as by default, Spring Boot only loads components that are in the same package (or subpackages) as the main class (the one annotated with #SpringBootApplication). If the other project uses different package names, ApplicationContextUtils is never loaded.

Autowiring an interface implementation from an external library

I have written a common library for future SpringBoot applications and made it into a jar. Inside, there is a service that autowires an interface:
#Service
public class MyService {
protected #Autowired AbstractUserService abstractUserService;
}
public interface AbstractUserService {
Optional<? extends AbstractUser> findByPrincipal(Principal principal);
}
In my main application, in build.gradle I load the library mentioned above as follows:
...
repositories {
flatDir {
dirs "$rootDir/libs"
}
}
...
dependencies {
...
implementation name: "my-library"
...
}
Then I have implemented the interface:
#Service
public class UserService implements AbstractUserService {
#Override public Optional<? extends AbstractUser> findByPrincipal(Principal principal) {
// do something
}
However, it seems that the library is unable to find it and I get the following exception:
Parameter 1 of constructor in MyService required a bean of type 'AbstractUserService' that could not be found.
In my main application I have also added the following annotation in order to #Autowire the services exposed by this library:
#SpringBootApplication(scanBasePackages = {"the.root.package.of.my-library"})
Is there something that can be done? Also, is there a better way of autowiring the exposed services without the explicit scanBasePackage description?
I have managed to fix it.
First, I have removed the explicit scanBasePackage description from #SpringBootApplication.
Secondly, I have annotated the configuration file from the library with #ComponentScan and created the following file: resources/META-INF/spring.factories with the following content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
my-library.package.MyLibraryConfig
This is my complete library config file:
#Configuration
#ComponentScan
public class MyConfig {
...
}

Why are my dependency Spring beans instantiated twice ? Once from the jar dependency and once from the classpath

I have the most puzzling Spring error message I ever had, well, I had a few over the years, but this one is one to remember.
The error message is:
Field orderService in com.thalasoft.butik.rest.config.FixtureService required a single bean, but 2 were found:
- com.thalasoft.butik.data.service.OrderServiceImpl: defined in URL [jar:file:/home/stephane/.m2/repository/com/thalasoft/butik-data/0.0.1-SNAPSHOT/butik-data-0.0.1-SNAPSHOT.jar!/com/thalasoft/butik/data/service/OrderServiceImpl.class]
- OrderService: defined by method 'OrderService' in class path resource [com/thalasoft/butik/data/config/JpaService.class]
The butik application is composed of 2 Spring projects, one being the butik-data project and the other one being the butik-rest project.
The error occurs when running the integration tests in the butik-rest project
mvn clean install -Denv="test" -Ddb="h2"
The very same error occurs when running the application and not running the integration tests:
mvn clean spring-boot:run
The dependency is only present once in the pom.xml file:
<dependency>
<groupId>com.thalasoft</groupId>
<artifactId>butik-data</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
My butik-rest project configuration looks like:
#EnvProd
#SpringBootApplication
#Slf4j
public class Application implements CommandLineRunner {
#Component
#ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "com.thalasoft.butik.rest.service", "com.thalasoft.butik.data" })
public class ApplicationConfiguration {
}
#Component
#EnableWebMvc
#EnableSpringDataWebSupport
#ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "com.thalasoft.butik.rest.exception",
"com.thalasoft.butik.rest.controller", "com.thalasoft.butik.rest.assembler" })
public class WebConfiguration implements WebMvcConfigurer {
The integration test configuration:
#RunWith(SpringRunner.class)
#Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {
"classpath:mysql/clean-up-before-each-test.sql" })
public abstract class BaseTest {
#Configuration
#EnableAutoConfiguration
#ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "com.thalasoft.butik.rest.config",
"com.thalasoft.butik.rest.service", "com.thalasoft.butik.data" })
public class TestConfiguration {
}
#EnableWebSecurity
#ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "com.thalasoft.butik.rest.filter" })
public class NoSecurityConfiguration extends WebSecurityConfigurerAdapter {
The service beans are instantiated explicitly in the dependency project:
#Configuration
public class JpaService {
#Bean
public ProductServiceImpl ProductService() {
return new ProductServiceImpl();
}
#Bean
public OrderServiceImpl OrderService() {
return new OrderServiceImpl();
}
}
Could it be that Spring gets one bean from the explicit instantiation above in the butik-data project, and another one from the "com.thalasoft.butik.data" scanning in the dependant butik-rest project ?
UPDATE: Even when changing the 2 instances of "com.thalasoft.butik.data" (there is one to run the application and another one to run the integration tests) into "com.thalasoft.butik.data.config" I still get the same error.
UPDATE: I see I had 2 mistakes compounding making the whole issue a bit tricky. I had to remove the "com.thalasoft.butik.data.config" instance from the integration tests also. And now the issue is gone.
It looks like you've scanned both of those locations. You need to investigate places which are currently scanned and which should be scanned.
If you think that current way of scanning (which include two beans which are suitable for autowiring 'orderService' field), you can mark one of those beans by annotation #Primary ( docs: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Primary.html ).
Bean marked by this annotation will be prefered over another ones, which should solve your problem.
Good luck :)

Can't autowire repository from an external Jar into Spring Boot App

I have packaged my entire entities of the application and the repository interfaces into one jar. The repositories are written with #Repository annotation:
#Repository
public interface InternalUserRepository extends JpaRepository<InternalUser, Long>{
}
I have included this jar file inside my spring boot application and trying to autowire the interface like this from a controller:
#RestController
public class AuthenticationController {
#Autowired
AuthenticationService authenticationService;
#Autowired
InternalUserRepository internalUserRepository;
#GetMapping("/")
public String home() {
return "Hello World!";
}
}
My Main app class is written like:
#SpringBootApplication
#EnableJpaRepositories
#ComponentScan("com.cdac.dao.cdacdao.*")
public class CdacAuthenticationMgntApplication {
public static void main(String[] args) {
SpringApplication.run(CdacAuthenticationMgntApplication.class, args);
}
}
The repository is not getting autowired. When I am firing up the Spring boor application I am getting the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field internalUserRepository in
com.cdac.user.cdacauthenticationmgnt.controller.AuthenticationController required a bean of type 'com.cdac.dao.cdacdao.repository.InternalUserRepository' that could not be found.
Action:
Consider defining a bean of type 'com.cdac.dao.cdacdao.repository.InternalUserRepository' in your configuration.
Have anyone tried similar architecture like this?
If your JPA repositories are in a different package than your Spring Boot application class, you have to specify that package on the EnableJpaRepositories annotation, not Component:
#EnableJpaRepositories("com.cdac.dao.cdacdao")
The package you specified on ComponentScan is for detecting classes as regular Spring beans, not repository interfaces.
As I remember, #ComponentScan should take a complete path to a package, so I think that your package.* does not work.
Try to use type-safe component scan instead:
// You refer to your packages of your base project and your module here.
// Choose the class so that their package is cover all child package
#SpringBootApplication(scanBasePackageClasses = {xxx. InternalUserRepository.class, xxx.CdacAuthenticationMgntApplication.class})
#EnableJpaRepositories
// No need to explicit #ComponentScan
public class CdacAuthenticationMgntApplication {
Or you can try #EnableJpaRepositories("com.cdac.dao.cdacdao")
Either way, you should pick the class in the most outer package (Spring will aslo try to find bean in sub package of those component scan packages)
Annotation #SpringBootApplication has supported all function
So we don't need config manually
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan(excludeFilters = {
#Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

Spring Boot auto configuration order from external dependency

I have a problem trying to get my autoconfiguration working. I have two jars as follows, each have a spring.factories file where these two are enabled for EnableAutoConfigurationProperties.
This configuration is in my-package-mock.jar, it depends on my-package-real.jar below:
package org.packages.package.packageA;
#Configuration
#AutoConfigureBefore(AutoConfigurationB.class)
public class AutoConfigurationA {
#Bean
public MyService mockService() {
return new MyMockService();
}
}
This configuration is in my-package-real.jar:
package org.packages.package.packageB;
#Configuration
#ConditionalOnMissingBean(MyService.class)
public class AutoConfigurationB {
#Bean
public MyService realService() {
return new MyRealService();
}
}
Now the idea is that if my-package-mock.jar is included then AutoConfigurationB will not be processed as A is ordered to be before and by the time it gets to B MyService is already defined.
However, it does not work when used in a third project that includes these jars. It looks like the AutoConfigureOrder annotation is skipped when loading these jars from the classpath and these configurations are processed in the order the jvm loads these classes. In my particular case it does B first and at that point MyService is not yet defined and thus will instantiate the RealService bean. How can I get this to work?
Obviously this is a small example where a #Primary annotation on the mock will do the job, but that is not what I'm looking for.
Edit: it seems if the #SpringBootApplication annotated main is not a part of the package where these configurations are then things do work. E.g. the annotation is not in "org.packages.package" but "org.somewhereelse" then things work.
package org.packages.package;
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(Collections.singletonList(TestApplication.class).toArray(), args);
}
}
#AutoConfigureBefore and #AutoConfigureAfter only apply when a configuration class is loaded as a result of auto-configuration being enabled and it being listed in spring.factories. When your auto-configuration classes are in org.packages.package (or a sub-package) and your main application class is in the same package, they're being found by Spring Framework's standard component scanning. This happens because #SpringBootApplication enables component scanning for the package of the class that it's annotating. As a result of this the auto-configuration-specific ordering doesn't apply.
To avoid the problem, you should places your auto-configuration classes in a package that isn't used by any application code.

Categories

Resources