@RefreshScope 和 @Scope的使用及源码解析
结论:
@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")每次都会新创建。
一、@Scope
@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 {@Autowiredprivate 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<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();for (String basePackage : basePackages) {Set<BeanDefinition> 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 ProxyConfigimplements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {/** The TargetSource that manages scoping. */private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();public void setTargetBeanName(String targetBeanName) {this.targetBeanName = targetBeanName;this.scopedTargetSource.setTargetBeanName(targetBeanName);}@Overridepublic void setBeanFactory(BeanFactory beanFactory) {if (!(beanFactory instanceof ConfigurableBeanFactory)) {throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);}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.");}if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));}// Add an introduction that implements only the methods on ScopedObject.ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));// Add the AopInfrastructureBean marker to indicate that the scoped proxy// itself is not subject to auto-proxying! Only its target bean is.pf.addInterface(AopInfrastructureBean.class);this.proxy = pf.getProxy(cbf.getBeanClassLoader());}@Overridepublic Object getObject() {if (this.proxy == null) {throw new FactoryBeanNotInitializedException();}return this.proxy;}........
}
通过ScopedProxyFactoryBean创建cglib对象,targetSource是SimpleBeanTargetSource对象。
public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {@Overridepublic 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<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);Object retVal;// Check whether we only have one InvokerInterceptor: that is,// no real advice, but just reflective invocation of the target.if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {// We can skip creating a MethodInvocation: just invoke the target directly.// Note that the final invoker must be an InvokerInterceptor, so we know// it does nothing but a reflective operation on the target, and no hot// swapping or fancy proxying.Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = methodProxy.invoke(target, argsToUse);}else {// We need to create a method invocation...retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}retVal = processReturnType(proxy, target, method, retVal);return retVal;}finally {if (target != null && !targetSource.isStatic()) {targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}}
通过target = targetSource.getTarget();获取源对象要执行的方法。也就是SimpleBeanTargetSource获取的BeanB对象。
三、@RefreshScope
@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@ConditionalOnMissingBeanpublic static LoggingRebinder loggingRebinder() {return new LoggingRebinder();}@Bean@ConditionalOnMissingBeanpublic ContextRefresher contextRefresher(ConfigurableApplicationContext context,RefreshScope scope) {return new ContextRefresher(context, scope);}
RefreshScope对象
@ManagedResource
public class RefreshScope extends GenericScope implements ApplicationContextAware,ApplicationListener<ContextRefreshedEvent>, 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<String, Object> cache = new ConcurrentHashMap<String, Object>();public Object remove(String name) {return this.cache.remove(name);}public Collection<Object> clear() {Collection<Object> values = new ArrayList<Object>(this.cache.values());this.cache.clear();return values;}public Object get(String name) {return this.cache.get(name);}public Object put(String name, Object value) {Object result = this.cache.putIfAbsent(name, value);if (result != null) {return result;}return value;}}
该方法将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<S extends GenericScope>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// UndeclaredThrowableExceptioncatch (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")每次都会新创建。
相关文章:
RAG模型
RAG(Retrieval-Augmented Generation)模型是一种结合了检索和生成的深度学习模型 主要由两部分组成:检索模块和生成模块检索模块负责从知识库中检索出与输入相关的信息生成模块则根据检索到的信息生成输出 应用领域:问答系统,文档摘要和推荐任务,对话系统,文本翻译等...
ARCGIS PRO3 三维模型OSGB转SLPK场景数据集
1.前言 因项目工作,需要将三维模型发布到arcgisserver上,但arcgisserver只支持slpk格式的模型,于是我开启了漫长的三维模型格式转换之旅,在这里记录下本人踩过的坑。 2.三维模型数据情况 2.1 模型大小:在20GB以上&a…...
【Godot4.2】自定义Todo清单类 - myTodoList
概述 在写myList类的时候,就想到可以写一个类似的Todo清单类。 基础思路 本质还是在内部维护一个数组,在其基础上进行增删改查操作的封装为了方便存储数据,编写一个自定义内置类TodoItem,内部数组就变成了Array[TodoItem]类型的…...
C语言Linux vim shell命令
无论是在插入模式或者是其他模式下对于文件的修改都是对于内存缓冲区进行修改,只有当点击w进行保存以后才会将数据写入到一个新的文件中的,将源文件删除,并且新文件改为文件的名字 1. actionmotion dG删到文件尾 ggdG先到开头再删除到末尾…...
JVM 内存溢出、泄漏与引用
1、引用概述在栈上的reference类型存储的数据代表某块内存地址,称reference为某内存、某对象的引用。实际上引用分为很多种,从强到弱分为:强引用 > 软引用 > 弱引用 > 虚引用。平常我们使用的引用实际上是强引用,各种引用有自己的特点,强引用就是Java中普通的对象…...
平衡树炫酷科技
炫酷平衡树科技标题是吸引你点进来的 Case 1 LCT 的 access 操作应该可以实现某种区间覆盖的操作(听同学讲的,具体的还待讨论) Case 2 众所周知,可并堆中左偏树合并是这样写的(记 \(dist\) 为 \(rd\)): merge(A, B)if !A: return Bif !B: return Aif B.val > A.valsw…...
@RefreshScope 和 @Scope的使用及源码解析
结论: Scope 1、Scope代表spring Bean对象的作用域,一般分为singleton、prototype、request、session、application。其中request、session、application的缓存在GenericWebApplicationContext.postProcessBeanFactory方法中创建 2、Scope的注解proxy…...
element ui通过formdata上传文件
ElementUI的upload组件手动上传,formdata表单数据 Excerpt 使用场景:选取好excel文件后,再把导入的excel文件和参数同时提交给服务器,需要用到文件的手动上传,但是直接拷贝官网的demo会出现问题,下面会具体…...
关于微信小程序安装npm的过程,从下载到小程序内部安装完成
1.先从官网下载nodejs 网站为Node.js (nodejs.org),选择左边第一个2 然后一直next,选默认就行 选择自己喜欢的路径我的是D:\nodejs-v18.12.1 3 下载完成后,先在安装文件夹中新建两个文件夹 node_cache node_global 4 配置一下环境变量&…...
【游戏开发环境】Unity使用Mac电脑开发,开发环境的搭建(Mac mini M1 VSCode Git 好用工具)
文章目录 一、前言二、Unity下载安装三、VSCode 1、VSCode下载安装2、VSCode插件 2.1、unity3d-pack插件2.2、Bracket Pair Colorizer 2插件2.3、其他插件 3、代码无法提示与补全的问题 3.1、Windows解决办法3.2、Mac解决办法 3.2.1、安装Mono3.2.2、设置Omnisharp: Use Global…...
原神 Android 教程 —安卓版
准备材料 一台能读写 /system 分区的 Android 手机(或:一台安装了 Magisk 的 Android 手机) 有人搞出来免root端了,此条件不再必须私服客户端...
Windows安装配置tidevice
一直以来,iOS自动化的实现和执行都依赖Mac系统,其主要原因是因为需要通过Xcodebuild编译安装WDA (WebDriverAgent)到iOS设备中,通过WDA实现对被测应用进行操作。而Windows系统无法运行Xcode工具,所以无法运行iOS自动化测试&#x…...
关于Spring 的IoC和AOP的面试题,快看看你都能答上来哪些
推荐:前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。。 点击跳转到网站 🏡 博客首页:派 大 星 ⛳️ 欢迎关注 ❤️ 点赞 🎒 收藏 ✏️ 留言 …...
Python Module — OpenAI ChatGPT API
目录 文章目录目录OpenAI Python SDKopenai.ChatCompletion 模块openai.ChatCompletion.create 函数OpenAI Python SDK 官方文档:https://platform.openai.com/docs/api-reference/introduction OpenAI Python SDK 用于开发与 OpenAI RESTful API 进行交互的客户端…...
Linux 0.11
调试介绍 Linux 0.11-调试 Linux 最早期的代码-36 启动跟踪 BIOS 加载 电脑启动,CPU指向0xFFFFFFF0处,这里正好是系统ROM BIOS存放的地址。即开始执行BIOS指令。为了保持向下兼容,就会把与原PC兼容的BIOS代码和数据复制到低端1M末端的64K…...
Elasticsearch 需要了解的都在这
ES选主过程?其实ES的选主过程其实没有很高深的算法加持,启动过程中对接点的ID进行排序,取ID最大节点作为Master节点,那么如果选出来的主节点中存储的元信息不是最新的怎么办?其实他是分了2个步骤做这件事,先…...
anaconda如何改变虚拟环境安装路径
1、查看anaconda的环境配置(对应结果如下图所示) conda config --show 或者 conda info 查看虚拟环境安装位置的结果如上图所示 2、修改配置语法 conda config --add key value #添加语法 conda config --remove key value #删除语法 其中࿰…...
基于opencv的边缘检测方法
1、梯度运算 用OpenCV的形态变换( 膨胀、腐蚀、开运算和闭运算)函数morphologyEx 梯度运算即膨胀结果-腐蚀结果: 【注意】对于二值图像来说,必须是前景图像为白色,背景为黑色,否则需要进行反二值化处理 …...
Mac环境变量配置(Java)
1.打开终端: 2.输入命令:【/usr/libexec/java_home -V】,查看默认的jdk下载地址(绿色下划线的就是jdk默认路径)(注意⚠️:命令行终端是区分大小写的【-v 是不对的,必须是大写 -V】) …...
Linux定时任务:编辑、查看、删除
查看定时任务文件所在位置(通常情况下在/var/spool/cron/文件夹里,文件名通常是以创建者的用户名命名) 注意:当前登录用户名为root: 1、编辑定时任务(相当于自动在/var/spool/cron目录下,创建了一个 以当…...
昇腾AI机器人发布,12家企业、5家高校签约,昇腾AI开发者创享日全国巡展沈阳首站成功举办
“创未来,享非凡”昇腾AI开发者创享日2023年全国巡回首站活动成功举办,本次活动由辽宁省科技厅指导,由沈阳市科技局、浑南区人民政府、沈阳高新区管理委员会、华为技术有限公司共同主办,沈阳昇腾人工智能生态创新中心承办…...
基于 OpenCV 与 Java 两个语言版本实现获取某一图片特定区域的颜色对比度
本文目录一、什么是对比度二、什么是颜色直方图三、如何通过RGB计算颜色对比度什么是HSV、Lab颜色空间四、OpenCV代码五、Java代码5.1 平滑处理5.2 完整代码一、什么是对比度 对比度是指图像中不同区域之间的明暗差异程度,它是图像质量中的重要指标之一。除了颜色对…...
Springboot是什么
目录 为什么会要用springboot 1、之前 2、现在 springboot优点 springboot四大核心 自动装配介绍 1、自动装配作用是什么 2、自动装配原理 springboot starter是什么 1、starter作用 2、比如:我们想搭建java web框架 3、starter原理 SpringBootApplica…...
Mac M1通过VMWare Fusion安装Centos7记录(镜像和网络有大坑)
以前用linux系统基本都在我的服务器上或者是在win上进行,从没有在M1上进行创建,因此走了一些坑吧,这里会列出我的详细安装步骤。 下载镜像 镜像的下载网站:https://www.centos.org/download/ 在该网站中,不管是Every…...
Windows Server 2016远程桌面配置全过程
镜像下载 系统镜像网址 本次下载的是 Windows Server 2016 (Updated Feb 2018) (x64) - DVD (Chinese-Simplified) 远程桌面配置 Step 1 在开始菜单搜索服务,打开服务器管理器,点击右上角的管理按钮 Step 2 添加角色控制,点击下一步 S…...
js 数据类型
1.概念 数据类型指的是可以在程序中存储和操作的值的类型,每种编程语言都有其支持的数据类型,不同的数据类型用来存储不同的数据,例如文本、数值、图像等。 JavaScript 是一种动态类型的语言,在定义变量时不需要提前指定变量的类…...
No.020<软考>《(高项)备考大全》【第05章】项目范围管理
1 章节相关 1.1 考试相关 上午一般考3分左右,20下、21下、22上考案例分析 21上考论文写作,是案例、论文需要学习准备的重点 1.2 6个过程 (1)规划范围管理:对如何定义、确认和控制项目范围的过程进行描述。 &#x…...
LAMP架构之zabbix监控(2):zabbix基础操作
目录 一、zabbix监控节点添加和删除 (1)手动添加 (2)自动添加 (3)按照条件批量添加 (4)使用api工具进行管理 二、针对应用的zabbix监控 一、zabbix监控节点添加和删除 实验说明&a…...
九龙证券|重磅文章引爆两大板块!千亿煤炭龙头强势涨停!
A股今天走势分化,创业板指逆市拉升;港股走势疲弱,恒生科技指数一度跌超3%。 具体来看,A股方面,沪指弱势震荡下探,盘中一度跌超1%,午后跌幅收窄;创业板指走势微弱,午后涨超…...
OCR之论文笔记TrOCR
文章目录TrOCR: Transformer-based Optical Character Recognition with Pre-trained Models一. 简介二. TrOCR2.1. Encoder2.2 Decoder2.3 Model Initialiaztion2.4 Task Pipeline2.5 Pre-training2.6 Fine-tuning2.7 Data Augmentation三. 实验3.1 Data3.2 Settings3.2 Resul…...
静态路由+DHCP实验(四路由器八PC)
一.200.1.1.0/24子网划分 1.划分八个子网 2.选用前5个,第五个子网再划分4个子网作为骨干 二.规划路由 三.配置(下一跳) 1.先依次实现四个路由器之间全网可通 2.为路由器配置地址池,使用全局模式获取dhcp,指定网关…...
文件包含漏洞原理与实践
「作者主页」:士别三日wyx 「作者简介」:CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 文件包含漏洞一、什么是文件包含漏洞?二、文件包含函数三、两种文件包含1、本地文件包含1.1 使用教程演示2、远程文件包含1.1 使…...
利用摄影测量进行地形建模的介绍
一、前言 从一个地方到另一个地方的地球表面由连续和突然的海拔变化组成,个人和社会都必须应对这些变化。 水从高山和丘陵向下流,从溪流流入河流,形成三角洲,最终汇入大海。 三维 (3D) 地面信息的获取和表示一直是与行星表面相关的…...
【Nginx】Nginx 常用的基础配置
文章目录一、基础配置二、隐藏 Nginx 版本信息三、禁止ip直接访问80端口四、启动 web 服务 (vue 项目为例)五、PC端和移动端使用不同的项目文件映射六、一个web服务,配置多个项目 (location 匹配路由区别)七、配置负载均衡八、SSL 配置 HTTPS一、基础配置 user …...
基于OpenCV的图片和视频人脸识别
目录 🥩前言 🍖环境使用 🍖模块使用 🍖模块介绍 🍖模块安装问题: 🥩人脸检测 🍖Haar 级联的概念 🍖获取 Haar 级联数据 🍗 1.下载所需版本 🍗 2.安…...
比肩ChatGPT的国产AI:文心一言——有话说
🔗 运行环境:chatGPT,文心一言 🚩 撰写作者:左手の明天 🥇 精选专栏:《python》 🔥 推荐专栏:《算法研究》 #### 防伪水印——左手の明天 #### 💗 大家好&am…...
剑指offer JZ77 按之字形顺序打印二叉树
Java JZ77 按之字形顺序打印二叉树 文章目录Java JZ77 按之字形顺序打印二叉树一、题目描述二、双栈法三、队列reverse()法使用双栈法和队列reverse()法解决剑指offer JZ77 按之字形顺序打印二叉树的问题。 一、题目描述 给定一个二叉树,返回该二叉树的之字形层序遍…...
【Azure 架构师学习笔记】-Azure Data Factory (5)-Managed VNet
本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Data Factory】系列。 接上文【Azure 架构师学习笔记】-Azure Data Factory (4)-触发器详解-事件触发器 前言 PaaS服务默认都经过公网传输, 这对很多企业而言并不安全,那么就需要对其进行安全改…...
聚类算法层次聚类
###cluster.py #导入相应的包 import scipy import scipy.cluster.hierarchy as sch from scipy.cluster.vq import vq,kmeans,whiten import numpy as np import matplotlib.pylab as plt #生成待聚类的数据点,这里生成了20个点,每个点4维: pointsscipy.randn(20,4) #加一…...
大数据写入到Oracle数据库(批量插入数据)
开发中经常遇到批量插入数据的需求,为了提高开发效率大多会使用ORM架构,个别之处 才会手写SQL,我们使用C#.NET Core5.0开发,所以优先选择了微软的EF。 但是EF原生没有批量操作功能,需要自己扩展或使用第三方的扩展&am…...
【LeetCode】剑指 Offer 39. 数组中出现次数超过一半的数字 p205 -- Java Version
题目链接:https://leetcode.cn/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/ 1. 题目介绍(39. 数组中出现次数超过一半的数字) 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可…...
es-head插件插入查询以及条件查询(五)
es-head插件插入查询以及条件查询 1.es-head插件页面介绍 页面详细介绍 2.es-head查询语句 2.1.查询索引中的全部数据 curl命令交互,采用GET请求 语法格式: curl -XGET es地址:9200/索引名/_search?pretty [rootelaticsearch ~]# curl -XGET 192…...
数据挖掘(作业汇总)
目录 环境配置 实验1 数据 作业2 环境配置 实验开始前先配置环境 以实验室2023安装的版本为例: 1、安装anaconda:(anaconda自带Python,安装了anaconda就不用再安装Python了) 下载并安装 Anaconda3-2022.10-Windows-x86_64.ex…...
基于微信小程序的图书馆选座系统源码
开发环境及工具: 大等于jdk1.8,大于mysql5.5,idea(eclipse),微信开发者工具 技术说明: springboot mybatis 小程序 代码注释齐全,没有多余代码,适合学习(…...
特斯拉4D成像毫米波雷达与木牛科技早期开源的产品几乎一模一样?
近日,在中欧智能网联汽车协会秘书长林示的组织下,中益基金投资总监王子文考察了全球毫米波雷达领跑者—木牛科技。 考察过程中,木牛科技COO王卫向两位详细介绍了木牛科技具备怎样的全球毫米波雷达领跑者的科技硬实力,以及木牛科技…...
[golang gin框架] 10.Gin 商城项目介绍
一.商城项目介绍 1.详细功能介绍图 2.数据库 ER 图 需要用到的数据表举例 二.MVC架构搭建以及执行流程分析 1.关于 MVC 模式的简单介绍 Gin 不是一个 MVC 的框架,所有的代码都可以写在 main.go 中。当我们的项目比较大的时候, 所有代码写在一个文件里面…...
python@调用系统命令行@os.system@subprocess@标准输入输出@sys.stdin@sys.stdout@input@print
文章目录python调用系统命令行os.system标准输入输出sys.stdinsys.stdoutinputprint概要os.systemdemoswindows命令解释器ComSpecsubprocessrecommended🎈基本用法demos标准输入输出sys.stdininput()sys.stdin.inputinput()交互模式小结sys.stdoutsys.stdout.wirte(…...
15-哈希表
哈希表(Hash table),也称散列表,是一个能够将数值映射而成地址从而进行直接访问的数据结构,通过哈希表我们可以快速、迅捷地访问数据。 哈希表原理 假设我们拥有一个数x(也称关键值,key&#…...
【超详细文件操作(三)】C语言
作者:日出等日落 专栏:C语言 只有流过血的手指,才能弹出世间的绝唱。 ——泰戈尔 目录 1.文件的随机读写 1.1 fseek函数 1.1.1 下面使用fseek函数 1.2 ftell函数 1.3 rewind函数 …...
【数据分析之道-基础知识(四)】字典
文章目录专栏导读1、字典简介2、字典创建3、字典访问4、字典修改5、字典添加6、字典删除7、字典内置函数专栏导读 ✍ 作者简介:i阿极,CSDN Python领域新星创作者,专注于分享python领域知识。 ✍ 本文录入于《数据分析之道》,本专栏…...
【计组】RAM的深入理解
一、存储机理 RAM的实现逻辑有种,分别是触发器和电容。 SRAM(Static)DRAM(Dynamic)存储方式触发器电容破坏性读出否(触发器具有稳态,能够锁住0或1两种状态)是(电容需要…...
51单片机学习笔记_13 ADC
ADC 使得调节开发板上的电位器时,数码管上能够显示 AD 模块 采集电位器的电压值且随之变化。 开发板上有三个应用:光敏电阻,热敏电阻,电位器。 一般 AD 转换有多个输入,提高使用效率。 ADC 通过地址锁存与译码判断采…...
JVM 常量池
Class文件常量池 每个Class文件都会有一个单独的常量池,我们称为Class文件常量池,我们可以用javap命令反汇编Class文件,可以查看java编译器为我们生成的字节码。 CLass文件常量池存下内容: 字面量: 文本字符串(代码中用双引号包裹的字符串部分的值)被…...
Leetcode.1024 视频拼接
题目链接 Leetcode.1024 视频拼接 Rating : 1746 题目描述 你将会获得一系列视频片段,这些片段来自于一项持续时长为 time秒的体育赛事。这些片段可能有所重叠,也可能长度不一。 使用数组 clips描述所有的视频片段,其中 clips[i…...
彩色滤镜阵列CFA与Bayer滤镜
彩色滤镜阵列(CFA) 在彩色光电摄影中,可以通过分光,使用三个传感器分别捕获红、绿、蓝三个通道的颜色分量。但为了降低成本,绝大部分成像系统中采用单芯片 CMOS、CCD 传感器结合彩色滤镜阵列(Color Fiter Array&#…...
Unity和GAN实现AI绘画五等分的新娘、国家队02(附源码)
UnityGAN实现AI绘画五等分的新娘、国家队02附源码写在前面源码演示视频部分界面展示使用教程敬请期待其他写在后面写在前面 博主喜欢二次元,想着在读研期间做点对自己有意义的事,因此选择了动漫人物生成方向(呜呜呜,太感谢导师理解和支持了)…...
Android 分享页面下载app后 自动填充邀请码
android 分享出去h5页面,通过页面下载对应包,安装启动登录注册时候,需要自动填写邀请码,一般情况下,我们需要将分享人信息写入app包中,这样下载时候根据这个信息可以获取到对应安装包。 android我们可以把…...
数据分析专家能力模型
招式:懂商业(业务能力) 外功更偏重于技能,首先需要懂招式,即懂商业,数据分析最终是为业务服务的,无论是互联网企业准求的用户增长和UJM分解,还是传统企业追求的降本增效和精细化运营…...
安卓免费抠图、AI绘图、修图软件——SnapEdit
一、前言 AI绘图原理主要基于深度学习和神经网络技术,通过训练数据集中的图像,从中学习并提取特征,进而生成新的图像。这一过程涉及到多种技术和模型,包括但不限于VAE(变分自编码器)、auto-encoder、GAN&a…...
设计模式-创建型模式-工厂模式
工厂模式是一种用来创建对象的模式,它将对象的创建和使用分离开来,使得代码更加灵活和可扩展。 下面代码中CarFactory是一个工厂类,它根据传入的参数来创建不同类型的Car对象。通过工厂模式,在不改变客户端代码的情况下轻松地添加…...
Lock-It for Mac(应用程序加密工具)
OSXBytes Lock-It for Mac是一款功能强大的应用程序加密工具,专为Mac用户设计。该软件具有多种功能,旨在保护用户的隐私和数据安全。 Lock-It for Mac v1.3.0激活版下载 首先,Lock-It for Mac能够完全隐藏应用程序,使其不易被他人…...
Pytorch 学习路程 - 1:入门
目录 下载Pytorch 入门尝试 几种常见的Tensor Scalar Vector Matrix AutoGrad机制 线性回归尝试 使用hub模块 Pytorch是重要的人工智能深度学习框架。既然已经点进来,我们就详细的介绍一下啥是Pytorch PyTorch 希望将其代替 Numpy 来利用 GPUs 的威力&…...