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时

相关推荐
Neoest31 分钟前
【EasyExcel 填坑日记】“Syntax error on token )“: 一次编译错误在逃 Runtime 的灵异事件
java·eclipse·编辑器
自在极意功。33 分钟前
Web开发中的分层解耦
java·microsoft·web开发·解耦
是一个Bug35 分钟前
ConcurrentHashMap的安全机制详解
java·jvm·安全
断剑zou天涯38 分钟前
【算法笔记】bfprt算法
java·笔记·算法
番石榴AI40 分钟前
java版的ocr推荐引擎——JiaJiaOCR 2.0重磅升级!纯Java CPU推理,新增手写OCR与表格识别
java·python·ocr
码事漫谈1 小时前
VSCode CMake Tools 功能解析、流程与最佳实践介绍
后端
鸽鸽程序猿1 小时前
【项目】【抽奖系统】抽奖
java·spring
火云牌神2 小时前
本地大模型编程实战(38)实现一个通用的大模型客户端
人工智能·后端
码事漫谈2 小时前
从C++/MFC到CEF与TypeScript的桌面架构演进
后端
weixin_462446232 小时前
SpringBoot切换Redis的DB
数据库·spring boot·redis