@Scope简介
@Scope注解是为了指明Spring IOC容器中Bean的作用域的注解,可以配合@Component和@Bean注解一起使用。当与@Component一起使用时,表示该对象的作用域,与@Bean一起使用时,表示@Bean注解的方法返回对象的作用域。对于XML声明的Bean需要在XML文件中进行定义,无法使用该注解。
@Scope注解源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
@AliasFor("scopeName")
String value() default "";
@AliasFor("value")
String scopeName() default "";
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}@Scope注解使用示例
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON, proxyMode = ScopedProxyMode.DEFAULT)
@Configuration //@Configuration使用了@Component
public class MyConfig {
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON, proxyMode = ScopedProxyMode.DEFAULT)
@Bean
public Stock getStock() {
return new Stock();
}
}从上面的示例代码中我们可以注意到,我们向@Scope注解传入了两个参数,scopName和proxyMode。在Scope注解中一共有三个方法,其中value()和scopeName()互为别名,scopeName()在语义上更为贴近;proxyMode()指定了该Bean的代理模式,返回类型为ScopeProxyMode。下面我们详细介绍scopeName和proxyMode。
scopeName
scopeName是为了声明Bean的作用域,在Spring4.2版本以前,有singleton, prototype两种模式,4.2之后新加了web作用域(request, session, globalsession)。
- singleton:单例模式,顾名思义即Spring IOC容器对于一个Bean,只会有一个共享的Bean实例。这一个单一的实例会被存储到单例缓存(singleton cache)中,当有请求或者是引用时,IOC容器都会返回存储在singleton cache的同一个实例。
- prototype:多实例模式,即当每次客户端向容器获取Bean的时候,IOC容器都会新建一个实例并返回。与单例不同的是,在IOC容器启动的时候并不会创建Bean的实例,并且在有请求创建Bean实例之后也不会管理该实例的生命周期,而是由客户端自行处理。
- request:web应用针对每一次HTTP请求都会创建一个新的Bean实例,且该实例仅在该次HTTP请求有效。
- session:针对每一个session会创建一个Bean实例,且生命周期为该session有效期间。
- globalsession:仅基于portlet的web应用才有意义,否则可以当作session使用。
这五种scopeName的使用方法
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON)
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST)
@Scope(scopeName = WebApplicationContext.SCOPE_SESSION)
@Scope(scopeName = "globalSession")ScopeProxyMode
proxyMode表明了@Scope注解的Bean是否需要代理。
首先我们要先知道为什么需要Bean的代理对象。我们看下面的代码,ClassA的作用域是singleton,而ClassB的作用域是prototype,当Spring应用上下文加载时会去创建ClassA的实例对象,并将ClassB的实例注入到ClassA中,但是由于ClassB此时并没有实例,所以在初始化的时候调用ClassB的方法会抛出NullPointerException。因此我们需要创建一个ClassB的代理来解决这种情况。
ClassA示例代码:
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON)
@Configuration
public class ClassA {
@Autowired
private ClassB classB;
public ClassA() {
this.classB.test();
}
}ClassB没有代理示例代码:
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class ClassB {
public void test() {
System.out.println("This is test-B");
}
}在@Scope注解中我们通过proxyMode来表示是否需要Bean代理,以及使用哪种代理。proxyMode有四个值:
- DEFAULT:proxyMode的默认值,一般情况下等同于NO,即不需要动态代理。
- NO:不需要动态代理,即返回的是Bean的实例对象。
- INTERFACES:代理的对象是一个接口,即@Scope的作用对象是接口,这种情况是基于jdk实现的动态代理。
- TARGET_CLASS:代理的对象是一个类,即@Scope的作用对象是一个类,上面例子中的ClassB就可以用这种代理,是以生成目标类扩展的方式创建代理,基于CGLib实现动态代理。
@Scope(proxyMode = ScopedProxyMode.DEFAULT)
@Scope(proxyMode = ScopedProxyMode.NO)
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)