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

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.

Related

Should #ComponentScan stay in the class containing the main method?

I know that #ComponentScan with #Configuration tell Spring where to look for beans.
#ComponentScan
#Configuration
public class MyApp{
...
}
What I do not understand is on which class I have to put these two annotations. Should they stay on the class containing the main method?
Like this
#ComponentScan
#Configuration
public class MyApp{
public static void main(String[] args) {
...
}
}
Or they can stay on whatever class of the application?
The question comes from the fact that Spring has to know the location of #ComponentScan... or is there an automatic way of detection of the #ComponentScan annotation which Spring is performing under the hood?
Hope to have explained myself!
You can put it wherever you want (I usually put mine in com.domain.project-name.config) and just specify the directories it should scan, for example if you want it to scan everything in project use
#ComponentScan("com.domain.project-name")
#Configuration
public class Config {
...
By default, ComponentScan scans all the annotated classes at the current directory level and below.
#Configuration annotation tells the Spring container that the class contains Spring bean configuration.
#ComponentScan annotation tells the Spring container that the annotated class to scan/searches for other annotations and components. You can also define package name to scan with the annotation like #ComponentScan("your.package.name") or you can give package/class names that need not be scanned.
Hence, you can put these annotations on any class that defines your bean configuration and could be required by spring container to parse and create objects for your entities/POJOs, services and DAOs.
To conclude, I would like to add #ComponentScan and other annotations are there for automatic detection. Else, you would need to define XMLs (that's what happens under the hood with annotations) for spring to read and perform these actions.
Using simple example. You can place #ComponentScan with #Configuration in any class which main method can scan.
Main class scans MyScan class which then scan for bean class.
package com.boot.spring;
#SpringBootApplication
#ComponentScan(basePackages = "com.boot.scan")
public class BootApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(BootApplication.class, args);
System.out.println(ctx.getBean("demoBean"));
}
}
Bean class is in different package
package com.boot.bean;
#Service
public class DemoBean {
}
Now, bean class is discovered through DemoScan class
package com.boot.scan;
#ComponentScan(basePackages = "com.boot.bean")
#Configuration
public class DemoScan {
}

Spring Boot - How to exclude one component during scan?

I have a JAR dependency that I am required to use. There is one component in that JAR that is interfering with a part of my Spring Boot application. I need to exclude that ONE component, and changing the component definition in the JAR dependency is not currently possible.
I have tried the following, but it does not work, the bean is still loaded:
#SpringBootApplication
#ComponentScan(useDefaultFilters = false, excludeFilters = [Filter(type = FilterType.ASSIGNABLE_TYPE, classes = [SomeBean::class])])
The bean is defined as:
#RestController
class SomeBean {
NOTE: These snippets are in Kotlin.
Try this
#SpringBootApplication(exclude = { SomeBean.class })
Can you do something like below to skip creating bean?
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;
#Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {}
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
registry.removeBeanDefinition("myBeanName");
}
}
Doesn't the #Bean/#Component/etc. or #Configuration have any Conditional annotation on it (e.g.: #ConditionalOnMissingBean, #ConditionalOnProperty, etc.)? That would be the right solution for this problem. Can you ask the maintainer to add proper conditions? So that you (and other users) don't need to hack this around.
There is a terrible hack though: BeanDefinitionRegistry#removeBeanDefinition

Spring boot cannot find beans

i have a Spring Boot project which has some external packages i need to import as Beans in the main application.
So i have my main application in com.package.app package and some classes (among which some repositories) in com.package.commons package.
In order to take these beans i have my main class annotated as follows:
#SpringBootApplication
#ComponentScan({ "com.package.commons" ,"com.package.app"})
#EnableScheduling
#EnableAsync
public class EmanagerApplication extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(EmanagerApplication.class, args);
}
}
But when i launch the application it may occur (not always but very ofter) that the start up fails with these kind of error:
Description:
Field repository in com.package.commons.service.BrandService required a bean of type 'com.package.commons.persistence.repository.BrandRepository' that could not be found.
Action:
Consider defining a bean of type 'com.package.commons.persistence.repository.BrandRepository' in your configuration.
My BrandRepository is annotated with #Repository and the service class with #Service
The really strange thing is that if i keep launching the app at the end it stars... but there is no reason for it...
If you're using JPA, you'll also need the #EnableJpaRepositories annotation.
Also consider to use #EnableTransactionManagement to enable declarative transaction handling.
E.g. use something like the following in the same package or a parent package where you have your JPA entities and JPA repositories (untested):
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.jta.JtaTransactionManager;
#Configuration
#EntityScan
#EnableJpaRepositories
#EnableTransactionManagement
public class HibernateConfig extends JpaBaseConfiguration {
public HibernateConfig(DataSource dataSource, JpaProperties properties, ObjectProvider<JtaTransactionManager> jtaTransactionManager,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
super(dataSource, properties, jtaTransactionManager, transactionManagerCustomizers);
}
#Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
#Override
protected Map<String, Object> getVendorProperties() {
return new HashMap<>();
}
}
And don't forget to annotate your #Service classes also with #Transactional.
If you confirm that the Application which with the startup method of this application is good, and confirm the #ComponentScan is good also. And the configuration file yaml or properties of JPA also good.
How about trying extends JPA Repository like this:
public class xxxResponsitory extends JpaRepository<T, E>{
...
}
Cause JpaRepository has already annotated with #Repository annotation, T means the type of Primary Key, I always use Integer or Long, autoboxing type. E means the main type of this repository.
Make an example:
Now we have an Entity type named User, the Primary key type of User is Long, I would write the repository like this:
public class UserRepository extends JpaRepository<Long, User>{
...
}
Don't need annotated anything, then, In the service class, #Autowried UserRepository, everything is good to run. But make sure the things that I talk at the start of my answer.
Hope this can help you.

Spring conditional component scan configuration

I have a configuration class which registers beans based on a very simple condition (checking a property value in application.properties). The configuration class and the condition are the following:
#Configuration
#Conditional(DatabaseConfigurationCondition.class)
#ComponentScan(basePackageClasses = DBConfigComponents.class)
public class DatabaseConfigurationLoader {
#Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
}
and
public class DatabaseConfigurationCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("configuration.type").contains("db");
}
}
In addition of the beans registered in this configuration class I have component scan which scans for other components. When the condition is not met, I expect the beans which are defined in the configuration class not to be registered (which happens to be a case), but also I expect other classes which are annotated with #Component (or #Repository, #Service, etc.. ) and are in same folder as DBConfigComponents.class marker interface not to be registered, which does not happen. Beans which are scanned are always registered, no matter if the condition is fulfilled or not.
When I put the #Conditional(DatabaseConfigurationCondition.class) on each #Component annotated class, than it's working correctly, but I don't want to put it on each class separately.
Any suggestion?
Fortunately, I managed to fix this. The problem in my case was that I had another #ComponentScan annotation placed in other configuration class in other Maven module - not conditional on any property. The components which are in same package as DBConfigComponents marker interface were actually scanned by the other configuration class.
The way #ComponentScan works is on package level. Although, in different Maven modules, both configuration classes were in same package. #ComponentScan works perfectly fine with #Conditional. No need #Conditional to be placed on each component separately.
The best way to achieve this is not to annotate these beans using #Component / #Service and #Repository annotations. Instead you should return these as part of the configuration you have setup which would be DatabaseConfigurationLoader. See sample below.
#Configuration
#Conditional(DatabaseConfigurationCondition.class)
public class DatabaseConfigurationLoader {
#Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
#Bean
public SomeService someService() {
return new SomeService();
}
#Bean
public SomeComponent someComponent() {
return new SomeComponent();
}
}
Note: Typically #Configuration with #Conditional are used in libraries that you want to include in your spring boot application. Such libraries should not share the same package as your spring boot application. Thus they should not be picked up by #ComponentScan annotation. Beans from libraries should not be annotated with #Component / #Service / #Repository annotations. Spring suggests using AutoConfiguration for that. See https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html & https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html
No need to implement Condition interface, you need to use '#ConditionalOnProperty' annotation:
#Configuration
#ComponentScan(basePackageClasses = DBConfigComponents.class)
#ConditionalOnProperty(name = "configuration.type", havingValue = "db")
public class DatabaseConfigurationLoader {
#Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
}
you can use 'prefix' instead of 'havingValue' depending on your needs.

#Autowired dependencies are null in compile time weaving of #Aspect class [duplicate]

I have the following spring configuration:
<context:component-scan base-package="uk.co.mysite.googlecontactsync.aop"/>
<bean name="simpleEmailSender" class="uk.co.mysite.util.email.simple.SimpleEmailSenderImplementation"/>
<aop:aspectj-autoproxy/>
Then I have an aspect:
#Aspect
public class SyncLoggingAspect {
#Autowired
private SimpleEmailSender simpleEmailSender
#AfterReturning(value="execution(* uk.co.mysite.datasync.polling.Poller+.doPoll())", returning="pusher")
public void afterPoll(Pusher pusher) {
simpleEmailSender.send(new PusherEmail(pusher));
}
}
This aspect works (I can hit a breakpoint on afterPoll) but simpleEmailSender is null. Unfortunately I cannot find clear documentation on why this is. (For the record, my simpleEmailSender bean exists and is correctly wired into other classes) The following things confuse me:
Is context:component-scan supposed to be picking up #Aspect? If it is then surely it would be a spring managed bean, thus autowired should work?
If context:component-scan isn't for creating aspects, how is my aspect being created? I thought aop:aspectj-autoproxy just creates a beanPostProcessor to proxy my #Aspect class? How would it do this if it isn't a spring managed bean?
Obviously you can tell I don't have an understanding of how things should be working from the ground up.
The aspect is a singleton object and is created outside the Spring container. A solution with XML configuration is to use Spring's factory method to retrieve the aspect.
<bean id="syncLoggingAspect" class="uk.co.demo.SyncLoggingAspect"
factory-method="aspectOf" />
With this configuration the aspect will be treated as any other Spring bean and the autowiring will work as normal.
You have to use the factory-method also on Enum objects and other objects without a constructor or objects that are created outside the Spring container.
For Spring Boot to use #Autowired with AspectJ I have found the following method.
In configuration class add your aspect:
#Configuration
#ComponentScan("com.kirillch.eqrul")
public class AspectConfig {
#Bean
public EmailAspect theAspect() {
EmailAspect aspect = Aspects.aspectOf(EmailAspect.class);
return aspect;
}
}
Then you can successfully autowire your services in your aspect class:
#Aspect
public class EmailAspect {
#Autowired
EmailService emailService;
Another option is to add #Configurable to your aspect class instead of messing around with XML.
Configuring #Autowired with java config only (so no XML based configuration) requires a bit of extra work than just adding #Configuration to the class, as it also needs the aspectOf method.
What worked for me was creating a new class:
#Component
public class SpringApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
And then use that in you aspect in conjunction with using #DependsOn #Configured and #Autowired:
#DependsOn("springApplicationContextHolder")
#Configuration
#Aspect
public class SomeAspect {
#Autowired
private SomeBean someBean;
public static SomeAspect aspectOf() {
return SpringApplicationContextHolder.getApplicationContext().getBean(SomeAspect.class);
}
The #DependsOn is needed because spring can't determine the dependency because the bean is used staticly.
I dont have 50 rep to comment on a question so here is another answer relating to #
Jitendra Vispute answer.
The official Spring doc mentions:
You may register aspect classes as regular beans in your Spring XML configuration, or autodetect them through classpath scanning - just like any other Spring-managed bean. However, note that the #Aspect annotation is not sufficient for autodetection in the classpath: For that purpose, you need to add a separate #Component annotation (or alternatively a custom stereotype annotation that qualifies, as per the rules of Spring’s component scanner).Source: Spring '4.1.7.Release' documentation.
This would mean that adding a #Component annotation and adding the #ComponentScan on your Configuration would make #Jitendra Vispute's example work. For the spring boot aop sample it worked, though I did not mess around with context refreshing.Spring boot aop sample:
Application:
package sample.aop;
#SpringBootApplication
public class SampleAopApplication implements CommandLineRunner {
// Simple example shows how an application can spy on itself with AOP
#Autowired
private HelloWorldService helloWorldService;
#Override
public void run(String... args) {
System.out.println(this.helloWorldService.getHelloMessage());
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleAopApplication.class, args);
}
}
The application should also run as plain Spring Framework application with the following annotations instead of #SpringBootApplication:
#Configuration
#EnableAspectJAutoProxy
#ComponentScan
and an AnnotationConfigApplicationContext instead of SpringApplication.
Service:
package sample.aop.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class HelloWorldService {
#Value("${name:World}")
private String name;
public String getHelloMessage() {
return "Hello " + this.name;
}
}
Monitor Aspect:
package sample.aop.monitor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class ServiceMonitor {
#AfterReturning("execution(* sample..*Service.*(..))")
public void logServiceAccess(JoinPoint joinPoint) {
System.out.println("Completed: " + joinPoint);
}
}
This blog post explains it really well. Due to the fact that aspect singleton is created outside spring container you'd need to use factory-method=”aspectOf” that is only available after it is woven in by AspectJ ( not Spring AOP ) :
Notice factory-method=”aspectOf” that tells Spring to use a real
AspectJ ( not Spring AOP ) aspect to create this bean. So that after
the aspect is woven in it has an
“aspectOf” method.
So that :
No matching factory method found: factory method 'aspectOf()' - That
would mean that the aspect was not woven by AspectJ weaver.
From my experience with spring 3.1, if I don't use #Autowired but traditional setter for dependency injection, it gets injected and works as expected without aspectJ weaver. Although I'm encountering problems with the aspect being singleton... It results in 'perthis' instantiation model.
.
Add #Component to aspect class and your dependencies should get injected automatically.
and add context:component-scan for package where your aspect is in spring context file.
#Component
#Aspect
public class SomeAspect {
/* following dependency should get injected */
#Autowired
SomeTask someTask;
/* rest of code */
}
Use compile time weaving, see for plugin example at: https://github.com/avner-levy/minimal_spring_hibernate_maven_setup/blob/master/pom.xml
The following combination of annotation and Spring config works for me thanks to notes above by Tobias/Willie/Eric:
Class:
package com.abc
#Configurable
#Aspect
public class MyAspect {
#Autowired
protected SomeType someAutoWiredField;
}
XML:
<context:spring-configured />
<context:component-scan base-package="com.abc" />
#Configurable(autowire = Autowire.BY_TYPE)
Add this annotation to your Aspectj class. Then it will be handled by Spring IOC.
For Spring Boot using #Autowired in #Aspect,
my way is using spring.factories configuration file
create a file named spring.factories in src/main/resources
the file content is as following
org.springframework.boot.autoconfigure.EnableAutoConfiguration=pack.age.to.YourAspect,pack.age.to.YourDAO

Resources