Spring原理:Bean详解(含源码分析)

一、Bean 的创建

在Spring框架中,Bean是构成应用程序的基本组件,Spring容器负责管理这些Bean的生命周期和配置。Spring通过DI和IoC减少组件间的耦合度。

在Spring框架中,创建Bean的方式主要有两种:通过@Component注解以及相关的派生注解(如@Service, @Repository, @Controller),和通过@Bean注解。这两种方式各有特点,适用于不同的场景。以下是对这两种方式的详细解释:

1. 使用@Component及其派生注解
  • @Component是一个通用的立体化注解,用于指示Spring框架该类是一个组件类,Spring应该为这个类创建Bean实例。

  • 这个注解及其派生注解(@Service, @Repository, @Controller)都是通过类路径扫描自动检测来自动注册Bean的。这些注解通常用于自动扫描和自动装配Bean到Spring容器中。

    java 复制代码
    @Component
    public class MyComponent {
        // 类体
    }
    
    @Service
    public class MyService {
        // 服务层逻辑
    }
    
    @Repository
    public class MyRepository {
        // 数据访问逻辑
    }
    
    @Controller
    public class MyController {
        // Web层逻辑
    }

    特点

  • 被注解的类将自动被Spring容器扫描和实例化。

  • 适用于那些不需要太多自定义配置的Bean,主要通过类型安全的方式进行依赖注入。

2. 使用@Bean注解
  • @Bean注解通常用于配置类中,方法级别的注解,用于显式声明一个Bean,以及其生命周期的配置。

  • 使用@Bean的方法返回一个对象的实例,Spring容器将注册返回的对象作为Bean

    java 复制代码
    @Configuration
    public class AppConfig {
        @Bean
        public MyBean myBean() {
            return new MyBean();
        }
    }
    
    public class MyBean {
        public void doSomething() {
            // 业务逻辑
        }
    }
  • 提供了更多的灵活性,可以在方法中编写复杂的实例化逻辑。

  • 适用于需要程序员精确控制Bean初始化方式和配置的场合,包括依赖的注入方式、作用域、生命周期回调等。

  • @Component注解适用于自动扫描和装配,当你有大量需要注册的类且不需要特别指定太多配置时非常有用。

  • @Bean注解提供了更精细的控制,特别适用于创建第三方库的对象,或者复杂的初始化逻辑,以及当你需要显式配置某些属性时。

二、Bean的使用和依赖注入(DI)

在Spring框架中,Bean的使用包括它们的创建、管理以及依赖注入(DI)。ApplicationContext是Spring的高级容器,负责实例化、配置和组装Bean。

1. 依赖注入(DI)

依赖注入是一种允许对象定义它们依赖关系的设计模式,而无需知道如何构造它们。在Spring中,依赖注入主要有以下几种方式:

构造器注入:通过类的构造器注入依赖,这是推荐的注入方式,因为它可以保证所需依赖的不变性和必需性。

java 复制代码
@Component
public class MyService {
    private final MyRepository repository;

    @Autowired
    public MyService(MyRepository repository) {
        this.repository = repository;
    }
}

Setter注入:通过公开的setter方法注入依赖,适用于可选依赖。

java 复制代码
@Component
public class MyService {
    private MyRepository repository;

    @Autowired
    public void setRepository(MyRepository repository) {
        this.repository = repository;
    }
}

字段注入:直接在字段上注入依赖,虽然方便但是不推荐使用,因为它可能会导致依赖不明确和难以进行单元测试。

java 复制代码
@Component
public class MyService {
    @Autowired
    private MyRepository repository;
}
2. ApplicationContext

ApplicationContext是Bean工厂的扩展,提供了更多企业级功能,是Bean生命周期管理和高级配置的中心。Spring应用中的所有Bean都是由ApplicationContext管理的,它提供了以下功能:

  • Bean实例化和配置 :容器使用Bean定义(如通过@Component@Bean注解提供的配置)来创建和配置所有的Bean。
  • 自动装配ApplicationContext自动检测并配置Bean之间的依赖关系。
  • 国际化:提供国际化的文本消息。
  • 事件传播:支持事件和监听器,允许Bean接收数据变更或容器事件的通知。
  • 不同层次的上下文:支持多个层次的上下文,允许继承配置。

以下是一个使用ApplicationContext获取Bean实例的简单例子:

java 复制代码
public class MyApp {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        MyService myService = ctx.getBean(MyService.class);
        myService.performService();
    }
}

三、Bean的作⽤域

作用域定义了Bean实例的创建、管理、和访问方式。下面是对每种作用域的详细介绍:

1. Singleton(单例作用域)
  • 描述:这是Spring默认的作用域。在这种作用域中,Spring容器中每个Spring Bean定义只会创建一个实例。该实例在整个容器中是共享的,所有对该Bean请求都返回同一个实例。

  • 用途 :适用于无状态的服务或单例模式需要的场合。

    java 复制代码
    @Component
    public class SingletonService {
        public void serviceMethod() {
            System.out.println("Singleton instance method called.");
        }
    }
2. Prototype(原型作用域)
  • 描述:每次请求Bean时,Spring容器都会创建一个新的Bean实例,而不是返回现有的Bean。

  • 用途 :适用于每个使用者都需要一个新实例的情况,例如,使用Bean来维护用户的状态。

    java 复制代码
    @Component
    @Scope("prototype")
    public class PrototypeBean {
        public void beanMethod() {
            System.out.println("Prototype instance " + this + " method called.");
        }
    }
3. Request(请求作用域)
  • 描述:该Bean作用域限定在单个HTTP请求内。每个HTTP请求都会创建一个新的Bean实例,该实例仅在当前HTTP请求内有效。(依赖Web MVC)

  • 用途 :适用于Web应用程序,每次HTTP请求需要独立处理的数据。

    java 复制代码
    @Component
    @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class RequestBean {
        public void printId() {
            System.out.println("Request bean instance " + System.identityHashCode(this));
        }
    }
4. Session(会话作用域)
  • 描述:该Bean作用域限定在HTTP会话内。每个HTTP会话创建一个Bean实例,该实例在整个会话期间共享。(依赖Web MVC)

  • 用途 :适用于需要维护用户会话数据的情况,如用户登录信息。

    java 复制代码
    @Component
    @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class SessionBean {
        public void printId() {
            System.out.println("Session bean instance " + System.identityHashCode(this));
        }
    }
5. Application(应用作用域)
  • 描述:Bean的作用域限定在ServletContext的生命周期内。这意味着Bean是全局共享的,与Singleton作用域类似,但是范围限定在整个Web应用上下文。(依赖Web MVC)

  • 用途 :适用于需要跨多个Servlet共享数据的场合,如全局配置数据。

    java 复制代码
    @Component
    @Scope(value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class ApplicationBean {
        public void printId() {
            System.out.println("Application scope bean instance " + System.identityHashCode(this));
        }
    }
6. Websocket(WebSocket作用域)
  • 描述:Bean的生命周期绑定到WebSocket会话的生命周期。每个WebSocket会话会创建一个Bean实例。(依赖Web MVC)

  • 用途 :适用于基于WebSocket的通信应用,每个WebSocket会话需要单独管理的数据。

    java 复制代码
    @Component
    @Scope("websocket")
    public class WebSocketBean {
        public void handleMessage(String message) {
            System.out.println("Handling message in WebSocket session bean: " + message);
        }
    }

    对于request, session, 和 websocket 作用域,你需要配置相应的Web环境和websocket配置,可能还需要使用proxyMode = ScopedProxyMode.TARGET_CLASS以确保Bean是正确代理的,特别是在单例Bean中注入这些作用域的Bean时

相关推荐
suweijie7683 小时前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
公贵买其鹿4 小时前
List深拷贝后,数据还是被串改
java
向前看-7 小时前
验证码机制
前端·后端
xlsw_7 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹7 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭8 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫8 小时前
泛型(2)
java
超爱吃士力架8 小时前
邀请逻辑
java·linux·后端
南宫生8 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石8 小时前
12/21java基础
java