Spring @Scope批注

时间:2020-01-09 10:44:35  来源:igfitidea点击:

Spring容器不仅实例化bean并连接其依赖关系,还提供实例的范围。在Spring Framework中对六个bean作用域有内在的支持。例如,bean作用域可以是单例,原型等。这篇文章展示了如何使用Spring @Scope注释设置bean范围。

Spring bean支持的作用域

  • 单例作用域–为每个Spring IoC容器创建一个与bean定义相对应的单个对象实例。 Singleton范围是默认的bean范围。

  • 原型作用域–每次从Spring容器请求bean时都会创建一个新的对象实例。

  • 请求范围–为每个HTTP请求创建一个新的bean实例。此Spring bean范围仅在可感知网络的Spring ApplicationContext的上下文中有效。

  • 会话范围–单个bean实例的范围是HTTP会话的生命周期。该范围仅在可感知网络的Spring ApplicationContext的上下文中有效。

  • 应用程序范围–将单个bean定义范围限定为ServletContext的生命周期。该范围仅在可感知网络的Spring ApplicationContext的上下文中有效。

  • Websocket范围–将单个bean定义的范围扩展到WebSocket的生命周期。该范围仅在可感知网络的Spring ApplicationContext的上下文中有效。

在哪里使用@Scope注释

@Scope批注可以与@Component和@Configuration批注一起用作类型级别的批注。当用作类型级注释时,@ Scope表示要用于带注释类型的实例的范围的名称。

@Scope注释还可以与@Bean一起用作方法级注释。 @Scope用作方法级注释时,@ Scope表示要用于从方法返回的实例的范围的名称。

具有不同范围的@Scope批注

在本部分中,我们将看到将@Scope注释与不同的bean范围一起使用的代码段。可以使用ConfigurableBeanFactory和WebApplicationContext接口中可用的SCOPE_ *常量来引用Spring中开箱即用的范围。

@Scope与Singleton bean范围

@Service
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class BeanService{
 ...
 ...
}

@Scope与原型bean范围

@Service
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BeanService{
 ...
 ...
}

@Scope与请求bean范围

@Service
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class BeanService{
 ...
 ...
}

@Scope与会话bean范围

@Service
@Scope(WebApplicationContext.SCOPE_SESSION)
public class BeanService{
 ...
 ...
}

@Scope与应用程序bean范围

@Service
@Scope(WebApplicationContext.SCOPE_APPLICATION)
public class BeanService{
 ...
 ...
}

WebScope bean作用域的@Scope

@Component
@Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBean {
 ...
 ...
}

Spring @Scope批注Singleton示例

用单例作用域定义一个bean意味着该bean的单个实例是由容器创建的。对于该bean的所有请求都将返回相同的对象,并且对该字段的任何修改将反映在该bean的所有引用中,因为所有引用都指向同一对象。

如果未明确指定范围,则Singleton范围是默认的bean范围。

我们有一个User类,其中包含firstName,lastName和age等字段。用@Scope批注对类进行批注,范围指定为单例。

@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class User {
	private String firstName;
	private String lastName;
	private int age;
	public String getFirstName() {
		return firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public int getAge() {
		return age;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
}

应用程序配置类,用于指定组件扫描。

@Configuration
@ComponentScan("com.theitroad.springexample")
public class AppConfig {

}

现在,如果我们运行此示例并从容器中获取User Bean的两个对象,则两个对象引用将指向同一bean,因为作用域为单例。

public class App {
  public static void main(String[] args) {
    AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    User user1 = context.getBean("user", User.class);
    User user2 = context.getBean("user", User.class);
    user1.setFirstName("Ray");
    user1.setLastName("Chandler");
    System.out.println("First Name- " + user2.getFirstName());
    System.out.println("Last Name- " + user2.getLastName());
    context.close();
  }
}

输出:

First Name- Ray
Last Name- Chandler

如我们所见,firstName和lastName是使用user1引用设置的,从user2引用获取和打印这些字段的值也提供相同的值。

Spring @Scope注释原型示例

如果范围更改为原型,则在上面显示的User类中。

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class User {
	private String firstName;
	private String lastName;
	...
	...
}

现在运行示例并分析输出。

First Name- null
Last Name- null

由于原型范围,现在创建了单独的实例,因此user2实例的字段仍然为null。

使用@Configuration的Spring @Scope批注

在上面的示例中,@ Scope与@Component结合使用,让我们看一个示例,其中@Scope与@Configuration和@Bean批注一起使用。

这是两个类,其实例将从使用@Bean方法注释的方法中返回。

public class MyBean {
	public void myMethod() {
		System.out.println("In MyMethod of MyBean class");
	}
}
public class ClassB {
	public ClassB(){
		System.out.println("In constructor of ClassB");
	}
}

配置类

在配置类中,@Scope用作类型级注释和方法级注释。在类型级别(结合@Configuration),范围被指定为原型。
在使用一种方法的方法级别上,未指定范围,这意味着Bean将具有默认级别的Singleton。请注意,在类型级别指定的范围不适用于自动方法级别,方法将具有自己的范围。另一种方法具有范围原型。

@Configuration
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AppConfig {
	@Bean
	public MyBean myBean() {
		return new MyBean();
	}
	
	@Bean
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public ClassB classB() {
		return new ClassB();
	}
}

我们可以通过获取具有方法isSingleton和isPrototype的BeanDefinition实例来检查bean的范围。

public class App {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    System.out.println("AppConfig has prototype scope- " + context.getBeanDefinition("appConfig").isPrototype());
    System.out.println("MyBean has prototype scope- " + context.getBeanDefinition("myBean").isPrototype());
    System.out.println("MyBean has singleton scope- " + context.getBeanDefinition("myBean").isSingleton());
    System.out.println("ClassB has prototype scope- " + context.getBeanDefinition("classB").isPrototype());
    context.close();
  }
}

输出:

AppConfig has prototype scope- true
MyBean has prototype scope- false
MyBean has singleton scope- true
ClassB has prototype scope- true

从输出中,我们可以看到AppConfig bean的作用域是原型,这就是我们在类型级别上定义的范围。

MyBean实例的范围是默认情况下分配的Singleton。 ClassB实例的范围是在Config类的方法级别定义的原型。