How to Register beans from all sub packages in Spring boot app? - spring-boot

I am creating new spring boot + jpa + thymeleaf application, I am using many modules which are placed in sub packages. I have structure like ge.my.project where is placed my main class with #SpringBootApplication and under this package I have sub packages ge.my.project.dao,ge.my.project.service,ge.my.project.controller and so on where are placed my beans.#SpringBootApplication scans only beans under base(ge.my.project) package but I want to scan beans from all sub packages.
I tried many variants to scan sub packages :
#ComponentScan(basePackages = {"ge.my.project.controller","ge.my.project.service","ge.my.project.configuration"})
and
#ComponentScan({"ge.my.project.controller","ge.my.project.service","ge.my.proj
ect.configuration"})
and
#ComponentScan("ge.my.project.*")
but nothing works , When I am trying to inject beans using #Autowired
I am getting error like this Consider defining a bean of type 'ge.my.project.service.ServiceTypeService' in your configuration.
Here is my main class
package ge.my.project;
import ge.ufc.inhouseProjects.controller.ServiceTypeController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
//#ComponentScan(basePackages = {"ge.my.project.controller","ge.my.project.service","ge.my.project.configuration"})
#ComponentScan({"ge.my.project.*"})
#EntityScan({"ge.my.project.entity"})
#EnableJpaRepositories("ge.my.project.dao")
public class InhouseProjectsApplication {
public static void main(String[] args) {
/*ApplicationContext applicationContext = */SpringApplication.run(InhouseProjectsApplication.class, args);
}
}
Here is my full project https://github.com/JavaGeoGroup/inhouseProjects.git
Which is the clearest way to scan all project beans in spring boot application?

You need to annotate the implementation and not the interface. So in your case the #Service annotation needs to be set on ServiceTypeDaoImpl-class instead of ServiceTypeDao-interface.
You need to annotate your beans (your implementation of interfaces, not spring data repo interfaces) with the #Component annotation or one of its sub-annotations like #Repository, #Service, #Controller, #Configuration.
From the javadoc of #Component:
* Indicates that an annotated class is a "component".
* Such classes are considered as candidates for auto-detection
* when using annotation-based configuration and classpath scanning.
*
* <p>Other class-level annotations may be considered as identifying
* a component as well, typically a special kind of component:
* e.g. the {#link Repository #Repository} annotation or AspectJ's
* {#link org.aspectj.lang.annotation.Aspect #Aspect} annotation.
Specific extensions to your code-example:
Spring beans start with a small case, so your interface "ServiceTypeDao"will be registred as "serviceTypeDao" as long as you dont set a specific name in the "#Service" annotation.

Related

How does Spring know where to search for Components or Beans?

In an interview i was asked by the interviewer that "How does Spring know where to search for Components or Beans?".
As I was not aware about the internal flow details I was not able to answer the question properly.
I said through #Component and #Bean we can find. But the interviewer was not happy with the question.
If anybody knows please share your knowledge. TIA
I love to nswer interview questions. Read below...
#ComponentScan
If you understand Component Scan, you understand Spring.
Spring is a dependency injection framework. It is all about beans and wiring in dependencies.
The first step of defining Spring Beans is by adding the right annotation — #Component or #Service or #Repository.
However, Spring does not know about the bean unless it knows where to search for it.
This part of “telling Spring where to search” is called a Component Scan.
You define the packages that have to be scanned.
Once you define a Component Scan for a package, Spring would search the package and all its sub packages for components/beans.
Defining a Component Scan
If you are using Spring Boot, check the configuration in Approach 1.
If you are doing a JSP/Servlet or a Spring MVC application without
using Spring Boot, use Approach 2.
Approach 1: Component Scan in a Spring Boot Project
If your other package hierarchies are below your main app with the #SpringBootApplication annotation, you’re covered by the implicit Component Scan.
If there are beans/components in other packages that are not sub-packages of the main package, you should manually add them as #ComponentScan
Consider below class
package com.in28minutes.springboot.basics.springbootin10steps;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
#SpringBootApplication
public class SpringbootIn10StepsApplication {
public static void main(String[] args) {
ApplicationContext applicationContext =
SpringApplication.run(SpringbootIn10StepsApplication.class, args);
for (String name: applicationContext.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
#SpringBootApplication is defined in the SpringbootIn10StepsApplication class which is in the package com.in28minutes.springboot.basics.springbootin10steps
#SpringBootApplication defines an automatic Component Scan on the package com.in28minutes.springboot.basics.springbootin10steps.
You are fine if all your components are defined in the above package or a sub-package of it.
However, let’s say one of the components is defined in package com.in28minutes.springboot.somethingelse
In this case, you would need to add the new package into Component Scan.
You have two options:
Option 1:
#ComponentScan(“com.in28minutes.springboot”)
#SpringBootApplication
public class SpringbootIn10StepsApplication {...}
Option 2:: Define as array
#ComponentScan({"com.in28minutes.springboot.basics.springbootin10steps","com.in28minutes.springboot.somethingelse"})
#SpringBootApplication
public class SpringbootIn10StepsApplication {...}
Approach 2: Non-Spring Boot Project
Option 1:
#ComponentScan(“com.in28minutes)
#Configuration
public class SpringConfiguration {...}
Option 2:
#ComponentScan({"com.in28minutes.package1","com.in28minutes.package2"})
#Configuration
public class SpringConfiguration {...}
XML application context:
<context:component-scan base-package="com.in28minutes" />
Specific multiple packages:
<context:component-scan base-package="com.in28minutes.package1, com.in28minutes.package2" />
The IoC (Inversion of Control) container, represented in Spring by the class ApplicationContext, is the brain behind all of it. It all comes down to using reflection in a really powerful way.
To simplify, let's consider the following steps (all done through reflection):
Search all classes in the classpath
From those classes, get all classes annotated with #Component
For each class annotated with #Component, create a new instance of that class
Check for dependencies, i.e, for each created instance, check all fields annotated with #Autowired and create an instance for each one of them.
Keep everything in the context so they can be used later.
The remaining of this answer is an oversimplified version of how this happens as if we did it ourselves. Thankfully, Spring exists and we don't need to do this ourselves.
The annotations
#Retention(RetentionPolicy.RUNTIME)
public #interface Node {}
#Retention(RetentionPolicy.RUNTIME)
public #interface Wire { }
Some annotated classes for testing
#Node
public class ServiceA {
#Wire
private ServiceB serviceB;
public void doAStuff() {
System.out.println("A stuff");
serviceB.doBStuff();
}
}
#Node
public class ServiceB {
public void doBStuff() {
System.out.println("B stuff");
}
}
The IoC Container
import org.reflections.Reflections;
/* dependency org.reflections:reflections:0.9.12 */
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class IoC {
private final Map<Class<?>, Object> allNodes = new HashMap<>();
public void start() {
Reflections reflections = new Reflections(IoC.class.getPackageName());
Set<Class<?>> nodeClasses = reflections.getTypesAnnotatedWith(Node.class);
try {
for (Class<?> c : nodeClasses) {
Object thisInstance = c.getDeclaredConstructor().newInstance();
for (Field f : c.getDeclaredFields()) {
f.setAccessible(true);
if (f.getDeclaredAnnotation(Wire.class) != null) {
Object o = f.getType().getDeclaredConstructor().newInstance();
f.set(thisInstance, f.getType().cast(o));
}
}
allNodes.put(c, thisInstance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public <T> T getNodeByType(Class<T> cls) {
return cls.cast(allNodes.get(cls));
}
}
And the main class to get it all started.
public class Application {
public static void main(String[] args) {
IoC ioc = new IoC();
ioc.start();
ServiceA serviceA = ioc.getNodeByType(ServiceA.class);
serviceA.doAStuff();
}
}
This will output:
A stuff
B stuff
Of course, Spring is a lot more powerful (and robust) than this. It allows for custom package scanning using #ComponentScan, beans of the same type with different names, singleton/prototype scoped beans, constructor wiring, properties files injection, amongst many other things. When it comes to Spring Boot, the #SpringBootApplication annotation make sure it finds and wire all #Controller annotated classes and set up a Netty/Jetty/Tomcat embedded server to listen to the requests and redirect to the proper controller based on the annotated types.
Well where to search for the beans is defined by the #ComponentScan which can be annotated on the #Configuration class that is used to bootstrap Spring.
For example , it has an attribute called scanBasePackages which tells Spring to scan the beans (A class that is annotated with #Component or its sterotypes such as #Service , #Repository , #Controller etc. ) from certain packages and its sub-packages only.
Then for each bean that are registered , it goes on see if there are any methods annotation with #Bean.If yes, also register them as beans.

Spring boot not scanning components and configurations

I have a multi-module project:
TOP_LEVEL
|-> core
|-> assetManager
'-> requestManager
So, I have a core module which has Application class in the core module.
In my assetManger build.gradle I compile(project(:core))
The application main class is in package : com.test.companydomain.core of the core module.
This Application class is annotated with
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.test.companydomain"})
#EntityScan(basePackages = {"com.test.companydomain", "com.test.companydomain.assetManager"})
#EnableJpaRepositories
class Application {
}
There is a class ClientUtils in assetManager module in the package : com.test.domain.assetManager.server.common.utils;
annotated with :
#Slf4j
#Configuration
#Component("clientUtils")
There are beans that I am creating in this class and It uses other configuration classes for autowiring and creating beans.
The beans are not getting generated as of now from this ClientUtils class.
What can be a possible issue with this?
The error i see is
APPLICATION FAILED TO START
Description:
Field locationService in com.test.companydomain.assetManager.server.vendor.converter.ExternalVendorPojoConversionHelper required a bean of type
'com.test.companydomain.assetManager.server.common.utils.client.LocationService' that could not be found.
This class LocationService is also annotated with #Component for spring to create its bean.
In your application , the main class is present in the package com.test.companydomain.core and by default springboot scans all classes and packages under the current package of the main application for autowiring beans. So , you have provided the annotation #ComponentScan to explicitly tell spring to scan other packages as well.But your util class is in the package com.test.domain.assetManager.server.common.utils , which is not included in the #ComponentScan annotation, so it is not taken up for component scanning.
Could you try adding the package com.test.domain to the component scan in main class like :
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.test.companydomain","com.test.domain"})
#EntityScan(basePackages = {"com.test.companydomain", "com.test.companydomain.assetManager","com.test.domain"})
#EnableJpaRepositories
class Application {
}
If you are using Spring Boot, you should consider using #SpringBootApplication annotation and configure base packages to scan with scanBasePackages instead of #ComponentScan:
package com.test.companydomain.core;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages = "com.test.companydomain")
#EntityScan({"com.test.companydomain", "com.test.companydomain.assetManager"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
By default, Spring Boot scans only package of the class annotated with #SpringBootApplication and its sub-packages.
Also, #Configuration is meta-annotated with #Component, so #Configuration classes are candidates for component scanning and should not be explicitly annotated with #Component:
package com.test.domain.assetManager.server.common.utils;
#Configuration
public class ClientUtils {
#Autowired
private ClientProperties properties;
#Bean
public TestClient testClient() {
return new TestClient(properties); //example
}
}
and
package com.test.companydomain.assetManager.server.common.utils.client;
#Component
public class LocationService {
//...
}

Element Cannot be Resolved as Variable error in a custom annotation for method interception

New to Spring and AOP programming. Working on a spring AOP tutorial to write aspects that intercept method calls. Would like to enable time logging.
As instructed by the tutorial I created a custom annotation for logging and an aspect to define what should be done when this annotation is called.
The code below is the TrackTime annotation:
package com.in28minutes.springboot.tutorial.basics.example.aop;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface TrackTime {}
However Eclipse is displaying the errors –
“Element Cannot be Resolved as Variable/Retention Cannot be resolved to a variable”
I then created an aspect called MethodExecutionCalculationAspect with the ‘TrackTime’ annotation.
#Around("#annotation(com.in28minutes.springboot.tutorial.
basics.example.aop.TrackTime)")
MethodExecutionCalculationAspect
package com.in28minutes.springboot.tutorial.basics.example.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
#Aspect
#Configuration
public class MethodExecutionCalculationAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Around("#annotation
(com.in28minutes.springboot.tutorial.basics.example.aop.TrackTime)")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
joinPoint.proceed();
long timeTaken = System.currentTimeMillis() - startTime;
logger.info("Time Taken by {} is {}", joinPoint, timeTaken);
}
}
#Around uses an around advice. It intercepts the method call and uses joinPoint.proceed() to execute the method.
#annotation(com.in28minutes.springboot.tutorial.basics.example.aop.TrackTime) is the pointcut to define interception based on an annotation — #annotation
followed by the complete type name of the annotation.
Once I correct the annotation and the advice, I’m hoping to use the annotation on methods for time tracking. as shown below:
#Service
public class Business1 {
#TrackTime
public String calculateSomething(){
Any help would be appreciated.
Information about the project is as follows:
SpringBootTutorialBasicsAplication.java:
The Spring Boot application class generated with Spring Initializer. This class acts as the launching point for the application.
• pom.xml: Contains all the dependencies needed to build this project using Spring Boot Starter AOP.
• Business1.java, Business2.java, Dao1.java, Dao2.java: Business classes are dependent on DAO classes.
• We would write aspects to intercept calls to these business and DAO classes.
• AfterAopAspect.java: Implements a few After advices.
• UserAccessAspect.java: Implements a Before advice to do an access check.
• BusinessAopSpringBootTest.java: The unit test that invokes the business methods.
• Maven 3.0+ is your build tool
• Eclipse.
• JDK 1.8+
Your TrackTime is missing imports for RetentionPolicy and Target.
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

Spring data repository not found at compile time

I am trying to use Spring data and repositories in a Spring Boot application, but I have an error when compiling the project.
Here is my Entity :
package fr.investstore.model;
import javax.persistence.Id;
...
#Entity
public class CrowdOperation {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Long id;
#Enumerated(EnumType.STRING)
public RepaymentType repaymentType;
...
}
And the corresponding Repository:
package fr.investstore.repositories;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import fr.investstore.model.CrowdOperation;
public interface CrowdOperationRepository extends CrudRepository<CrowdOperation, Long> {
}
I use it in a WS controller, generating a repository through the Autowired annotation:
package fr.investstore.ws;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
...
#Controller
#EnableAutoConfiguration
public class SampleController {
#Autowired
private CrowdOperationRepository crowdOperationRepository;
#RequestMapping(path = "/", method = RequestMethod.GET)
#ResponseBody
public String getOperations(#RequestParam(required=true, defaultValue="Stranger") String name) {
crowdOperationRepository.save(new CrowdOperation());
return "Hello " + name;
}
}
And the code of the application:
package fr.investstore;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import fr.investstore.ws.SampleController;
#SpringBootApplication
public class InvestStoreApplication {
public static void main(String[] args) {
SpringApplication.run(SampleController.class, args);
}
}
But when compiling the project I get:
APPLICATION FAILED TO START
Description: Field crowdOperationRepository in
fr.investstore.ws.SampleController required a bean of type
'fr.investstore.repositories.CrowdOperationRepository' that could not
be found.
Action: Consider defining a bean of type
'fr.investstore.repositories.CrowdOperationRepository' in your
configuration.
Woudn't Spring automatically generate a bean for the repository through the interface?
How can I resolve this?
EDIT: I also tried to put the Repository annotation (from org.springframework.stereotype.Repository) onto CrowdOperationRepository, but I got the same error
While creating a spring-boot application, we need to keep some point in our mind like
Always keep main class (class with `#SpringBootApplication annotation) on the top level package and other classes should lie under sub-packages.
Always mark your bean classes with proper annotation e.g. all repositories should be marked by #Repository annotation, all service implementation classes should be marked with #Service, other component classes should be marked by #Component, class which defines our beans should be marked as #Configuration
Enable the feature which you are using e.g. #EnableJpaRepositories, #EnableTransactionManagement, #EnableJpaAuditing, these annotations also provides functionality which let us define which package spring needs to scan.
So in your case, you need to mark InvestStoreApplication class with #EnableJpaRepositories annotation and CrowdOperationRepository with #Repository.
you have to tell your spring boot application to load JPA repositories.
copy this one to your application class
it will auto-scan your JPA repository and load it in your spring container even if you do not define your interface with #Repository it will wire that bean in your dependent class.
#EnableJpaRepositories(basePackages = { "fr.investstore.repositories" })
Thank to #JBNizet for his comment, that made it working.
I create this answer since he did not:
Replace SpringApplication.run(SampleController.class, args); with SpringApplication.run(InvestStoreApplication.class, args);. And remove the useless #EnableAutoConfiguration on your controller.
Annotating your entity class as shown as spring hint below to allow spring get a valid repository bean
Spring Data JPA - Could not safely identify store assignment for repository candidate interface com.xxxxx.xxxxRepository.
If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: javax.persistence.Entity, javax.persistence.MappedSuperclass (preferred),
or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository.
2022-05-06 12:32:12.623 [ restartedMain] INFO [.RepositoryConfigurationDelegate:201 ] - Finished Spring Data repository scanning in 3 ms. Found 0 JPA repository interfaces.

#WebMvcTest mapperscan conflict

My project used spring-boot (1.4.0.release) and mybatis-spring-boot-starter. When I try to have some test code for controller, I always get a exception
Caused by: java.lang.IllegalArgumentException: Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
at org.springframework.util.Assert.notNull(Assert.java:115)
at org.mybatis.spring.support.SqlSessionDaoSupport.checkDaoConfig(SqlSessionDaoSupport.java:75)
at org.mybatis.spring.mapper.MapperFactoryBean.checkDaoConfig(MapperFactoryBean.java:74)
at org.springframework.dao.support.DaoSupport.afterPropertiesSet(DaoSupport.java:44)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
... 42 more`
But when I comment #MapperScan("com.toby.mapper"), it runs very well.
Here is my example class:
#MapperScan("com.toby.mapper")
#EnableTransactionManagement
#EnableConfigurationProperties(AppConfig.class)
#SpringBootApplication(scanBasePackages = "com.toby.configuration,com.toby.web.controller,com.toby.service,com.toby.dao")
public class Example {
public static void main(String[] args) throws Exception {
//new SpringApplicationBuilder().sources(Example.class).run(args);
SpringApplication application=new SpringApplication(Example.class);
application.addInitializers(new PropertyPasswordDecodingContextInitializer());
application.run(args);
}
}
Here is my test code:
package com.toby.web.controller;
import com.toby.common.config.AppConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
/**
* Created by Toby on 2016/8/10.
*/
#RunWith(SpringRunner.class)
#WebMvcTest(value = MyRestController.class)
public class MyRestControllerTests {
#Autowired
private MockMvc mvc;
#MockBean
private AppConfig appConfig;
#Test
public void testHome() throws Exception {
/*this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk()).andExpect(content().string("Honda Civic"));*/
}
}
I guess you've updated the description or I didn't read it properly the first time. #MapperScan is a mybatis specific annotation that triggers something but is missing some guard of some sort.
We had the same problem in boot actually. Let's say you put #EnableCaching on your main app. Because slicing disables all auto-configurations but a list of specific ones, the cache auto-configuration would not kick in and you'll get an exception because the CacheManager isn't found. To fix that issue, we've started to create some annotation to easily enable those. If you look at WebMbcTest you'll see it's annotated with AutoConfigureCache that's going to provide a dummy no-op cache manager unless specified otherwise.
Your problem is that the mybatis support is a third party integration and there isn't any support for that. Some solutions:
Change #WebMbvcTest to provide the class of another configuration class, effectivly disabling the use of your main spring boot app. Of course that class shouldn't define the #MapperScan annotation
Move the MapperScan (and anything that's not required with slicing) to another Configuration class. It could be a class in the same package as your app. Slicing won't scan those by default so you'll be fine. It's by far the easiest
Create an issue in the mybatis support so that they improve the auto-configuration to back-off (prevent this exception). I am not sure that's possible actually
Long story short, since #MapperScan is a way to tell mybatis to scan your entities, maybe you shouldn't add it on your main boot app if you use slicing. Because your #WebMbcTest doesn't want to use that obviously.

Resources