Spring注解 @Scope详解
项目中用到@Scope该注解,然后出线一些情况,所以总结下
下面是@Scope注解的源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
/**
* Alias for {@link #scopeName}.
* @see #scopeName
*/
@AliasFor("scopeName")
String value() default "";
/**
* Specifies the name of the scope to use for the annotated component/bean.
* <p>Defaults to an empty string ({@code ""}) which implies
* {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
* @since 4.2
* @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
* @see ConfigurableBeanFactory#SCOPE_SINGLETON
* @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
* @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
* @see #value
*/
@AliasFor("value")
String scopeName() default "";
/**
* Specifies whether a component should be configured as a scoped proxy
* and if so, whether the proxy should be interface-based or subclass-based.
* <p>Defaults to {@link ScopedProxyMode#DEFAULT}, which typically indicates
* that no scoped proxy should be created unless a different default
* has been configured at the component-scan instruction level.
* <p>Analogous to {@code <aop:scoped-proxy/>} support in Spring XML.
* @see ScopedProxyMode
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
该注解的value属性有5个值:
@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)多例,在每次注入的时候回自动创建一个新的bean实例
@Scope(value=ConfigurableBeanFactory.SCOPE_SINGLETON)(默认)单例模式,在整个应用中只能创建一个实例
@Scope(value=WebApplicationContext.SCOPE_GLOBAL_SESSION)全局session
@Scope(value=WebApplicationContext.SCOPE_APPLICATION)在一个web应用中只创建一个实例
@Scope(value=WebApplicationContext.SCOPE_REQUEST)在一个请求中创建一个实例
@Scope(value=WebApplicationContext.SCOPE_SESSION)每次创建一个会话中创建一个实例
proxyMode属性3个值:
proxyMode=ScopedProxyMode.INTERFACES创建一个JDK代理模式
proxyMode=ScopedProxyMode.TARGET_CLASS基于类的代理模式
proxyMode=ScopedProxyMode.NO(默认)不进行代理
结构目录
在单例模式下:
//control:
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private AService aService;
@RequestMapping("/say")
public void say(){
aService.sayHello();
}
}
//service:
public interface AService {
void sayHello();
}
@Service
//@Scope(value= ConfigurableBeanFactory.SCOPE_REQUEST,proxyMode = ScopedProxyMode.INTERFACES) //之后用到
public class AServiceImpl implements AService {
@Autowired
private TestC testC;
@Override
public void sayHello() {
System.out.println(Thread.currentThread()+"service对象"+this);
testC.say();
}
}
//实体
@Component
@Getter
@Setter
public class TestC {
private static int x = 0;
private int i = 0;
public void say(){
System.out.println("i:"+i+++" | x:"+x++);
}
}
//测试类:
@SpringBootTest(webEnvironment =SpringBootTest.WebEnvironment.MOCK,classes = DemoApplication.class)
@AutoConfigureMockMvc
public class DemoApplicationTests {
private ExecutorService service = Executors.newFixedThreadPool(5);
@Autowired
private MockMvc mockMvc;
@Test
public void test() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
service.execute(new RunableTask(countDownLatch,mockMvc));
}
countDownLatch.await();
}
}
class RunableTask implements Runnable {
private CountDownLatch countDownLatch;
private MockMvc mockMvc;
public RunableTask(CountDownLatch countDownLatch,MockMvc mockMvc) {
this.countDownLatch = countDownLatch;
this.mockMvc = mockMvc;
}
@Override
public void run() {
try {
MvcResult mvcResult = mockMvc.perform(get("/test/say").contentType(MediaType.APPLICATION_JSON)).andReturn();
mvcResult.getResponse().getContentAsString();
} catch (Exception e) {
e.printStackTrace();
}finally {
countDownLatch.countDown();
}
}
运行test方法:
这里使用多线程是为了模拟并发访问controller
假设: 现在的业务需要我们每次请求的是后testC都是新的,这里我们就需要用到多例了,在TestC类上注解@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)
运行test方法发现结果跟单例的时候一样!!i的值还是累加在一起
这是因为你的controller、service都是单例,testC注入到service后,尽管你将testC注解为多例,但是service中的testC还是原来的对象
解决方法是将TestC类上的注解改为@Scope(value= ConfigurableBeanFactory.SCOPE_PROTOTYPE,proxyMode = ScopedProxyMode.TARGET_CLASS)
运行test方法 结果是我们想要的!!
我们试下作用域为request情况下:
在AServiceImpl类上注解@Scope(value= WebApplicationContext.SCOPE_REQUEST)
运行发现报错
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘testController’: Unsatisfied dependency expressed through field ‘aService’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘AServiceImpl’: Scope ‘request’ is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
我理解的意思是:因为Controller需要注入一个service,但是该是request作用域,因此可以为每个请求创建一个新实例。但是,在启动时,没有这样的请求,因此AService无法注入Bean,因为它不存在。所以报错了!!其中singleton和prototype用于一半情况,而后面3个用于web项目。
所以我们将AServiceImpl类上注解改为@Scope(value= ConfigurableBeanFactory.SCOPE_REQUEST,proxyMode = ScopedProxyMode.INTERFACES)
运行代码 结果是我们要的!!
说明每次请求的时候都创建一个AServiceImpl的bean!
之后的session、global_session大同小异,大家自己取试下吧,我就不介绍拉!
下一篇: 隔夜的什么不能吃?这些食物一定要警惕
推荐阅读
-
详解如何使用Jersey客户端请求Spring Boot(RESTFul)服务
-
spring springMVC中常用注解解析
-
Spring的注解简单介绍
-
Spring boot中@Conditional和spring boot的自动配置实例详解
-
基于注解的组件扫描详解
-
Spring AOP + 注解实现统一注解功能
-
详解spring cloud中使用Ribbon实现客户端的软负载均衡
-
Spring中property-placeholder的使用与解析详解
-
Spring Boot整合ElasticSearch实现多版本兼容的方法详解
-
Spring MVC集成springfox-swagger2构建restful API的方法详解