@Scope 是用来定义Spring Bean的作用域范围。分为singleton、prototype、request、session、application等。其中singleton和prototype为bean对象单例和每次都创建,其余的为scope的扩展。spring中自带request、session、application。主要是通过map对象存储。在spring创建bean中的代码如下:
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
这个scopes在spring初始化的时候创建,在refresh()方法的postProcessBeanFactory(beanFactory);中GenericWebApplicationContext.postProcessBeanFactory方法
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
if (this.servletContext != null) {
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
}
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext);
}
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, @Nullable ServletContext sc) {
beanFactory.registerScope("request", new RequestScope());
beanFactory.registerScope("session", new SessionScope());
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
beanFactory.registerScope("application", appScope);
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
beanFactory.registerResolvableDependency(ServletRequest.class, new WebApplicationContextUtils.RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new WebApplicationContextUtils.ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new WebApplicationContextUtils.SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebApplicationContextUtils.WebRequestObjectFactory());
if (jsfPresent) {
WebApplicationContextUtils.FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
}
}
public void registerScope(String scopeName, Scope scope) {
Assert.notNull(scopeName, "Scope identifier must not be null");
Assert.notNull(scope, "Scope must not be null");
if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
}
Scope previous = this.scopes.put(scopeName, scope);
if (previous != null && previous != scope) {
if (logger.isDebugEnabled()) {
logger.debug("Replacing scope '" + scopeName + "' from [" + previous + "] to [" + scope + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Registering scope '" + scopeName + "' with implementation [" + scope + "]");
}
}
}
@Scope注解在使用时,需要定义value,内容如果不是自己扩展的话,只能定义为singleton、prototype、request、session、application。
同时还有一个proxyMode的参数,默认为DEFAULT,还有INTERFACES、TARGET_CLASS两种。interfaces主要使用jdk的动态代理,target_class使用cglib代理。
proxyMode参数主要是用在以下这种情况
@Component
public class BeanA {
@Autowired
private BeanB beanB;
public void printB(){
System.out.println(beanB);
}
}
@Component
@Scope("prototype")
public class BeanB {
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext an = new AnnotationConfigApplicationContext();
an.scan("lookup.test");
an.refresh();
BeanA beanA = (BeanA)an.getBean("beanA");
beanA.printB();
beanA.printB();
}
}
结果:
可以看到,即使BeanB写了每次创建,在注入到BeanA的时候,也是默认单例。那如何实现需求,可以将BeanB的注解改一下
@Component
@Scope(value = "prototype",proxyMode = ScopedProxyMode.TARGET_CLASS)
public class BeanB {
}
再执行一次,看结果:
可以看到已经实现了不同的BeanB对象注入到BeanA中。
在springboot或者spring加载扫描bean对象时,会通过ClassPathBeanDefinitionScanner的doScan方法。
protected Set doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
该方法在扫描标有@Component对象后,会执行this.scopeMetadataResolver.resolveScopeMetadata(candidate)方法
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), this.scopeAnnotationType);
if (attributes != null) {
metadata.setScopeName(attributes.getString("value"));
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode;
}
metadata.setScopedProxyMode(proxyMode);
}
}
return metadata;
}
该方法会获取@Scope注解中的proxyMode值,并set到metadata中。默认为No。之后会执行doScan方法中的 AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry)方法。
static BeanDefinitionHolder applyScopedProxyMode(
ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
return definition;
}
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}
public static BeanDefinitionHolder createScopedProxy(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
}
如果不使用动态代理,则直接返回,否则判断使用interface或是targetclass哪种。就是jdk和cglib两种代理的区别。示例中我们使用的cglib。之后进入ScopedProxyUtils.createScopedProxy方法。
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
BeanDefinitionRegistry registry, boolean proxyTargetClass) {
String originalBeanName = definition.getBeanName();
BeanDefinition targetDefinition = definition.getBeanDefinition();
// **1**
String targetBeanName = getTargetBeanName(originalBeanName);
// Create a scoped proxy definition for the original bean name,
// "hiding" the target bean in an internal target definition.
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
proxyDefinition.setSource(definition.getSource());
proxyDefinition.setRole(targetDefinition.getRole());
proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
if (proxyTargetClass) {
targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
}
else {
proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
}
// Copy autowire settings from original bean definition.
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
}
// The target bean should be ignored in favor of the scoped proxy.
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
// Register the target bean as separate bean in the factory.
registry.registerBeanDefinition(targetBeanName, targetDefinition);
// Return the scoped proxy definition as primary bean definition
// (potentially an inner bean).
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
public static String getTargetBeanName(String originalBeanName) {
return TARGET_NAME_PREFIX + originalBeanName;
}
private static final String TARGET_NAME_PREFIX = "scopedTarget.";
该方法主要做了两件事:
1、创建一个代理Bean对象,单例的,bean对象名称为之前的名字,示例为:beanB。
2、将之前的bean对象名称在//**1**处改为:scopedTarget.beanB,并注册。
3、代理Bean对象的class为ScopedProxyFactoryBean。通过ScopedProxyFactoryBean创建cglib对象,并设置TargetSource为SimpleBeanTargetSource类。
ScopedProxyFactoryBean类:
public class ScopedProxyFactoryBean extends ProxyConfig
implements FactoryBean
通过ScopedProxyFactoryBean创建cglib对象,targetSource是SimpleBeanTargetSource对象。
public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {
@Override
public Object getTarget() throws Exception {
return getBeanFactory().getBean(getTargetBeanName());
}
}
该对象获取bean对象的时候,是从spring工厂中获取beanName为scopedTarget.beanB的对象。也就是创建一个单例的动态代理对象,再被aop拦截的时候,会通过SimpleBeanTargetSource获取源bean对象,实现beanB的每次创建。aop拦截方法在CglibAopProxy类的内部类DynamicAdvisedInterceptor的intercept方法
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = targetSource.getTarget();
Class> targetClass = (target != null ? target.getClass() : null);
List
通过target = targetSource.getTarget();获取源对象要执行的方法。也就是SimpleBeanTargetSource获取的BeanB对象。
@RefreshScope注解底层是@Scope
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {
/**
* @see Scope#proxyMode()
* @return proxy mode
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
将Scope的value定义为了refresh,那么肯定就要用注册到scopes的地方。
@RefreshScope的注解是springboot的注解,在启动时会加载RefreshAutoConfiguration类。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RefreshScope.class)
@ConditionalOnProperty(name = RefreshAutoConfiguration.REFRESH_SCOPE_ENABLED,
matchIfMissing = true)
@AutoConfigureBefore(HibernateJpaAutoConfiguration.class)
public class RefreshAutoConfiguration {
/**
* Name of the refresh scope name.
*/
public static final String REFRESH_SCOPE_NAME = "refresh";
/**
* Name of the prefix for refresh scope.
*/
public static final String REFRESH_SCOPE_PREFIX = "spring.cloud.refresh";
/**
* Name of the enabled prefix for refresh scope.
*/
public static final String REFRESH_SCOPE_ENABLED = REFRESH_SCOPE_PREFIX + ".enabled";
@Bean
@ConditionalOnMissingBean(RefreshScope.class)
public static RefreshScope refreshScope() {
return new RefreshScope();
}
@Bean
@ConditionalOnMissingBean
public static LoggingRebinder loggingRebinder() {
return new LoggingRebinder();
}
@Bean
@ConditionalOnMissingBean
public ContextRefresher contextRefresher(ConfigurableApplicationContext context,
RefreshScope scope) {
return new ContextRefresher(context, scope);
}
RefreshScope对象
@ManagedResource
public class RefreshScope extends GenericScope implements ApplicationContextAware,
ApplicationListener, Ordered
RefreshScope继承自GenericScope
public class GenericScope implements Scope, BeanFactoryPostProcessor,
BeanDefinitionRegistryPostProcessor, DisposableBean {
public static final String SCOPED_TARGET_PREFIX = "scopedTarget.";
private static final Log logger = LogFactory.getLog(GenericScope.class);
private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache(
new StandardScopeCache());
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
this.beanFactory = beanFactory;
beanFactory.registerScope(this.name, this);
setSerializationId(beanFactory);
}
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
for (String name : registry.getBeanDefinitionNames()) {
BeanDefinition definition = registry.getBeanDefinition(name);
if (definition instanceof RootBeanDefinition) {
RootBeanDefinition root = (RootBeanDefinition) definition;
if (root.getDecoratedDefinition() != null && root.hasBeanClass()
&& root.getBeanClass() == ScopedProxyFactoryBean.class) {
if (getName().equals(root.getDecoratedDefinition().getBeanDefinition()
.getScope())) {
root.setBeanClass(LockedScopedProxyFactoryBean.class);
root.getConstructorArgumentValues().addGenericArgumentValue(this);
// surprising that a scoped proxy bean definition is not already
// marked as synthetic?
root.setSynthetic(true);
}
}
}
}
}
}
1、postProcessBeanFactory方法将RefreshScope注册到beanFatory中。当AbstractBeanFactory.doGetBean执行时
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' 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",
ex);
}
}
}
scopes.get("refresh"),就会调用GenericScope的get方法
private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache(
new StandardScopeCache());
public Object get(String name, ObjectFactory> objectFactory) {
BeanLifecycleWrapper value = this.cache.put(name,
new BeanLifecycleWrapper(name, objectFactory));
this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
try {
return value.getBean();
}
catch (RuntimeException e) {
this.errors.put(name, e);
throw e;
}
}
public class StandardScopeCache implements ScopeCache {
private final ConcurrentMap cache = new ConcurrentHashMap();
public Object remove(String name) {
return this.cache.remove(name);
}
public Collection
该方法将beanName作为key,将beanName和objectFactory封装成BeanLifecycleWrapper作为value,放入到StandardScopeCache对象中,在StandardScopeCache对象中,由ConcurrentMap存储。
GenericScope的get方法返回是BeanLifecycleWrapper.getBean()方法
private static class BeanLifecycleWrapper {
public Object getBean() {
if (this.bean == null) {
synchronized (this.name) {
if (this.bean == null) {
this.bean = this.objectFactory.getObject();
}
}
}
return this.bean;
}
}
2、postProcessBeanDefinitionRegistry方法,将doScan中代理bean对象的class由ScopedProxyFactoryBean设置为LockedScopedProxyFactoryBean。LockedScopedProxyFactoryBean继承ScopedProxyFactoryBean。
public static class LockedScopedProxyFactoryBean
extends ScopedProxyFactoryBean implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
if (AopUtils.isEqualsMethod(method) || AopUtils.isToStringMethod(method)
|| AopUtils.isHashCodeMethod(method)
|| isScopedObjectGetTargetObject(method)) {
return invocation.proceed();
}
Object proxy = getObject();
ReadWriteLock readWriteLock = this.scope.getLock(this.targetBeanName);
if (readWriteLock == null) {
if (logger.isDebugEnabled()) {
logger.debug("For bean with name [" + this.targetBeanName
+ "] there is no read write lock. Will create a new one to avoid NPE");
}
readWriteLock = new ReentrantReadWriteLock();
}
Lock lock = readWriteLock.readLock();
lock.lock();
try {
if (proxy instanceof Advised) {
Advised advised = (Advised) proxy;
ReflectionUtils.makeAccessible(method);
return ReflectionUtils.invokeMethod(method,
advised.getTargetSource().getTarget(),
invocation.getArguments());
}
return invocation.proceed();
}
// see gh-349. Throw the original exception rather than the
// UndeclaredThrowableException
catch (UndeclaredThrowableException e) {
throw e.getUndeclaredThrowable();
}
finally {
lock.unlock();
}
}
在执行aop方法时,通过getObject()获取ScopedProxyFactoryBean创建的动态代理对象,在执行真正方法时advised.getTargetSource().getTarget(),传入的时每次创造的Bean对象。
@Scope
1、@Scope代表spring Bean对象的作用域,一般分为singleton、prototype、request、session、application。其中request、session、application的缓存在GenericWebApplicationContext.postProcessBeanFactory方法中创建
2、@Scope的注解proxyMode的参数,默认为DEFAULT,还有INTERFACES、TARGET_CLASS两种。interfaces主要使用jdk的动态代理,target_class使用cglib代理。
3、@Scope("protype",proxyMode= TARGET_CLASS )创建动态代理时,会将注册到spring的bean对象变为两个。一个是代理Bean,实现类是ScopedProxyFactoryBean,通过SimpleBeanTargetSource获取名字scopedTarget.beanName的原Bean。另一个是将原bean名字改为scopedTarget.beanName,注册到spring中。
4、前台应用通过getBean("beanName")获取的是代理bean,真正执行方法时,由于aop的intercept方法会获取targetSource.getTarget(),也就是SimpleBeanTargetSource的getTarget()方法,该方法会调用spring,getBeanFactory().getBean(getTargetBeanName())返回每次新创建的原Bean。从而实现嵌套Bean的每次新建。
@RefreshScope
1、@RefreshScope注解底层是@Scope
2、postProcessBeanFactory方法将RefreshScope注册到beanFatory中,实现和request、session、application一样的缓存作用。
3、RefreshScope由RefreshAutoConfiguration类注入,get方法将beanName作为key,将beanName和objectFactory封装成BeanLifecycleWrapper作为value,放入到StandardScopeCache对象中,在StandardScopeCache对象中,由ConcurrentMap存储。
4、@RefreshScope和@Scope("prototype")区别在于
(1)作用域不同,@RefreshScope属于除singleton和prototype的扩展,而@Scope属于prototype
(2)用法不同。@RefreshScope实现了接受通知后(如调用ContextRefresher.refresh方法,该方法会把StandardScopeCache的map清空)再新建Bean。而@Scope("protype")每次都会新创建。