Spring Bean范围

Spring Bean作用域允许我们更加精细地控制bean实例的创建。有时我们希望将bean实例创建为单例,但在其他情况下,我们可能希望在每个请求中创建它,或者在会话中创建一次。

Spring Bean 作用域

有五种类型的SpringBean作用域:

    单例 – 在spring容器中只会创建一个spring bean实例。这是默认的spring bean范围。在使用这个范围时,确保bean没有共享实例变量,否则可能导致数据一致性问题。
    原型 – 每次从spring容器中请求bean时都会创建一个新的实例。
    请求 – 这与原型范围相同,但它适用于Web应用程序。每个HTTP请求都会为bean创建一个新实例。
    会话 – 容器会为每个HTTP会话创建一个新的bean。
    全局会话 – 用于为门户应用程序创建全局会话bean。

Spring Bean的单例(Singleton)和原型(Prototype)作用域

在独立的Spring应用程序中,可以使用单例和原型作用域的Spring Bean。让我们看看如何使用@Scope注解轻松配置这些作用域。假设我们有一个Java Bean类。

package com.Olivia.spring;

public class MyBean {

	public MyBean() {
		System.out.println("MyBean instance created");
	}

}

让我们定义Spring配置类,在这里我们将定义从Spring容器获取 MyBean 实例的方法。

package com.Olivia.spring;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class MyConfiguration {
	
	@Bean
	@Scope(value="singleton")
    public MyBean myBean() {
		return new MyBean();
	}
	
}

请注意,单例是默认作用域,所以我们可以从上面的Bean定义中删除@Scope(value = “singleton”)。现在让我们创建一个主方法并测试单例作用域。

package com.Olivia.spring;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MySpringApp {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
		ctx.register(MyConfiguration.class);
		ctx.refresh();

		 MyBean mb1 = ctx.getBean(MyBean.class);
		 System.out.println(mb1.hashCode());

		 MyBean mb2 = ctx.getBean(MyBean.class);
		 System.out.println(mb2.hashCode());

		ctx.close();
	}

}

当执行上述程序时,我们将得到如下输出。

MyBean instance created
867988177
867988177

请注意,两个MyBean实例具有相同的哈希码,并且构造函数只被调用一次,这意味着Spring容器始终返回同一个MyBean实例。现在让我们将作用域更改为原型。

@Bean
@Scope(value="prototype")
public MyBean myBean() {
	return new MyBean();
}

当执行主方法时,这次我们将会得到以下的输出结果。

MyBean instance created
867988177
MyBean instance created
443934570

很明显,每次从Spring容器请求时都会创建一个新的MyBean实例。现在让我们将作用域改为请求等级。

@Bean
@Scope(value="request")
public MyBean myBean() {
	return new MyBean();
}

在这种情况下,我们将得到以下异常。

Exception in thread "main" java.lang.IllegalStateException: No Scope registered for scope name 'request'
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:347)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:224)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1015)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:339)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:334)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1107)
	at com.Olivia.spring.MySpringApp.main(MySpringApp.java:12)

由于独立应用程序不支持请求、会话和全局会话范围,所以才会出现这个问题。

Spring Bean的请求和会话范围

spring bean scopes spring boot web app project
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.Olivia.spring</groupId>
	<artifactId>Spring-Boot-MVC</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<name>Spring-Boot-MVC</name>
	<description>Spring Beans Scope MVC</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>10</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

我们来创建一些春天的组件,并将它们配置为在春天容器中作为请求和会话范围的春天bean。

Spring Bean的请求范围

package com.Olivia.spring;

import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class DataRequestScope {

	private String name = "Request Scope";
	
	public DataRequestScope() {
		System.out.println("DataRequestScope Constructor Called");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

春天的豆子会话范围

package com.Olivia.spring;

import org.springframework.context.annotation.Scope;
import java.time.LocalDateTime;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class DataSessionScope {

	private String name = "Session Scope";
	
	public DataSessionScope() {
		System.out.println("DataSessionScope Constructor Called at "+LocalDateTime.now());
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Spring组件

现在让我们创建一个 Spring 组件,并使用 Spring 自动配置上述的 bean。

package com.Olivia.spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Customer {

	@Autowired
	private DataRequestScope dataRequestScope;
	
	@Autowired
	private DataSessionScope dataSessionScope;

	public DataRequestScope getDataRequestScope() {
		return dataRequestScope;
	}

	public void setDataRequestScope(DataRequestScope dataRequestScope) {
		this.dataRequestScope = dataRequestScope;
	}

	public DataSessionScope getDataSessionScope() {
		return dataSessionScope;
	}

	public void setDataSessionScope(DataSessionScope dataSessionScope) {
		this.dataSessionScope = dataSessionScope;
	}


}

Spring REST 控制器

最后,让我们创建一个RestController类并为我们的测试目的配置一些API终端。

package com.Olivia.spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloData {

	@Autowired
	private Customer customer;
	
	@RequestMapping("/nameRS")
	public String helloRS() {
		return customer.getDataRequestScope().getName();
	}
	
	@RequestMapping("/nameSSUpdated")
	public String helloSSUpdated() {
		customer.getDataSessionScope().setName("Session Scope Updated");
		return customer.getDataSessionScope().getName();
	}
	
	@RequestMapping("/nameSS")
	public String helloSS() {
		return customer.getDataSessionScope().getName();
	}
}

Spring Boot会话超时配置

最后,我们需要配置 Spring Boot 会话超时变量,在 src/main/resources/application.properties 文件中添加以下属性。

server.session.cookie.max-age= 1
server.session.timeout= 1

现在我们的会话范围的Spring bean将在一分钟内失效。只需将SpringBootMvcApplication类作为spring boot应用程序运行。您应该看到我们的端点配置的以下输出。

2018-05-23 17:02:25.830  INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameRS]}" onto public java.lang.String com.Olivia.spring.HelloData.helloRS()
2018-05-23 17:02:25.831  INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameSSUpdated]}" onto public java.lang.String com.Olivia.spring.HelloData.helloSSUpdated()
2018-05-23 17:02:25.832  INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameSS]}" onto public java.lang.String com.Olivia.spring.HelloData.helloSS()

SpringBean请求范围测试

在任何浏览器中打开,并输入网址https://localhost:8080/nameRS,然后查看控制台输出。您应该能看到在每个请求上都会打印出“DataRequestScope构造器已调用”。

Spring豆子会话范围测试

spring bean session scope
发表回复 0

Your email address will not be published. Required fields are marked *


广告
将在 10 秒后关闭
bannerAds