暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Spring Bean作用域

帽爹的技术轮子 2020-09-15
473
1
Spring Bean作用域

官方文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-scopes-custom

Scope
Description
signleton
默认Spring Bean作用域,一个BeanFactory有且仅有一个实例;换而言之,java里面一些静态字段由一个ClassLoader加载的,因为Class由ClassLoader加载的
prototype

原型作用域,每次依赖查找和依赖注入生成新Bean对象;脱离Spring容器管理,prototype没有完整的生命周期,如destroy不会执行,可以实现DisposableBean接口来调对象的Bean处理

request
ServletRequest上下文中
session
HttpSession会话中
application
ServletContext上下文中
websocket
websocket连接会话中
custom
自定义scope



2
Scope注解proxyMode代理模式


Scope注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {


...
/**
* 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
*/
  // INTERFACES jdk动态代理
// TARGET_CLASS CGLIB动态代理
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;


}

Scope注解代理实现
  • ConfigurationClassPostProcessor

  • AnnotatedBeanDefinitionReader#register(Class<?>)



3
自定义Scope

案例:实现一个Thread直接共享的Bean实例的Scope

自定义Scope注解


/**
* 自定义ThreadLocal Scope注解
*
* @author zli
* @create 2020-06-17 11:11
**/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(com.zli.spring.ioc.bean.scope.ThreadLocalScope.SCOPE_NAME)
public @interface ThreadLocalScope {
}


实现Scope接口
对Bean实例作相应处理,如ThreadLocalScope,则利用ThreadLocal缓存Bean实例,利用当前线程获取对应的Bean实例;这里Bean实例是通过ObjectFactory#getObject()赖加载来获取
/**
* 自定义ThreadLocal Scope实现
*
* @author zli
* @create 2020-06-17 10:34
**/
public class ThreadLocalScope implements Scope, BeanFactoryPostProcessor {


public static final String SCOPE_NAME = "thread-local";


private final NamedThreadLocal<Map<String, Object>> threadLocal = new NamedThreadLocal("thread-local-scope") {
@Override
protected Map<String, Object> initialValue() {
return new HashMap<>();
}
};


@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> contextMap = getContext();
Object object = contextMap.get(name);
if (object == null) {
object = objectFactory.getObject();
contextMap.put(name, object);
}
return object;
}




private Map<String, Object> getContext() {
return threadLocal.get();
}




@Override
public Object remove(String name) {
Map<String, Object> contextMap = getContext();
return contextMap.remove(name);
}


@Override
public void registerDestructionCallback(String name, Runnable callback) {
// TODO
}


@Override
public Object resolveContextualObject(String key) {
Map<String, Object> contextMap = getContext();
return contextMap.get(key);
}


@Override
public String getConversationId() {
Thread thread = Thread.currentThread();
return String.valueOf(thread.getId());
}


@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerScope(SCOPE_NAME, this);
}
}
配置自定义Scope实现

配置ThreadLocalScope,有以下三种方式:

  • 通过xml配置

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread-local">
<bean class="com.zli.spring.ioc.bean.scope.ThreadLocalScope"/>
</entry>
</map>
</property>
</bean>
  • 通过@Bean注解配置

 @Bean
public CustomScopeConfigurer customScopeConfigurer() {
CustomScopeConfigurer customScopeConfigurer = new CustomScopeConfigurer();
customScopeConfigurer.addScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());
return customScopeConfigurer;
}
  • 通过实现BeanFactoryPostProcessor接口注册自定义Scope

/**
* 自定义ThreadLocal Scope实现
*
* @author zli
* @create 2020-06-17 10:34
**/
public class ThreadLocalScope implements Scope, BeanFactoryPostProcessor {


public static final String SCOPE_NAME = "thread-local";


...


@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 通过BeanFactoryPostProcessor#postProcessBeanFactory注册自定义scope
beanFactory.registerScope(SCOPE_NAME, this);
}
}




/**
*
*
* @author zli
* @create 2020-06-17 10:34
**/
public class CustomTheadLocalScopeDemo {
...
public static void main(String[] args) {
...

// 通过API方式注册自定义Scope
applicationContext.addBeanFactoryPostProcessor(new ThreadLocalScope());

...
}
...
}
  • 通过API(硬编码)注册自定义Scope实现

// 实际就是CustomScopeConfigurer的简化版,通过lambda表达式
applicationContext.addBeanFactoryPostProcessor(beanFactory -> {
beanFactory.registerScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());
});

测试自定义Scope
/**
*
*
* @author zli
* @create 2020-06-17 10:34
**/
public class CustomTheadLocalScopeDemo {


@Autowired
private User user;


@Bean
//@Scope(value = ThreadLocalScope.SCOPE_NAME)
@com.zli.spring.ioc.bean.scope.annotaion.ThreadLocalScope
public User user() {
User user = new User();
user.setId(System.nanoTime());
return user;
}


public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册Configuration Class
applicationContext.register(CustomTheadLocalScopeDemo.class);


// 通过API方式注册自定义Scope
/*applicationContext.addBeanFactoryPostProcessor(beanFactory -> {
beanFactory.registerScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());
});*/


// 刷新上下文
applicationContext.refresh();


// 单线程获取实例
singletonThread(applicationContext);


// 多线程获取实例
multiThread(applicationContext);


// 关闭上下文
applicationContext.close();
}




private static void singletonThread(ApplicationContext applicationContext) {
for (int i = 0; i < 3; i++) {
System.out.println("singleton thread bean:" + applicationContext.getBean("user", User.class));
}
}


private static void multiThread(AnnotationConfigApplicationContext applicationContext) {
for (int i = 0; i < 3; i++) {
new Thread(() -> System.out.println(Thread.currentThread().getId() + " multi thread bean:" + applicationContext.getBean("user", User.class))).start();
}
}




@Bean
public CustomScopeConfigurer customScopeConfigurer() {
CustomScopeConfigurer customScopeConfigurer = new CustomScopeConfigurer();
customScopeConfigurer.addScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());
return customScopeConfigurer;
}
}


4
RefreshScope(Spring Cloud)实现分析

Spring Cloud RefreshCode是如何控制Bean的动态刷新 ? 


Spring Cloud RefreshCode通过自动装配实例化 


修改Properties资源内容,通过jconsole(RefreshScope被JMX管理)调用refresh、refreshAll方法来动态刷新Bean

RefreshScope注解
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {


/**
* @see Scope#proxyMode()
* @return proxy mode
*/
// 默认使用CGLIB动态代理,即被@RefreshScope标记的类是使用CGLIB动态代理
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
Scope接口的实现RefreshScope
/** 
* @author Dave Syer
* @since 3.1
*/
@ManagedResource
public class RefreshScope extends GenericScope implements ApplicationContextAware,
ApplicationListener<ContextRefreshedEvent>, Ordered {
...


@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
this.registry = registry;
// 调用GenericScope父类方法完成BeanDefinition初始化
super.postProcessBeanDefinitionRegistry(registry);
}


// 监听刷新scope事件
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 这里主要获取BeanDefinition,判断对应scope来依赖查找
start(event);
}

...

// JMX管理方法,可以通过jconsole来调用refresh,需要指定对应bean的名称
@ManagedOperation(description = "Dispose of the current instance of bean name "
+ "provided and force a refresh on next method execution.")
public boolean refresh(String name) {
// 如果没有拼接aop代理前缀,则拼接(scopedTarget.)
if (!name.startsWith(SCOPED_TARGET_PREFIX)) {
// User wants to refresh the bean with this name but that isn't the one in the
// cache...
name = SCOPED_TARGET_PREFIX + name;
}
// 调用父类destroy方法销毁bean实例
// Ensure lifecycle is finished if bean was disposable
if (super.destroy(name)) {
// 发布刷新scope事件
this.context.publishEvent(new RefreshScopeRefreshedEvent(name));
return true;
}
return false;
}


// JMX管理方法,可以通过jconsole来调用refreshAll方法,刷新所有被@RefreshScope注解的Bean
@ManagedOperation(description = "Dispose of the current instance of all beans "
+ "in this scope and force a refresh on next method execution.")
public void refreshAll() {
super.destroy();
this.context.publishEvent(new RefreshScopeRefreshedEvent());
}


...


}
RefreshScope的父类GenericScope实现
public class ScopedProxyFactoryBean extends ProxyConfig implements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {
private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();
@Nullable
private String targetBeanName;
@Nullable
private Object proxy;


public ScopedProxyFactoryBean() {
this.setProxyTargetClass(true);
}


public void setTargetBeanName(String targetBeanName) {
this.targetBeanName = targetBeanName;
this.scopedTargetSource.setTargetBeanName(targetBeanName);
}


public void setBeanFactory(BeanFactory beanFactory) {
if (!(beanFactory instanceof ConfigurableBeanFactory)) {
throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
} else {
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory)beanFactory;
this.scopedTargetSource.setBeanFactory(beanFactory);
ProxyFactory pf = new ProxyFactory();
pf.copyFrom(this);
pf.setTargetSource(this.scopedTargetSource);
Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
Class<?> beanType = beanFactory.getType(this.targetBeanName);
if (beanType == null) {
throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName + "': Target type could not be determined at the time of proxy creation.");
} else {
if (!this.isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
}


ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
pf.addInterface(AopInfrastructureBean.class);
this.proxy = pf.getProxy(cbf.getBeanClassLoader());
}
}
}


public Object getObject() {
if (this.proxy == null) {
throw new FactoryBeanNotInitializedException();
} else {
return this.proxy;
}
}


public Class<?> getObjectType() {
return this.proxy != null ? this.proxy.getClass() : this.scopedTargetSource.getTargetClass();
}


public boolean isSingleton() {
return true;
}
}


文章转载自帽爹的技术轮子,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论