ComponentScan and Autowired not working for dependent Spring project? - spring

I have two projects A and B. Both are built with Maven, and project A has a Maven dependency to project B. Both project have a class with #Configuration annotation where I define #Beans.
I have beans in project A, from both projects. If I use the #Autowired annotation in project A of a bean that is defined in same project, the autowiring works. However, if I use the #Autowired annotation in project A of a bean from project B, I will get an exception.
What does this mean? How can I autowire a bean in project A, that is defined in project B?

This is normally an issue with the base classpath on ComponentScan.
If you for example have the following base packages
com.myproject.a
and
com.myproject.b
in your project A and B respectively, and you're using SpringBoot with the main class
package com.myproject.a
#Configuration
#EnableAutoConfiguration
#ComponentScan
class MyApp {
// Some public static void main ...
}
it will only find your classes in the package com.myproject.a and it's children.
To solve this issue you must enhance the #ComponentScan in a way that it scans both package structures, eg.
package com.myproject.a
#Configuration
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.myproject.a", "com.myproject.b"}
// or basePackages = "com.myproject" in this example
class MyApp {
// Some public static void main ...
}

Related

EntityScan in library project breaks SpringBootApplication auto configuration

I'm trying to build a library with spring which will handle task management for other spring boot applications. My library includes services, repositories and entities. Library will use data source of parent project for entities. My target is using task management library in other spring projects with only using #EnableTask annotation.
To do that, I have prepared my library and it works as I expected. But when I tried to import this library to a spring boot application, repositories and entities from library were not available.
My EnableTask annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Import(TaskConfig.class)
public #interface EnableTask {
}
And TaskConfig
#Configuration
#ComponentScan("com.cbidici.task")
public class TaskConfig {
}
To make repositories from library registred I have changed my configuration class as below.
#Configuration
#ComponentScan("com.cbidici.task")
public class TaskConfig {
#Bean
public TaskRepository taskRepository(EntityManager entityManager) {
JpaRepositoryFactory jpaRepositoryFactory=new JpaRepositoryFactory(entityManager);
return jpaRepositoryFactory.getRepository(TaskRepository.class);
}
}
And it worked...
Now the part I mess up everything! With this configuration I've got excception that my Task entity is not a managed type.
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.cbidici.task.entity.Task
at org.hibernate.metamodel.internal.MetamodelImpl.managedType(MetamodelImpl.java:552) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:74) ~[spring-data-jpa-2.1.2.RELEASE.jar:2.1.2.RELEASE]
To make entities registered, I've tried to add EntityScan to my TaskConfig class. This messes up SpringBootApplication and my parent project does not scan entities in itself.
I've found this issue and there is a work around solution in it.
https://github.com/spring-projects/spring-boot/issues/6141
But, this solution includes adding #EnableJpaRepositories, #EntityScan to my parent project which I don't want to.
I think I need to find a way to register entities in library manually as I did for repositories but I couldn't.

What will happen when I add multiple #ComponentScan in different #Configuration class

When using spring, I want to have a configuration structure like:
//package com.test
//main configuration A
#Configuration
#ComponentScan({"com.pakcage.A", "com.common"})
public class AppA{
...
}
//package com.test
//main configuration B
#Configuration
#ComponentScan({"com.pakcage.B", "com.common"})
public class AppB{
...
}
//package com.common
//sub configuration for common use
#Configuration
#ComponentScan({"com.pakcage.common1", "com.package.common2"})
public class CommonConfig{
...
}
I can launch my Application by useing Configuration AppA or Configuration AppB, and all of them contains some common packages to scan like
"com.pakcage.common1"/"com.package.common2"
, I want to put it into a single configuration.
I want to ask
What will happen when I put multiply #ComponentScan, there will be a combination of all of these #ComponenScan?
Is there some source code reference to read about how this happen?
Yes, all the packages defined by any #ComponentScan will be scanned.
Yes, spring framework is opensource. You can access the sources here.

cannot Autowire a class from external jar in spring boot

Have a class A in a spring boot application. Have added a module in the pom, and i am able to import that class 'B' from that module in this class
but i am not able to autowire the same,
#EnableAutoConfiguration
class A
{
A(B b)
}
There are no compile time errors, but the application fails to start
Parameter 0 of method <> required a bean of type <> that could not be found
Tried annotating the main class with #ComponentScan({"package of class B"}) , no compile errors, but the application fails to start with same error, but with different classes.
thoughts ?
If you specify #ComponentScan with a specific package, Spring will only scan that package and subpackages for Spring beans. So if your different classes are in a different package structure, you have to add those packages as well in the annotation.

Spring Hibernate won't implement Repository Class from Interface

I have two separate projects. One project contains my Application logic and Controllers in org.patrick.application, and one separate project contains my Hibernate entities, Dao, and models in org.patrick.hibernate. My problem is that Spring will not instantiate a implementing class for my CrudRepository.
Here is my Application.java class annotations in my Application project:
package org.patrick.application;
#SpringBootApplication
#Configuration
#EnableAutoConfiguration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = { "org.patrick.hibernate" })
#EntityScan(basePackages = { "org.patrick.hibernate" })
#ComponentScan(basePackages = { "org.patrick.hibernate", "org.patrick.application" })
These annotations should scan my second Hibernate project for all of my Hibernate objects.
My Hibernate repository looks like this:
package org.patrick.hibernate;
#Repository
public interface PatrickDao extends CrudRepository<MyModel, Long>
This repository does not have a class implementation. I am expecting Spring to populate this implementation for me.
Now, inside of my application org.patrick.application, I am trying to use the Dao like so:
package org.patrick.application;
#Autowired
private PatrickDao patrickDao;
This is causing my Application project to fail to start because of the following error:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean found for dependency [org.patrick.hibernate.PatrickDao]: expected at least 1 bean which qualifies as autowire candidate.
I know that the core problem is that Spring is not implementing this interface -
because if I provide my own PatrickDaoImpl in the org.patrick.hibernate package, then the Application project will start just fine. This confuses me because I have the proper annotations on my Application.java class, and yet the Repository cannot be implemented by Spring for some reason.
Is there anything further I need to do in order to get Spring to implement the class for my Repository interface? In previous testing, this behavior works if everything is under the same package.
I found the problem. For this particular Model, the definition looked as such:
package org.patrick.hibernate;
public class MyModel implements Serializable {}
This Model did not have the #Entity annotation. If this annotation is missing, then Spring will give no warnings as to why it cannot implement the repository interface. In my case, it simply threw the NoSuchBeanDefinitionException exception.
I updated my Model with the proper annotations:
package org.patrick.hibernate;
#Entity
public class MyModel implements Serializable {}

how Spring boot explicitly not load a class if it's not a web project?

An independent common project A which supply common configuration, inside it there is a class
#RestController
public class CustomErrorController implements ErrorController {
#Autowired
private ErrorAttributes errorAttributes;
//...
}
Now there is another project B which depend on above project A, but this is not a web project only execute some business logic then exit. So in application.properties I have this configuration:
spring.main.web_environment=false
but when run project B, it failed because this exception:
No qualifying bean of type [org.springframework.boot.autoconfigure.web.ErrorAttributes] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
So how to solve this problem? Could exclude CustomErrorController in B's pom.xml or if it's not a web project could not load CustomErrorController?
spring.main.web_environment=false disable the embedded container even if it is present in your project. It does not perform black magic to prevent your classes to be instantiated.
If you are using component scan and you provide classes that require a web environment it will fail (as you just saw). Maybe you need to refactor your project in several modules and isolate the web part in a separate project?
exclude CustomErrorController class
#ComponentScan(basePackages = "com.foo",
excludeFilters = {#ComponentScan.Filter(value = ProjectAApplication.class,type = FilterType.ASSIGNABLE_TYPE),
#ComponentScan.Filter(value = CustomErrorController.class, type = FilterType.ASSIGNABLE_TYPE)})
#SpringBootApplication
public class ProjectBApplication implements CommandLineRunner {

Resources