To register spring beans, we can mix XML's <context:component-scan> and JavaConfig configuration. We can even avoid XML altogether by using @ComponentScan .
With @ComponentScan we can still mix the factory approach . In factory approach we annotate classes with @Configuration and define methods annotated with @Bean which return bean instances.
For component scanning to work we must annotate our @Configuration class with @ComponentScan and annotate our bean classes with one of the stereotype annotations
- Component
- Controller
- Repository
- Service
Classes annotated with one of the above are candidate for spring container registration when using scanning. The most important is Component annotation. The rest are specialization of Component . Each one is annotated with Component itself. They represent the roles in the overall application design.
you can annotate your component classes with @Component, but by annotating them with @Repository, @Service, or @Controller instead, your classes are more properly suited for processing by tools or associating with aspects. For example, these stereotype annotations make ideal targets for pointcuts. It is also possible that @Repository, @Service, and @Controller may carry additional semantics in future releases of the Spring Framework. Thus, if you are choosing between using @Component or @Service for your service layer, @Service is clearly the better choice.
Along with above annotations we can tag our beans with any of @Lazy, @DependsOn, @Scope etc to specify their specific behavior.
Following diagram summarizes how @ComponentScan works:
Spring Stereotype Annotations are not Inherited
None of the stereotype annotations are tagged with @Inherited . That means we cannot expect sub classes to inherit the stereotype annotations. We have to add them explicitly to each sub classes. Same is true for other annotations like Scope , Lazy etc. As per general Java concept, we can make use of method/fields/constructor level annotation from super classes, i.e. we don't have to repeat them in subclasses. In case of method overriding (typically setters in Spring), we have to explicitly add annotations in overridden methods, even though they are already present in the original super class methods.
Example
A singleton bean
package com.logicbig.example.bean;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
@Component
public class MySingletonBean {
@PostConstruct
public void init() {
System.out.println("initializing " +
this.getClass().getSimpleName());
}
}
Service beans
package com.logicbig.example.bean;
public interface MyService {
String getMessage();
}
package com.logicbig.example.bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
@Lazy
@Service("basic-service")
public class ServiceImplA implements MyService {
@PostConstruct
private void init() {
System.out.println("initializing lazily " +
this.getClass().getSimpleName());
}
@Override
public String getMessage() {
return "Message from " + getClass().getSimpleName();
}
}
package com.logicbig.example.bean;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
@Service
public class ServiceImplB implements MyService {
@PostConstruct
private void init(){
System.out.println("initializing at start up " +
this.getClass().getSimpleName());
}
@Override
public String getMessage() {
return "Message from "+getClass().getSimpleName();
}
}
A prototype bean
package com.logicbig.example.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MyPrototypeBean {
@Autowired
@Qualifier("basic-service")
private MyService myService;
public void doSomething(){
System.out.println(myService.getMessage());
}
}
@Configuration class with @ComponentScan
package com.logicbig.example.app;
import com.logicbig.example.bean.MyPrototypeBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.logicbig.example.bean")
public class AppConfig {
public static void main(String... strings) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println("-- Spring container started and is ready --");
MyPrototypeBean bean = context.getBean(MyPrototypeBean.class);
bean.doSomething();
}
}
Outputinitializing MySingletonBean initializing at start up ServiceImplB -- Spring container started and is ready -- initializing lazily ServiceImplA Message from ServiceImplA
Example ProjectDependencies and Technologies Used: - spring-context 6.1.2 (Spring Context)
Version Compatibility: 3.2.9.RELEASE - 6.1.2 Version compatibilities of spring-context with this example: Versions in green have been tested.
- jakarta.jakartaee-api 10.0.0 (Eclipse Foundation)
- JDK 17
- Maven 3.8.1
|