Spring @Scope批注
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类的方法级别定义的原型。