Failing to import external jar containing bean - java

I'm a little new t working with Spring so any help provided would be great.
I have a SpringApplication class (annotated with #SpringBootApplication. In another class (within the same project), it contains a ServiceClass class. When the class is in the same project, it runs as expected.
When the ServiceClass is moved to an external jar, I get the following error.
Description:
Field service in
package-to-class.Comp required a
bean of type 'package-to-class.ServiceClass'
that could not be found.
The injection point has the following annotations:
#org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type
'package-to-class.ServiceClass' in your
configuration.
I am trying to find what I need to do to inject (#AutoWired) into my project with the above error.

By default, Spring Boot's component scanning only looks in the same package as the #SpringBootApplication and its descendants.
The approach I would suggest is using #Import to import specific beans from outside the project, or use #ComponentScan to import all beans from a package in another project.
#SpringBootApplication
#Import(ServiceClass.class)
public class SpringApplication {
// ...
}
or
#SpringBootApplication
#ComponentScan(basePackages = "com.example.mylibrary")
public class SpringApplication {
// ...
}

Okey some basic things, you have mixed up your packages a bit.
#SpringBootApplication will scan all classes in packages below the class this is annotated on. This annotation is an alias for #EnableAutoConfiguration, #Configuration and #ComponentScan means that #ComponentScan(basePackages = {"com.springdi.example"}, basePackageClasses = DependencyBasePackageClass.class) is not needed.
com.springdi.example // class with #SpringBootApplication annotation
|
|
|
com.springdi.example.* // Will find all #Service, #Component, #Configuration
// in subpackages below the #SpringBootApplication
// annotation
You can read more about the annotation here SpringBootApplication
Since your other annotated classes are NOT in the same package structure as the #SpringBootApplication you need to define all the places you want to scan for annotations.
#SpringBootApplication(scanBasePackages = {"com.springdi.example", "com.dependency.example"})
will probably include all the packages that you want to scan through.

Related

Rest Controller works just for Application class

I'm trying to do a getmapping method using rest-api which will return as a JSON on localhost: 8080. When I do this from the default Application class it works great but when I move this functionality to another class nothing happens. Could someone help me with this, please?
The issue is that your package structure prevents BookController from being component scanned.
When you have your #SpringBootApplication defined in package com.xenon.myspringbootapp, anything in that package and in nested packages is eligible for component scanning, which will register a bean out of #(Rest)Controller and other stereotypes.
Because BookController is defined in book, it is outside of the component scanned packages and will therefore not be component scanned by your #SpringBootApplication annotation.
See the reference docs for more best practices on package structure with Spring Boot applications.
To resolve this, there are two choices to get your class component scanned.
The first (and the way I would recommend) is just to restructure your packages so that the #SpringBootApplication class is at the "base" of your application packages. For example, you could move book.BookController to be at com.xenon.myspringbootapp.book.BookController.
The second is to change the component scanning configuration to include your book package. You can do this either on the #SpringBootApplication annotation itself:
#SpringBootApplication(scanBasePackages = {
"com.xenon.myspringbootapp",
"book"
})
Or, define a different #ComponentScan. Note that the configuration class annotated with #ComponentScan must still be component scanned itself.
#SpringBootApplication
public class MySpringBootAppApplication {
#Configuration
#ComponentScan("book")
public static class MyBookComponentScanConfiguration {}
public static void main(String[] args) {
SpringApplication.run(MySpringBootAppApplication.class);
}
}
Reason 1: Default Spring's component scanning can't fount your #RestController component, so you can try to do #ComponentScan directly.
Reason 2: You need to specify an endpoint for your #GetMapping by using #RequestMapping/#GetMapping("url") or both of them.

Spring boot #ComponentScan vs #Import

I and my friend were discussed about #ComponentScan and #Import. Which one is better?
We have 2 different ideas.
#ComponentScan: Easy to use, import all beans from the component
scan.
#Import: You need to know what component you want to use, no need to scan all.
How about your idea? Which one is better for you to use?
Thanks!
#Import is used to import Java configuration classes marked with #Configuration/#Component typically. So if you have a bean inside this component, Spring will load it into Application Context. You can just put the name of the component or class and Spring will pull it up for you.
However, by using #ComponentScan, you tell the application which packages to scan for java classes are annotated with #Configuration/#Component (or any of #Component's sub-annotations like #Service or #Repository etc) and load all of them up in Application Context so they can be autowired when required. If there are inner instances that need to be populated, Spring will take care of it.
You can read more about #Import and #ComponentScan on their respective doc pages.
This page explains pretty well the difference.
#ComponentScan scans and searches for any beans inside packages/classes specified under basePackageClasses or basePackages options, whichever is configured.
This option also allows you to filter some classes that you do not want to be included in search.
#Import is like clubbing one java configuration into another.
eg:
#Configuration
#ComponentScan(basePackages="com.stackoverflow")
public class Dbconfig {
#Bean
public Datasource dSource(){
return new Datasource()
}
}
#Configuration
#Import(Dbconfig.class)
#ComponentScan(basePackages="org.hellospring")
public class AppConfig {
...// beans
}
So here, if we check AppConfig class,
it will include all beans registered in Dbconfig configuration class including inside of package com.stackoverflow
+
It will include all beans inside AppConfig class and beans under package org.hellospring

SpringBootApplication scanBasePackages is not reading subpackages

I have a common library published on nexus that has package id
x.xx.common
it contains sub packages of common feign clients proxy interfaces
The project that uses this library has package id.
x.xx.account
x.xx.device
each of these projects has its application class in the root
x.xx.account.AppClass
x.xx.device.AppClass
each of these class has
#SpringBootApplication(scanBasePackages = {"x.xx"})
for some reason both projects don't see any of the proxy interfaces under subpackages
x.xx.common.proxy
x.xx.common.configuration
I tried moved the proxy interfaces directly under main package
x.xx.common
but it also failed
Parameter 0 of constructor in x.xx.common.service.impl.AuditServiceImpl required a bean of type 'x.xx.common.LogProxy' that could not be found.
that error is given for every interface proxy
Add annotation to your sub packages class which you want to be scanned. Add the annotations like #Component, #Service or #Repository respectively to the class respectively.
For the annotation:
#SpringBootApplication(scanBasePackages = {"x.xx"})
Suppose there is a class named Abc in the subpackage x.xx, so add the annotation #Component to the class.
#Component
class Abc{}
This will help to read the sub packages class.
To know more about the difference between the above mention annotations: What's the difference between #Component, #Repository & #Service annotations in Spring?
I have figured it out, apparently the AppClass #EnableFeignClients need to have the base class added to it as well.
So for anyone that has the same problem my AppClass now has the following annotation
#SpringBootApplication(scanBasePackages = {"x.xx"})
#EnableFeignClients(basePackages= {"x.xx"})
public class AppClass {
}

#SpringBootApplication and #ComponentScan not working together (bean configuration)

I have a multi module project but I am having a problem with my configuration.
I have a my main method in package nl.example.hots.boot
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan(basePackages = {"nl.*"})
#EntityScan("nl.*")
public class HotsApplication {
public static void main(String[] args) {
SpringApplication.run(HotsApplication.class, args);
}
In the nl.example.hots.core.* package I have the class:
#Service
#AllArgsConstructor
#Transactional(propagation = Propagation.REQUIRED)
public class MapImportService {
private MapInputModelMapper mapInputModelMapper;
private MapEntityRepository mapEntityRepository;
public void add(final MapInputModel mapInputModel) {
System.out.println(mapInputModel.getName());
mapEntityRepository.save(mapInputModelMapper.mapToEntiy(mapInputModel));
}
and:
#Component
#Mapper
public interface MapInputModelMapper {
MapInputModel mapToInputModel(final MapEntity n);
MapEntity mapToEntiy(final MapInputModel n);
}
The repository is in the package nl.example.hots.persistence.*
I get the following error when running the application:
Description:
Parameter 0 of constructor in nl.example.hots.core.dataimport.MapImportService.MapImportService required a bean of type 'nl.timonschultz.hots.core.map.mapper.MapInputModelMapper' that could not be found.
Action:
Consider defining a bean of type 'nl.example.hots.core.map.mapper.MapInputModelMapper' in your configuration.
When I remove the #EnableAutoConfiguration and #ComponentScan annotations it works. The application starts without the bean error.
In that case however my restcontroller does not work anymore:
{
"timestamp": "2018-07-18T20:48:39.414+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/maps"
}
When I remove the MapImportService class (and the bean error doesn't pop up) it works on the same url.
package nl.example.hots.api.data_import;
#RestController
#AllArgsConstructor
public class ImportController {
private static final String URL = // a url
private Reader reader;
#RequestMapping("/maps")
public String abilityStreamImport() {
reader.readStream(URL); // calls a class in nl.example.hots.core.*
return "Greetings from APP!";
}
}
I tried several different combinations and I have a different project as and example where the annotations are used together and it works right. Can somebody explain why the annotations generate the bean error when used together? And why the controller gives an error when only using #SpringBootApplication?
In my Pom.XML the boot module has a dependency on the API layer that has a dependency on the core layer that has a dependeny on the persistence layer.
I'm using mapstruct and Lombok in the project.
--- edit: repository ---
#Entity(name = "MAPS")
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Getter
public class MapEntity extends HasId<Long> {
private String name;
#ElementCollection
private List<String> translations;
}
#Repository
public interface MapEntityRepository extends JpaRepository<MapEntity, Long> {
}
#MappedSuperclass
public abstract class HasId<T> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Setter
#Getter
private T id;
}
project structure:
hots-application;
- hots-api
- pom.xml
- nl.example.hots.api.dataimport.ImportController;
- hots-boot
- pom.xml
- nl.example.hots.boot.HotsApplication;
- hots-core
- pom.xml
- nl.example.hots.core.dataimport.mapImportService.MapImportService;
- nl.example.hots.core.map.mapper.MapInputModelMapper
- hots-persistence
- pom.xml
- nl.example.hots.persistence.common.HasId;
- nl.example.hots.persistence.map.MapEntity;
- nl.example.hots.persistence.map.MapEntityRepository;
pom.xml
Spring Boot will scan all the packages and sub packages starting with the package that the #SpringBootApplication annotated class is in. Your class is in nl.example.hots.boot scanning only that package. The other classes are in different, non-scanned packages.
Due to this package structure and not following the best practices you basically loose a lot of the auto configuration features (Spring Data JPA, ReST etc) and you have to resort to manually enabling/configuring that. Partially through adding additional #ComponentScan annotation, for JPA the #EntityScan annotations. But you would also need to add all the #EnableJpaRepository etc. annotations as those aren't added anymore (at least not with the right packages).
The fix is fairly easy. Move your #SpringBootApplication annotated class to the nl.example.hots package (as stated in the best practices). Remove the annotations other then #SpringBootApplication and simply start your application.
You're missing the #Autowired annotation on your #AllArgsConstructor, try:
#AllArgsConstructor(onConstructor = #__(#Autowired))
After some trial and error I managed to get it working for the most part.
I added the scanBasePackages = "nl" to the SpringBootApplication annotation.
Mapstruct did give me some trouble still however but when I removed it and made a quick mapper myself it all worked fine.
Thanks for all the help and suggestions and links to read up on! I'll keep the (bestpractice) suggestions in mind for my next project.
Below is my main class now
package nl.example.hots.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication(scanBasePackages = "nl")
#EntityScan("nl.*")
#EnableJpaRepositories("nl.*")
public class HotsApplication {
public static void main(String[] args) {
SpringApplication.run(HotsApplication.class, args);
}
}
Spring is not creating a MapInputModelMapperBean. I have never used MapStruct, but it kinda reminds me of mybatis. It looks weird to me that you annotate MapInputModelMapperBean with #Mapper and #Component. How is spring supposed to create the bean? Are you using some sort of spring-boot-mapstruct package that performs this magic?
I made a quick search and found this https://www.credera.com/blog/technology-solutions/mapping-domain-data-transfer-objects-in-spring-boot-with-mapstruct/, from which it seems like you would need something like:
#Mapper(componentModel = "spring")
public interface MapInputModelMapper
Which I guess will generate the implementation of the mapper with #Component on it or something similar.
The #SpringBootApplication annotation is equivalent to using #Configuration, #EnableAutoConfiguration and #ComponentScan with their default attributes.
The component scan default behaviour happens only in the same package and in its sub package. If your controllers or components are in different, dont forget to override your component scan default configurations.
Here is the sample code on how to override the default behavour.
#SpringBootApplication(scanBasePackages = "com.code")
Or you can specify multiple packages as shown below:
#SpringBootApplication(scanBasePackages = {"com.java2novice", "com.example"})
The #SpringBootApplication annotation is a convenience annotation that combines the #EnableAutoConfiguration, #Configuration and the #ComponentScan annotations in a Spring Boot application.
#EnableAutoConfiguration – This enables Spring Boot’s autoconfiguration mechanism. Auto-configuration refers to creating beans automatically by scanning the classpath.
#ComponentScan – Typically, in a Spring application, annotations like #Component, #Configuration, #Service, #Repository are specified on classes to mark them as Spring beans. The #ComponentScan annotation basically tells Spring Boot to scan the current package and its sub-packages in order to identify annotated classes and configure them as Spring beans. Thus, it designates the current package as the root package for component scanning.
You have used all these three annotations #SpringBootApplication , #EnableAutoConfiguration and #ComponentScan.
Instead use single annotation like #SpringBootApplication(scanBasePackages = "abc.def.ghi")
Here the HotsApplication class with more efficient annotation
#SpringBootApplication(scanBasePackages = "nl.example.hots")
public class HotsApplication {
public static void main(String[] args) {
SpringApplication.run(HotsApplication.class, args);
}

Scan components of different maven modules/JARs in a Spring Boot application

I have two Maven modules.
The first one, called "application", contains the spring boot Application class that just contains these lines:
package org.example.application;
#SpringBootApplication
#ComponentScan({"org.example.model", "org.example"})
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
In the same Maven module and package, org.example.application, I have a RestController that uses a Component that in turn uses the components of the other Maven module described below.
The other Maven module, called "model", contains the spring boot components (crud-repositories, entities etc). All those classes are under the same package structure as the first Maven module (org.example) but in subpackages of that, like org.example.model.entities, org.example.model.repositories etc.
So, the flow is like this:
Maven module application in package org.example:
SpringBootApplication -> RestController -> MyComponent
And the components that should be autowired in MyComponent are the ones in the model Maven module under the package org.example.model.
But when I start the application I just get the error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field myRepository in org.example.MyComponent required a bean of type 'org.example.model.repositories.MyRepository' that could not be found.
Action:
Consider defining a bean of type 'org.example.model.repositories.MyRepository' in your configuration.
org.example.model.repositories.MyRepository does exist in Maven module "model" but cannot be found by the SpringBootApplication class!
As you can see, I have tried to explicitly define the scan components to:
#ComponentScan({"org.example.model", "org.example"}) but that does not seem to help.
So what have I done wrong?
The first thing that you should wonder is : why do you declare #ComponentScan while one of the goal of #SpringBootApplication is (among other things) to enable the component scan ?
From Spring Boot documentation :
The #SpringBootApplication annotation is equivalent to using
#Configuration, #EnableAutoConfiguration and #ComponentScan with their
default attributes
Note that when on the class of your Spring Boot Application, you declare #ComponentScan to specify a value as basePackages, it overrides the basePackages used by default by #SpringBootApplication that is the current package where the class resides. So to have as base package both the package of the Spring Boot Application class and the additional packages that were missing, you have to explicitly set them.
Besides basePackages is recursive. So to enable the scan both for classes locating in the "org.example" and "org.example.model" packages, specifying "org.example" is enough as "org.example.model" is a sub-package of it.
Try that :
#SpringBootApplication(scanBasePackages={"org.example"})
Or alternatively :
#SpringBootApplication
#ComponentScan("org.example")
When specify #EnableJpaRepositories/#ComponentScan/scanBasePackages in a Spring Boot Application ?
As you design your Spring Boot application layout, your have two cases :
1) case (to favor) where you use a package layout that provides the auto configuration of Spring Boot with zero configuration.
To summarize : if your classes annotated with Spring Bean stereotypes : #Component, #Repositories, #Repositories,... are located in the same package or a sub-package of the Spring Boot Application class, declaring only
#SpringBootApplication is all you need.
2) case (to avoid) where you don't use a package layout that provides the auto configuration of Spring Boot with zero configuration.
It generally means that you have candidate classes to scan that are not in the package (or sub-package) of your class annotated with #SpringBootApplication.
In this case, you add the scanBasePackages attribute or add #ComponentScan to specify packages to scan.
But additionally, if your repositories are not located in a package or sub-package of your class annotated with #SpringBootApplication, something else has to be declared such as : #EnableJpaRepositories(="packageWhereMyRepoAreLocated")
Here is the documentation about this part (emphasis is mine) :
80.3 Use Spring Data Repositories
Spring Data can create implementations of #Repository interfaces of
various flavors. Spring Boot handles all of that for you, as long as
those #Repositories are included in the same package (or a
sub-package) of your #EnableAutoConfiguration class.
For many applications, all you need is to put the right Spring Data
dependencies on your classpath (there is a
spring-boot-starter-data-jpa for JPA and a
spring-boot-starter-data-mongodb for Mongodb) and create some
repository interfaces to handle your #Entity objects. Examples are in
the JPA sample and the Mongodb sample.
Spring Boot tries to guess the location of your #Repository
definitions, based on the #EnableAutoConfiguration it finds. To get
more control, use the #EnableJpaRepositories annotation (from Spring
Data JPA).
Examples
1) case (to favor) where you use a package layout that provides the auto configuration of Spring Boot with zero configuration.
With a Spring Boot application declared in the org.example package, and all bean classes (Repositories included) declared in the same package or a sub-package of org.example, the following declaration is enough for the Spring Boot application :
package org.example;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
The repositories could be located in the org.example.repository package such as :
package org.example.repository;
#Repository
public interface FooRepository extends JpaRepository<Foo, Long>, { }
and
package org.example.repository;
#Repository
public interface BarRepository extends JpaRepository<Bar, Long>, { }
The controllers could be located in the org.example.controller package :
package org.example.controller;
#RestController
#RequestMapping("/api/foos")
public class FooController {...}
and so for...
2) case (to avoid) where you don't use a package layout that provides the auto configuration of Spring Boot with zero configuration.
With a Spring Boot application declared in the org.example.application package, and not all bean classes (Repositories included) declared in the same package or a sub-package of org.example.application, the following declaration will be required for the Spring Boot application :
package org.example.application;
#SpringBootApplication(scanBasePackages= {
"org.example",
"org.thirdparty.repository"})
#EnableJpaRepositories("org.thirdparty.repository")
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
And the bean classes could be as below.
The repositories that may come from an external a JAR could be located in the org.thirdparty.repository package such as :
package org.thirdparty.repository;
#Repository
public interface FooRepository extends JpaRepository<Foo, Long>, { }
and
package org.thirdparty.repository;
#Repository
public interface BarRepository extends JpaRepository<Bar, Long>, { }
The controllers could be located in the org.example.controller package :
package org.example.controller
#RestController
#RequestMapping("/api/foos")
public class FooController {...}
and so for...
Conclusion : defining the Spring Boot application in the base package of your namespace is really encouraged to make the Spring Boot configuration as simple as possible.

Categories