Autowiring an interface implementation from an external library - java

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 {
...
}

Related

Spring: error using Java Mapper from ibatis library. The interface is not a bean

I am developing a Back-end application using Java and Spring framework.
I start from an archetypal project which, however, causes some problems in the run phase.
I have this interface:
#Mapper
public interface MyInterface {
public ReturnType1 method1(long myLong);
#Select("SELECT * FROM myTab WHERE id = #{myLong}")
public ReturnType2 method2(long myLong);
...
...
}
This interface uses the ibatis library (class org.apache.ibatis.annotations.Mapper);
Elsewhere in the code I have this service:
#Service
public class ExampleService {
#Autowired
private MyInterface myInterface;
...
}
where the #Service annotation is org.springframework.stereotype.Service;. Thanks to #Autowired, this service uses the interface that has #Mapper as an annotation (seen before).
However, in the run phase I get the following error:
APPLICATION FAILED TO START: Field myInterface required a bean of type MyInterface that could not be found. The injection point has the following annotations: #org.springframework.beans.factory.annotation.Autowired(required=true). Consider defining a bean of type 'MyInterface' in your configuration.
Why is this error reported to me? I am not familiar with the ibatis library... in my project I have an xml file in this path: myProject/src/main/resources/mybatis/FileMapper.xml and in the application.properties file I have this row:
mybatis.mapper-locations=classpath*:mybatis/*Mapper.xml
It seems to me that everything is configured correctly. Could you explain to me where and why I get this error?
Hello add #MapperScan
#SpringBootApplication
#MapperScan("com.demo.mappers")
public class SpringbootDemoApplication
{
public static void main(String[] args)
{
SpringApplication.run(SpringbootDemoApplication.class, args);
}
}

Cannot find the Repository bean in Spring boot / Spring Data MongoDB

I have created a spring project which has a controller and all of its logic is written in Service Interface, which is implemented by ServiceImpl class. I have a repository with which has a model.
//Service Interface
public interface Service{
List<Model> getAllKpiData();
}
//ServiceImpl Class
#Service
public class ServiceImpl implements Service{
#Autowired
private KPIRepository kpiRepository;
#override
private List<Model> getAllKpiData()
{
this.kpiRepository.findAll();
//gets me an empty list.
}
}
//KPIRepository
#Repository
public inerface KPIRepository extends MongoRepository<KPIModel, String>
{
}
//Another Service Interface in another package
public interface AnotherService{
List<Model> getAllKpiData();
}
//ServiceImpl Class
#Service
public class AnotherServiceImpl implements Service{
#Autowired
private KPIRepository kpiRepository;
#override
private List<Model> getAllKpiData()
{
this.kpiRepository.findAll();
//gets me list of values, which are inside the repo(master data).
}
}
Both of them are pointing to same repo, but in AnotherService class i am able to get values inside the repository, whereas i am not able to get any values inside Service, on doing this.kpiRepository.findAll().
Do you have spring-boot-starter-data-mongodb dependency on classpath? If yes, then is KPIRepository in the same package as your main class? If not then in your main class put this annotation #EnableMongoRepositories(basePackageClasses=KPIRepository.class) to safely tell Spring Data MongoDB to scan a different root package by type if your project layout has multiple projects and its not finding your repositories. Or you can use #EnableMongoRepositories(basePackages = "com.acme.repositories.mongo") to specify the package that contains all of your repositories.
The presense of spring-boot-starter-data-mongodb will automatically enable #EnableMongoRepositories. And Spring will automatically create proxies of all classes implementing Repository<T,ID> (your class implements MongoRepository, which itself implements Repository) and create beans of them and make them available for injection. And when your repository is in a different package then it is unable to create proxies of your repository, hence fails to create a bean of it. And since there is no bean, it cannot inject it, hence you see the error.
Did you use the #EnableMongoRepositories annotation? Take a look to this link: https://docs.spring.io/spring-data/mongodb/docs/1.2.0.RELEASE/reference/html/mongo.repositories.html. Review the "6.2 usage" point.
Regards

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

Transactional annotation error

When I put "#Transactional(readOnly=false)" annotation in my Service class I get the following error
Description:
The bean 'studentService' could not be injected as a
'com.student.service.StudentServiceImpl' because it is a JDK dynamic
proxy that implements: com.student.service.StudentService
Sample code:
#Service("studentService")
#Transactional(readOnly=false)
public class StudentServiceImpl implements StudentService {
}
public interface StudentService {
}
Action:
Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on #EnableAsync and/or #EnableCaching.
Process finished with exit code 1
What is causing this?
As SO already mentioned on the comment, the error occurs when you are trying to inject/autowire the implementation class instead of interface.
The bean 'studentService' could not be injected as a
'com.student.service.StudentServiceImpl' because it is a JDK dynamic
proxy that implements: com.student.service.StudentService
On the setup posted by SO,
public class StudentServiceImpl implements StudentService {
}
public interface StudentService {
}
If you autowire the interface as below you won't get an error:
#Autowired //or #Inject
StudentService studentService;
in spring boot projects, try to add :
spring.aop.proxy-target-class=true
to your application.properties
OR
#EnableAspectJAutoProxy(proxyTargetClass = true)
to your spring boot entry point.
In your application class file add this:
#SpringBootApplication
#EnableCaching(proxyTargetClass = true)
I had similar problem and resolved in this way

How work with 3rd-party #ConfigurationProperties #Bean?

I use IntellijIdea and gradle. Gradle config:
...
apply plugin: 'propdeps'
apply plugin: 'propdeps-idea'
apply plugin: 'propdeps-maven'
buildscript {
repositories {
maven { url 'http://repo.spring.io/plugins-release' }
}
dependencies {
classpath 'org.springframework.build.gradle:propdeps-plugin:0.0.7'
}
}
compileJava.dependsOn(processResources)
dependencies {
...
optional group: 'org.springframework.boot', name: 'spring-boot-configuration-processor', version: '1.4.0.RELEASE'
}
Ok, for creating my own properties i need:
#Component
#ConfigurationProperties("own.prefix")
#Data
public class TestProps {
public String field;
}
#Configuration
#EnableConfigurationProperties(TestProps.class)
public class AppConf {}
And after i rebuild project spring-boot-configuration-processor genereate new META-INFO, so in application.properties i can use own.prefix.field= and Spring see it.
But what should i do with 3rd party configuration class?
Docs http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html say:
As well as using #ConfigurationProperties to annotate a class, you
can also use it on #Bean methods. This can be particularly useful
when you want to bind properties to third-party components that are
outside of your control.
To configure a bean from the Environment properties, add
#ConfigurationProperties to its bean registration:
#ConfigurationProperties(prefix = "foo")
#Bean
public FooComponent fooComponent() {
...
}
Any property defined with the foo prefix will be mapped onto that
FooComponent bean in a similar manner as the ConnectionProperties
example above.
Ok. Lets try. For example I declare bean like in gide (https://spring.io/guides/tutorials/spring-boot-oauth2/):
#Configuration
#EnableOAuth2Sso
public class SocialConfig {
#Bean
#ConfigurationProperties("facebook.client")
OAuth2ProtectedResourceDetails facebook() {
return new AuthorizationCodeResourceDetails();
}
#Bean
#ConfigurationProperties("facebook.resource")
ResourceServerProperties facebookResource() {
return new ResourceServerProperties();
}
}
But after rebuilding project property facebook.client and facebook.resource do not exist in my application.properties.
Also i tried add SocialConfig.class to
#Configuration
#EnableConfigurationProperties(SocialConfig.class)
public class AppConf {}
After rebuild it still not work.
And like this:
#Configuration
#EnableConfigurationProperties
public class AppConf {}
Still the same.
What am I doing wrong?
Also sorry for my English :)
What you're doing wrong is that the methods in your #Configuration class are not public. Just add public to facebook and facebookResource and you'll be fine. I've just polished that doc in c4cb8317 and I've submitted a PR to fix the tutorial you were using as a base.
Also the generated metadata were mostly empty since nested objects were not flagged with #NestedConfigurationProperty. I've submitted another pull request to fix that.

Categories

Resources