我们常见的Bean有单例Bean与多例Bean,但其实scope还有有一些其它的值,如果只是使用简单的spring就只有这些,但是要是使用SprigBoot或者SpringMvc,那么就就还会有一些其它的比较常用的值,例如
session,request
如果是一个单例bean,spring就只需要创建一次,多例每次都创建,还有一些其它的scope也是会在指定时候创建
以下是简化后的代码
if (mbd.isSingleton()) {
// 如果是单例先从单例池拿,没有就执行createBean
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
});
// FactoryBean逻辑
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
// 每次都是执行createBean
prototypeInstance = createBean(beanName, mbd, args);
beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
// 获取Scope
String scopeName = mbd.getScope();
Scope scope = this.scopes.get(scopeName);
// 先从scope中拿,没有再createBean
Object scopedInstance = scope.get(beanName, () -> {
return createBean(beanName, mbd, args);
});
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
可以看到,除了单例和原型,还会有其它的值,如果是其它的值,应该是会注册一个Scope,通过Scope的get方法获取Bean
上面代码的第三个分支是需要有Scope,那么scopes什么时候有值呢,在spring-web里就有注册一些Scope
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,
@Nullable ServletContext sc) {
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
}
上面代码可以看到注册了RequestScope,与SessionScope,可以想到SessionScope就是在一个session中都可以拿到Bean,RequestScope便是在一次请求中都可以拿到
两个scope获取方式差不多,源码如下
public Object get(String name, ObjectFactory<?> objectFactory) {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
// ServletRequestAttributes.getAttribute
Object scopedObject = attributes.getAttribute(name, getScope());
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
// 拿到了塞进去
attributes.setAttribute(name, scopedObject, getScope());
Object retrievedObject = attributes.getAttribute(name, getScope());
if (retrievedObject != null) {
scopedObject = retrievedObject;
}
}
// 可以拿到直接返回
return scopedObject;
}
上面的attributes.getAttribute查看实现类 ServletRequestAttributes.getAttribute
原理就是使用的request与session暂存,如下所示
public Object getAttribute(String name, int scope) {
if (scope == SCOPE_REQUEST) {
return this.request.getAttribute(name);
}
else {
HttpSession session = getSession(false);
if (session != null) {
try {
Object value = session.getAttribute(name);
if (value != null) {
this.sessionAttributesToUpdate.put(name, value);
}
return value;
}
catch (IllegalStateException ex) {
// Session invalidated - shouldn't usually happen.
}
}
return null;
}
}
public void setAttribute(String name, Object value, int scope) {
if (scope == SCOPE_REQUEST) {
this.request.setAttribute(name, value);
}
else {
HttpSession session = obtainSession();
this.sessionAttributesToUpdate.remove(name);
session.setAttribute(name, value);
}
}
由上面代码分析,自定义scope只需要实现org.springframework.beans.factory.config.Scope就行
spring提供了一个SimpleThreadScope,我们就使用它,不重新写了
SimpleThreadScope代码如下
public class SimpleThreadScope implements Scope {
private static final Log logger = LogFactory.getLog(SimpleThreadScope.class);
private final ThreadLocal<Map<String, Object>> threadScope =
new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
@Override
protected Map<String, Object> initialValue() {
return new HashMap<>();
}
};
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = this.threadScope.get();
Object scopedObject = scope.get(name);
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
scope.put(name, scopedObject);
}
return scopedObject;
}
@Override
@Nullable
public Object remove(String name) {
Map<String, Object> scope = this.threadScope.get();
return scope.remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
logger.warn("SimpleThreadScope does not support destruction callbacks. " +
"Consider using RequestScope in a web environment.");
}
@Override
@Nullable
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return Thread.currentThread().getName();
}
}
该代码其实就是保证在同一个线程上下文中拿到的对象都是同一个
使用SimpleThreadScope
@Component
// 需要注册
@Scope("thread")
public class UserBean {
public UserBean() {
System.out.println("实例化UserBean");
}
}
public class Application {
public static void main(String[] args) throws InterruptedException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
// 注册Scope
context.getBeanFactory().registerScope("thread", new SimpleThreadScope());
context.refresh();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(context.getBean("userBean"));
}
}).start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(context.getBean("userBean"));
}
}).start();
TimeUnit.SECONDS.sleep(1);
}
}
输出
实例化UserBean
实例化UserBean
com.shura.beans.UserBean@30f45b6
com.shura.beans.UserBean@1712a6b0
com.shura.beans.UserBean@1712a6b0
com.shura.beans.UserBean@1712a6b0
com.shura.beans.UserBean@1712a6b0
com.shura.beans.UserBean@1712a6b0
com.shura.beans.UserBean@30f45b6
com.shura.beans.UserBean@30f45b6
com.shura.beans.UserBean@30f45b6
com.shura.beans.UserBean@30f45b6
以上就是scope的分析了
欢迎关注,学习不迷路!