在 Spring 框架里,Bean 作用域明确了 Spring 容器如何创建和管理 Bean 实例。不同的作用域适用于不同的应用场景,下面将详细介绍 Spring 中常见的 Bean 作用域。
1. singleton(单例)
解释
这是 Spring 默认的 Bean 作用域。当一个 Bean 被定义为单例作用域时,Spring 容器在整个应用的生命周期里只会创建该 Bean 的一个实例,并且所有对该 Bean 的请求都会返回这个唯一的实例。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AppConfig {@Beanpublic MyBean myBean() {return new MyBean();}
}class MyBean {public MyBean() {System.out.println("MyBean 实例被创建");}
}import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);MyBean bean1 = context.getBean(MyBean.class);MyBean bean2 = context.getBean(MyBean.class);System.out.println(bean1 == bean2); // 输出 truecontext.close();}
}
适用场景
适用于那些无状态或者状态可以共享的 Bean,像服务层组件、数据访问对象(DAO)等。由于单例作用域只创建一个实例,所以能减少内存开销。
可能存在的问题及解决方案
- 线程安全问题:若单例 Bean 是有状态的,多个线程同时访问该 Bean 时可能会出现线程安全问题。解决方案是保证 Bean 无状态,或者使用线程安全的类和同步机制。
2. prototype(原型)
解释
当一个 Bean 被定义为原型作用域时,每次从 Spring 容器请求该 Bean 时,容器都会创建一个新的实例。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;@Configuration
public class AppConfig {@Bean@Scope("prototype")public MyBean myBean() {return new MyBean();}
}class MyBean {public MyBean() {System.out.println("MyBean 实例被创建");}
}import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);MyBean bean1 = context.getBean(MyBean.class);MyBean bean2 = context.getBean(MyBean.class);System.out.println(bean1 == bean2); // 输出 falsecontext.close();}
}
适用场景
适用于有状态且状态不能共享的 Bean,比如需要为每个请求创建独立实例的场景。
可能存在的问题及解决方案
- 内存开销问题:由于每次请求都会创建新实例,若频繁请求可能会导致内存开销增大。解决方案是合理控制原型 Bean 的使用,避免不必要的创建。
3. request(请求)
解释
此作用域仅适用于 Web 应用的 Spring 上下文。在一个 HTTP 请求的生命周期内,容器会为每个请求创建一个 Bean 实例,不同的请求会有不同的实例。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;@Configuration
public class AppConfig {@Bean@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)public MyBean myBean() {return new MyBean();}
}class MyBean {public MyBean() {System.out.println("MyBean 实例被创建");}
}
适用场景
适用于需要在一个请求内保持状态的 Bean,例如处理用户请求时存储请求相关信息的 Bean。
可能存在的问题及解决方案
- 代理模式问题:在使用
request
作用域时,通常需要使用代理模式(如ScopedProxyMode.TARGET_CLASS
),否则可能会出现注入失败的问题。
4. session(会话)
解释
该作用域也仅适用于 Web 应用的 Spring 上下文。在一个用户会话的生命周期内,容器会为每个会话创建一个 Bean 实例,不同的会话会有不同的实例。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;@Configuration
public class AppConfig {@Bean@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)public MyBean myBean() {return new MyBean();}
}class MyBean {public MyBean() {System.out.println("MyBean 实例被创建");}
}
适用场景
适用于需要在用户会话期间保持状态的 Bean,例如存储用户登录信息的 Bean。
可能存在的问题及解决方案
- 会话管理问题:如果会话过期或者被销毁,对应的 Bean 实例也会失效。需要确保在会话管理方面进行合理配置。
5. application(应用)
解释
同样适用于 Web 应用的 Spring 上下文。在整个 Web 应用的生命周期内,容器只会创建一个 Bean 实例,类似于单例作用域,但它是基于 ServletContext 的。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;@Configuration
public class AppConfig {@Bean@Scope(value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)public MyBean myBean() {return new MyBean();}
}class MyBean {public MyBean() {System.out.println("MyBean 实例被创建");}
}
适用场景
适用于需要在整个 Web 应用中共享状态的 Bean,比如全局计数器等。
可能存在的问题及解决方案
- 资源竞争问题:多个线程同时访问应用作用域的 Bean 时可能会出现资源竞争问题。可以使用同步机制来解决。
6. websocket(WebSocket)
解释
这是 Spring 4.2 引入的作用域,适用于 WebSocket 应用。在一个 WebSocket 会话的生命周期内,容器会为每个 WebSocket 会话创建一个 Bean 实例。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.socket.config.annotation.EnableWebSocket;@Configuration
@EnableWebSocket
public class AppConfig {@Bean@Scope(value = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)public MyBean myBean() {return new MyBean();}
}class MyBean {public MyBean() {System.out.println("MyBean 实例被创建");}
}
适用场景
适用于处理 WebSocket 会话相关的 Bean,比如存储 WebSocket 会话状态的 Bean。
可能存在的问题及解决方案
- 会话管理复杂:WebSocket 会话的管理相对复杂,需要确保在会话关闭时正确处理 Bean 实例。可以在 WebSocket 处理器中添加相应的关闭逻辑。