Understanding Spring Bean Fundamentals

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:

  1. Bean Factory

  2. Bean Creation

  3. Bean Injection

  4. Lifecycle Management

  5. Scopes and Singleton Pattern

  6. Common Annotations

  7. 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:

  1. 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.

  1. 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:

  1. Annotation injection - see common annotations later
  2. Method injection - subdivided into constructor and setter injection
  3. 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:

  1. Side effect exploitation - executing additional code in methods
  2. Cleaner code without pervasive bean annotations
  3. 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 @Primary annotation during bean definition to designate a primary bean—the main solution for unmodifiable third-party components
  • Combining @Qualifier with 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.

  1. 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
  1. 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.

  1. 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.

Tags: Spring java dependency-injection beans spring-boot

Posted on Thu, 29 Oct 2026 16:18:48 +0000 by iloveny