In the Spring framework architecture, beyond the foundational core components, beans and their factories represent the most critical elements.
The remaining components include AOP, design patterns, and higher-level modules such as DATA and WEBMVC...
For clarity, this article collectively refers to beans and bean factories simply as "beans."
Etymologically, "bean" means legume. To align with its practical function, I prefer translating it as "caching object instances," though conventionally, we'll continue calling them beans.
In this context, beans primarily refer to bean instances and bean factories.
Within the Spring ecosystem, beans serve to instantiate and manage class objects, achieving the core objective of enhanced creation efficiency through spatial trade-offs for temporal gains.
This principle essentially defines all caching mechanisms.
Spring has refined bean handling to an art form, incorporating beans throughout its architecture.
In JavaEE development with Spring, avoiding beans is neither feasible nor advisable.
Therefore, understanding fundamental bean concepts is essential for effective application development.
Note: Since XML-based (or non-Boot) configuration approaches are largely obsolete, this article excludes historical XML configurations.
Additionally, all content and examples are based on Spring 6.x + JDK 17.
For application development purposes, mastering the following knowledge areas should suffice:
-
Bean Factory
-
Bean Creation
-
Bean Injection
-
Lifecycle Management
-
Scopes and Singleton Pattern
-
Common Annotations
-
Bean Factory
If you're familiar with common design patterns, you understand the concept of a factory.
Indeed, bean factories follow this pattern, albeit with more complex implementation. At its core, it uses Java's map data structure to store created object instances, providing methods for CRUD operations to enable programmatic access to these structures.
1.1 Multiple Bean Factories Are Unnecessary
While Spring doesn't prevent creating multiple factories, most business scenarios lack justification for managing different bean categories through separate factories.
As previously mentioned, Spring's core value lies in JavaEE/web application development.
When most objects relate to web functionality, consolidating web-related beans in a single factory generally offers more benefits than drawbacks.
Creating additional factories beyond the default is posssible, typically achieved by instantiating extra application contexts to generate new bean factories.
1.2 Default Bean Factory
By default, Spring creates an org.springframework.beans.factory.support.DefaultListableBeanFactory instance.
Though designed per factory patterns, this class provides extensive data structures for storing various information and numerous methods implementing diverse functionalities, including the typical getBean method.
1.3 Application Context and Bean Factory
In Spring (unless specified otherwise referring to Spring web applications), the bean factory resides within the application context.
This context is org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext (for WebFlux, it would differ).
This context contains the default bean factory: DefaultListableBeanFactory.
(See diagram showing AnnotationConfigServletWebServerApplicationContext inheriting from GenericApplicationContext)
1.4 Aware Interface
Spring's Aware interface aims to inject bean objects, typically injecting application contexts or bean factories.
The Aware interface has numerous implementations:
- Creating Beans
Spring offers multiple bean creation approaches. Note this refers to "creation" or definition, excluding injection.
Creation means instantiating Java classes and placing these instances in the bean factory.
Injection involves assigning bean instances as properties of other instances.
Broadly, bean creation falls into two categories: automatic and manual.
- Automatic creation equals annotation-based creation
- Manual creation requires explicit coding
Manual creation is rare, with few valid business scenarios or justifications.
While articles demonstrating manual creation exist, I generally discourage this approach since it undermines beans' primary purpose: managing instances efficiently while improving performance and reducing memory usage.
For annotation-based bean creation, see the "Common Annotations" section later.
Code Structure Considerations
Some organizations enforce line limits for classes or methods.
Examining Spring source code reveals many instances violating such constraints. Thus, length restrictions should be approximate guidelines requiring case-by-case analysis.
I oppose excessively short methods since modern languages incur performance costs during function calls due to stack operations.
However, I understand organizational rules addressing varying developer skill levels and responsibilities through blanket measures.
No specific line limit applies to beans; it depends on context and organizational requirements.
- Injecting Beans
As mentioned, bean injection involves assigning bean instances as components (properties) to other instances—essentially property assignment.
3.1 Injection Methods
Few injection approaches exist, commonly:
- Annotation injection - see common annotations later
- Method injection - subdivided into constructor and setter injection
- Utility class injection - retrieving beans via tools and setting instance properties
Generally, annotation and utility class injections are most common.
Annotation injection is recommended for simplicity, time savings, and minimal side effects.
Utility class injection mainly addresses bean usage in non-autocreated instances.
Method injection isn't worthless, offering three main benefits:
- Side effect exploitation - executing additional code in methods
- Cleaner code without pervasive bean annotations
- Easier migration to non-Spring systems
The last benefit inadequately justifies abandoning @Autowired. Some articles advocate switching to method injection, which I find unconvincing.
Most projects adopting Spring never consider migrating, making this argument unpersuasive.
I never encourage method injection since my work focuses on application development with no migration intentions.
Theoretically, following rules is good practice, but engineering realities require flexibility, similar to database design deviating from third normal form.
This approach saves time—costs often stem from factors beyond coding itself.
Projects have lifecycles (typically 5 years, up to 10), usually lacking genericity requirements, so migration compatibility isn't prioritized.
Business systems rarely employ groundbreaking technologies; most Chinese IT companies focus on quick profits rather than innovation.
Therefore, if developers properly design services, there's no need for method injection—use @Autowired confidently without abuse.
Despite discouraging method injection, adhering to design principles, patterns, and code standards remains crucial for professional integrity and optimal outcomes.
Supplementary note: In Spring 6.x, constructor injection doesn't require @Autowired, but setter methods still do. Much online content is outdated; engineers should verify independently.
Example: Spring 6.x constructor injection without @Autowired:
@Service
public class StudentService {
private StudentDaoService stuDaoService;
public StudentService(StudentDaoService stuDaoService) {
this.stuDaoService = stuDaoService;
}
public Map<String,SchoolStudent> getAll(){
return this.stuDaoService.get();
}
}
3.2 Injection Qualification
Handling Multiple Implementations
During injection, bean factories search by type by default, causing issues when interfaces have multiple implementations:
Error example: "Parameter 0 of method xxx required a single bean, but *** were found" (method injection)
Or during @Autowired injection.
A typical scenario occurs with multiple data sources: "Parameter 0 of method sqlSessionFactory... required a single bean, but 3 were found"
Many components use @Bean annotations defining beans through method injection.
Component designers sometimes omit qualifier annotations, leading to errors with multiple implementations.
Solutions include:
- Using
@Primaryannotation during bean definition to designate a primary bean—the main solution for unmodifiable third-party components - Combining
@Qualifierwith name attributes to specify target bean names—used when controlling bean naming
3.3 Injection Order
Injection order is rarely used in practice, mainly by Spring internally.
Relevant annotations include: @Order, @Priority (JSR250 standard).
Using @Order implies defining multiple interface implementations (beans) requiring ordered collection injection.
Lower numbers indicate earlier positions—a widely accepted convention.
This annotation enables flexible functionality implementation.
Spring uses this for event sequencing and AOP execution order determination.
Overall, @Order serves internal Spring purposes for ordered collections, determining other sequences based on element order.
Business development rarely requires this, though emulation is possible.
// Interface
package study.example.school.service;
public interface LearnService {
public String getName();
public void learn();
}
// Implementation 1 - LearnChineseServiceImpl
package study.example.school.service.impl;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import study.example.school.service.LearnService;
@Order(1)
@Service
public class LearnChineseServiceImpl implements LearnService {
@Override
public String getName() {
return "中文";
}
@Override
public void learn() {
System.out.println("少读诗书陋汉唐");
}
}
// Implementation 2 - LearnMathServiceImpl
package study.example.school.service.impl;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import study.example.school.service.LearnService;
@Order(2)
@Service
public class LearnMathServiceImpl implements LearnService {
@Override
public String getName() {
return "数学";
}
@Override
public void learn() {
System.out.println("1+1=2;1-1=0");
}
}
// Injecting List<LearnService> bean
@Service
public class StudentService {
private List<LearnService> learnServices;
@Autowired
public void setLeanServices(List<LearnService> learnServices) {
// Add code here, e.g., sysout to verify order
this.learnServices = learnServices;
}
}
3.4 Circular References
Spring 6.x discourages circular references between objects. Such references pose challenges for coding and serialization.
Spring provides parameters to resolve this: spring.main.allow-circular-references=true
However, circular references complicate engineering tasks and may slow startup times slightly.
Therefore, avoid circular references during daily coding. If unavoidable, Spring can handle them.
- Lifecycle Management
Usually, we don't concern ourselves with bean lifecycles, assuming beans create at startup and disapppear with factory shutdown.
We treat beans like pens—grab when needed, set aside when not, without tracking location or existence.
Unless resource impact is significant, we don't consider lazy loading.
Two aspects warrant attention: when beans initialize/completion occurs; when beans destroy
We often want initialization operations after certain beans ready or resource release before destruction.
While @PostConstruct/@PreDestroy annotations exist, for complex systems, I prefer Spring events for initialization.
For example, using ApplicationReadyEvent for centralized initialization offers better control over execution sequence and easier maintenance compared to scattered initialization code.
If initialization order isn't critical, @PostConstruct-like annotations work fine—modern IDEs help with maintenance.
Similarly, use ContextClosedEvent for pre-shutdown operations.
| Index | Name | Type | Purpose | Invocation Timing | Notes |
|---|---|---|---|---|---|
| 1 | InstantiationAwareBeanPostProcessor | Interface | Personalized initialization work Some utility | Before/after bean initialization | Bean properties might not be initialized |
| 2 | BeanPostProcessor | Interface | Modify bean instances Limited utility | Before/after bean creation (properties completed) | Uncertain if both methods receive fully injected beans—in test cases, they do. |
| 3 | ApplicationContextAwareProcessor | Class | Inject ConfigurableApplicationContext or properties into Aware-implementing classes | After bean instance (with properties) creation, if class implements Aware | |
| 4 | DestructionAwareBeanPostProcessor | Interface | Perform actions before destruction | Before class destruction | |
| 5 | PostConstruct | Annotation | After initialization | ||
| 6 | PreDestroy | Annotation | Before destruction | ||
| 7 | Multiple Aware Interfaces | Interface | Processed by ApplicationContextAwareProcessor, injecting context info | Roughly after initialization | |
| 8 | org.springframework.context.ApplicationListener<E> | Interface | Application events triggered under specific conditions. Example: Operations after startup or before shutdown Centralize tasks for better coding/management instead of complex auto-configurations. | After specific events complete |
- Scopes and Singletons
Scope (SCOPE) receives little attention, like bean factories.
org.springframework.beans.factory.config.ConfigurableBeanFactory defines two classic scopes: singleton, prototype.
Singleton (single item/person)—commonly translated as singleton, meaning one instance per bean factory for a given class
Prototype (original model/prototype)—unclear foreign interpretation, possibly "original sample (create one per use)," domestically interpreted as multi-instance since each injection creates a new instance
Later additions included scope variations similar to prototypes: request, session, application, global session
Finally, Spring thoughtfully provides custom scope mechanisms, though rarely used—showcasing Spring's extensibility consideration.
However, most business applications don't use scopes beyond singleton. I discourage using prototype-like scopes for bean management.
Why choose singleton? It simplifies design, reduces errors, and eases maintenance.
Most business systems process data without internet-scale performance demands, delegating consistency tasks to databases and reducing programming complexity.
Primary singleton rule: Avoid class variables (except utilities)—data should pass via parameters or DAO components.
Following this principle ensures safe singleton bean usage.
- Common Annotations
The table below lists common Spring bean-related annotations:
| # | Code | Name | Category 1 | Standalone Use | Class | Method | Field | Parameter | Source | Description |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | @Component | Component | Definition | Yes | ✔ | |||||
| 2 | @Repository | Repository | Definition | Yes | ✔ | Component specialization indicating repository/storage (data) | ||||
| 3 | @Controller | Controller | Definition | Yes | ✔ | Component specialization indicating controller (view) | ||||
| 4 | @Service | Service | Definition | Yes | ✔ | Component specialization indicating business logic | ||||
| 5 | @Bean | Bean Instance | Definition | Maybe? | ✔ | ✔ | Defines a bean on methods, usually paired with Configuration Note: Though definable on types, rarely used. | |||
| 6 | @Configuration | Configuration | Configuration Creation | Yes | ✔ | Indicates current class is configuration, also generates bean | ||||
| 7 | @Import | Import Configuration | Configuration Import | No | ✔ | Usually paired with Configuration | ||||
| 8 | @PropertySource | Property Resource | Property Creation | No | ✔ | Usually paired with Configuration Specifies config file | ||||
| 9 | @PropertySources | Property Resource Set | Property Creation | No | ✔ | Usually paired with Configuration, PropertySources Specifies config files @PropertySources({<br></br>@PropertySource("classpath:application.properties"),<br></br>@PropertySource("classpath:custom.properties")<br></br>}) |
||||
| 10 | @ConfigurationProperties | Configuration Properties | Property Creation | No | ✔ | Usually paired with Configuration Specifies part of config file | ||||
| 11 | @Conditional | General Condition Qualifier | Creation Condition | No | ✔ | ✔ | Generates bean based on conditions, requires Condition interface cooperation. Uncertain impact on injection. | |||
| 12 | @ConditionalOnMissingBean | Absence Qualifier | Creation Condition | No | ✔ | ✔ | Similar to above, condition being absence of specific bean | |||
| 13 | @ConditionalOnxxxx | Specific Condition Qualifier | Creation Condition | No | ✔ | ✔ | Same as above | |||
| @DependsOn | Dependency Qualifier | Creation Condition | No | ✔ | ✔ | Ensures current bean initializes after others, controlling initialization/loading order. Usually paired with bean-generating annotations. | ||||
| 21 | @Profile | Profile Qualifier | Creation Condition | No | ✔ | ✔ | Bean generation/injection only when specific profiles load. Requires spring.profiles.active coordination Usually paired with Configuration, Bean | |||
| 22 | @AutoConfigureAfter | Post Auto-configuration Recognition | Creation Sequence | No | ✔ | |||||
| 23 | @AutoConfigureBefore | Priority Auto-configuration Recognition | Creation Sequence | No | ✔ | |||||
| 24 | @AutoConfigureBefore | Creation Sequence | No | ✔ | ||||||
| 25 | @Scope | Scope Binding Marker | Target & Scope Creation | No | ✔ | ✔ | Used on classes | |||
| 26 | @Target | Target Binding Marker | Target & Scope Creation | No | ✔ | JAVA | Used on classes, specifies available targets | |||
| 27 | @Lazy | Deferred Specification | Creation Timing | No | ✔ | ✔ | Lazy loading Created only when explicitly injected (various annotations) or accessed via Bean factory methods. | |||
| 28 | @ComponentScan | Component Scan | Creation Scan | No | ✔ | |||||
| 29 | @ComponentScans | Component Collection Scan | Creation Scan | No | ✔ | |||||
| 30 | @MapperScan | Mapper Scan | Creation Scan | Yes | ✔ | Mybatis | ||||
| 31 | @SpringBootApplication | Application Startup | Creation Scan | Yes | ✔ | |||||
| 51 | @Resource | Resource | Injection | Yes | ✔ | ✔ | ✔ | JSR-250 | Component specialization indicating resource | |
| 52 | @Resources | Resource | Injection | Yes | ✔ | JSR-250 | ||||
| 53 | @Autowired | Auto-binding | Injection | Yes | ✔ | ✔ | ✔ | ✔ | If used on methods, executes setter during initialization, attempts auto-injecting bean parameters Sometimes used for initialization operations (not recommended) Theoretically usable for method parameter injection (not recommended) Note: Though definable on types, rarely used. | |
| 54 | @Qualifier | Name Qualifier | Injection Qualification | No | ✔ | ✔ | ✔ | ✔ | When interfaces have multiple implementations (beans), specifies particular targets via bean names. | |
| 55 | @Primary | Priority Qualifier | Injection Qualification | No | ✔ | ✔ | When interfaces have multiple implementations (beans), designates primary bean. Selects default bean in auto-injection scenarios. Often paired with Component, Bean | |||
| 56 | @Order | Sequence Specification | Injection Order | No | ✔ | ✔ | ✔ | Injection arrangement order. Note: Not creation order "Arrangement order" means injection order. | ||
| 57 | @Priority | Sequence Specification | Injection Order | No | ✔ | ✔ | ✔ | JSR-250 | Injection order, not creation order Serves Primary-like role. Mainly effective for single injections | |
| 91 | @PostConstruct | Post-construction | Weakly Related - Lifecycle | Yes | ✔ | Java Standard | Executes after bean initialization | |||
| 92 | @PreDestroy | Pre-destruction Processing | Weakly Related - Lifecycle | Yes | ✔ | Java Standard | Executes before bean destruction | |||
| 93 | @Retention | Persistence | Weakly Related - Lifecycle | No | ✔ | Meta-annotation. Specifies retention policy for other annotations. Retention policy determines annotation validity timing/location |
This covers most bean-related annotations, though not exhaustive. Some annotations vary by version, so absence in older Spring versions isn't surprising.