一.问题引入

代码如下:

@RestController
public class ConfigController {@Autowiredprivate TestService testService;@GetMapping("/testPrototype")public String testPrototype() {System.out.println(testService);return testService.toString();}
}
@Scope("prototype")
@Component
public class TestService {
}

当我们访问/testPrototype这个接口的时候,可以发现注入的TestService实例永远都只会有一个,如下图:

也就是说TestService在ConfigController中是以单例存在的,而我们想要的TestService的多例效果则是每次请求/testPrototype这个接口的时候都能够获取到不同的TestService实例,那么怎样才能达到这种效果呢?有两种方式,一种是把TestService的作用域设置为request,另一种就是说每次在调用TestService的方法之前,都需要从spring容器中获取到一个新的TestService实例,然后调用这个新实例的方法,而这种方式就是我们这里所说的作用域代理的方式

二.什么是bean的作用域代理?

在说作用域代理之前,我们先来讲一下@Scope注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {@AliasFor("scopeName")String value() default "";@AliasFor("value")String scopeName() default "";ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;}

在@scope注解中有两个属性我们需要关注,一个是value属性,该属性就是我们常说的指定的作用域,比如在spring中默认有singleton,prototype两种,springmvc中默认有request,sessioon等作用域;还有一个属性就是proxyMode属性,该属性就是是否指定作用域代理,它有4个可选的值:

public enum ScopedProxyMode {DEFAULT,NO,INTERFACES,TARGET_CLASS}

其中DEFAULT和NO这两个值表示不使用作用域代理,INTERFACES和TARGET_CLASS都表示使用作用域代理,只不过INTERFACES表示使用JDK动态代理模式,而TARGET_CLASS表示使用cglib代理模式,那么说了这么多这个作用域代理到底有什么用呢?回到上面的问题,在ConfigController中,使用注入的TestService实例并没有办法实现多实例的效果,原因也很简单,就是因为ConfigController是单例的,它在经历spring的bean生命周期属性注入的时候就只注入了一次TestService实例,在这之后ConfigController所拥有的TestService实例就不会变了。如果需要实现多实例TestService得需要是每一次使用TestService的时候都要去spring容器获取,spring容器判断到TestService的scope是prototype的时候就会创建一个新的TestService实例给用户,那么上面的代码我们可以修改成这样:

@RestController
public class ConfigController {@Autowiredprivate ApplicationContext applicationContext;@GetMapping("/testPrototype")public String testPrototype() {TestService testService = applicationContext.getBean(TestService.class);System.out.println(testService);return testService.toString();}
}

每一次都从spring容器中拿TestService实例,而TestService的作用域是多实例的,所以每一次拿到的实例都是不一样的。而使用作用域代理则不需要这么麻烦,还是保持原来的代码结构,只需要在@Scope注解上面声明开启作用域代理就行,代码如下:

@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class TestService {
}

那么作用域代理是如何实现在ConfigController这么一个单例bean中能够使用到多实例的bean呢?我们先来想想这个问题的解决思路,ConfigController这个bean在它的spring生命周期的属性注入的时候注入了TestService实例,也就是说TestService这个属性引用永远都会只指向同一个TestService实例了,这一点是无法更改的,想要更换这个引用指向不同TestService实例基本上是不可能的了,所以这时就可以考虑用代理了,也就是说ConfigController注入的是一个TestService的代理,当调用TestService的方法的时候,TestService代理会去执行目标类的方法,而这个目标类就可以由我们自己来控制了,比如实现多实例,那么就从spring容器中重新拿一遍TestService不就好了,而这也就是spring的作用域代理的实现原理,根据上面的分析总结来说其实作用域代理的出现就是为了解决一个作用域范围大的bean依赖了一个作用域范围小的bean的场景

三.bean的作用域代理实现原理分析

org.springframework.context.annotation.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) {// 完成了扫描  class文件----有选择性bdSet<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {// 默认解析该类上面的@Scope注解,然后把@Scope注解中的属性封装到ScopeMetadata对象中ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);// 如果是AbstractBeanDefinitionif (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);// 在里面会判断是否需要基于原始的bd去创建一个新的scope代理bd// 如果需要,那么这里返回的definitionHolder就是新的scope代理bd,反之就是原始的bddefinitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);// 注册bd// 这里需要注意的是,如果上面返回的definitionHolder是一个新的scope代理bd,那么原始的bd不就没有被注册吗?// 答案是如果返回的definitionHolder是一个新的scope代理bd,那么原始的bd就会在applyScopedProxyMode方法中被注册registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;
}

当spring容器启动的时候会去扫描所有的bd,然后判断每一个bd中是否有@Scope注解,如果有的话解析@scope注解中的value和proxyMode属性,并把属性值封装到一个ScopeMetadata对象中,代码如下:

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) {// 获取注解设置的value属性metadata.setScopeName(attributes.getString("value"));// 获取注解设置的proxyMode属性ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");// 如果proxyMode == DEFAUL,那么proxyMode就等于默认的代理类型if (proxyMode == ScopedProxyMode.DEFAULT) {proxyMode = this.defaultProxyMode;}metadata.setScopedProxyMode(proxyMode);}}return metadata;
}

获取到@scope注解的属性值之后,有一行很关键的代码,就是会去判断是否开启了作用域代理

static BeanDefinitionHolder applyScopedProxyMode(ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {// 获取到scope代理模式ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();// 条件成立:scope代理模式等于NO,直接返回// 默认@Scope注解中指定的scope代理模式就是NOif (scopedProxyMode.equals(ScopedProxyMode.NO)) {return definition;}// 代码执行到这里说明scope代理模式不是NOboolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);// 基于原始的bd创建一个新的scope代理bdreturn ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
}
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,BeanDefinitionRegistry registry, boolean proxyTargetClass) {// 获取原始bd的beanNameString originalBeanName = definition.getBeanName();BeanDefinition targetDefinition = definition.getBeanDefinition();// targetBeanName = scopedTarget.beanNameString targetBeanName = getTargetBeanName(originalBeanName);// Create a scoped proxy definition for the original bean name,// "hiding" the target bean in an internal target definition.// 创建一个新的scope代理的bd对象RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);// 把原始bd放到这个新创建的scope代理bd中proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));proxyDefinition.setOriginatingBeanDefinition(targetDefinition);proxyDefinition.setSource(definition.getSource());proxyDefinition.setRole(targetDefinition.getRole());// 设置属性注入时ScopedProxyFactoryBean类中的targetBeanName属性值为scopedTarget + 原始beanNameproxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);// 条件成立:使用cglib对原始bd进行代理if (proxyTargetClass) {// 给原始bd设置一个使用cglib代理的标记位targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);}else {proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);}// 从原始bd中复制属性注入的配置,比如复制是否需要属性注入,是否是Primary注入proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());proxyDefinition.setPrimary(targetDefinition.isPrimary());if (targetDefinition instanceof AbstractBeanDefinition) {proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);}// 忽略原始bd,以支持scope代理targetDefinition.setAutowireCandidate(false);targetDefinition.setPrimary(false);// 注册原始bdregistry.registerBeanDefinition(targetBeanName, targetDefinition);// 返回scope代理bdreturn new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}

核心就是最后会调用createScopedProxy方法,

1.在这个方法中会去创建一个新的作用域代理bd对象,并且主要的是这个bd中的class类型是ScopedProxyFactoryBean,这个类很明显就是一个FactoryBean,下面我们再详细说这个类,

2.给ScopedProxyFactoryBean的targetBeanName属性设置 一个值,这个targetBeanName等于“scopedTarget.” + 原始bd的beanName,还会根据@scope注解中设置的proxyMode类型去设置proxyTargetClass的值,proxyTargetClass等于true的时候就使用cglib生成代理,否则就使用JDK动态代理;

3.新的作用域代理bd对象复制原始bd对象的一些属性注入的配置属性,并且复制完之后原始bd会把autowireCandidate属性和primary属性都设置为false(表示原始bd不能被其他bd进行属性注入)

4.注册原始bd到容器中

5.返回新的作用域代理bd

外层的方法拿到新的作用域代理bd之后也会把这个bd注册到容器中,也就是说此时容器中会有这个类的原始bd以及由这个类衍生出来的一个作用域代理bd

org.springframework.aop.scope.ScopedProxyFactoryBean

public class ScopedProxyFactoryBean extends ProxyConfigimplements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {/*** 目标源对象*/private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();/*** 被代理的bean的名称*/@Nullableprivate String targetBeanName;/*** 当前FactoryBean创建的代理对象*/@Nullableprivate Object proxy;/*** Create a new ScopedProxyFactoryBean instance.*/public ScopedProxyFactoryBean() {setProxyTargetClass(true);}/*** Set the name of the bean that is to be scoped.*/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;// 给目标源设置一个BeanFactory,因为SimpleBeanTargetSource这个目标源需要从BeanFactory中获取被代理对象this.scopedTargetSource.setBeanFactory(beanFactory);// 创建一个代理工厂ProxyFactory pf = new ProxyFactory();// 设置代理配置pf.copyFrom(this);// 设置目标源pf.setTargetSource(this.scopedTargetSource);Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");// 从BeanFactory中获取到被代理bean的类型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.");}// 使用jdk动态代理if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));}// 创建ScopedObject实例ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());// 给代理对象添加一个introduction类型的拦截器,并且把scopedObject作为introduction实现委托对象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;}@Overridepublic Class<?> getObjectType() {if (this.proxy != null) {return this.proxy.getClass();}return this.scopedTargetSource.getTargetClass();}@Overridepublic boolean isSingleton() {return true;}}

接下来我们就详细地去看ScopedProxyFactoryBean这个类,这个类继承了FactoryBean接口,所以我们主要关注它的getObject方法,在getObject方法中就直接返回了一个proxy对象,而这个proxy对象是在setBeanFactory方法中初始化的,而setBeanFactoryBean方法的调用时机在bean的生命周期中是在属性注入这个阶段之后执行的

org.springframework.aop.scope.ScopedProxyFactoryBean#setBeanFactory

public void setBeanFactory(BeanFactory beanFactory) {if (!(beanFactory instanceof ConfigurableBeanFactory)) {throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);}ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;// 给目标源设置一个BeanFactory,因为SimpleBeanTargetSource这个目标源需要从BeanFactory中获取被代理对象this.scopedTargetSource.setBeanFactory(beanFactory);// 创建一个代理工厂ProxyFactory pf = new ProxyFactory();// 设置代理配置pf.copyFrom(this);// 设置目标源pf.setTargetSource(this.scopedTargetSource);Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");// 从BeanFactory中获取到被代理bean的类型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.");}// 使用jdk动态代理if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));}// 创建ScopedObject实例ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());// 给代理对象添加一个introduction类型的拦截器,并且把scopedObject作为introduction实现委托对象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());
}

(1)设置目标源

首先会创建一个ProxyFactory对象,我们知道这是一个代理工厂对象,通过这个对象的一些API就可以创建出一个代理对象,而ProxyFactory中比较重要的就是设置一个目标源,我们知道一个代理对象首先它需要有一个目标代理类,也就是它代理的对象是谁,而从目标源中就可以获取到目标代理对象。这里设置的具体目标源是SimpleBeanTargetSource,我们可以看下这个目标源有啥功能

public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {@Overridepublic Object getTarget() throws Exception {return getBeanFactory().getBean(getTargetBeanName());}}

很简单,就是从容器中根据beanName去获取到对应的bean作为目标代理对象,也就是说每一次代理对象在调用代理方法的时候,都会从容器中拿到对应的bean作为目标代理对象去执行代理方法。而这个beanName是怎么来的呢?这个就需要回到上面生成ScopedProxyFactoryBean的beanDefinition的时候了,注意这一行代码:

proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);

在创建ScopedProxyFactory的beanDefinition的时候会直接给targetBeanName属性赋值,也就是说这个targetbeanName的值就是target + 上面说的原始beanDefinition的beanBean

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

然后在ScopedProxyFactory进行属性注入的时候就会调用到上面的setTargetBeanName方法,此时就会把targetBeanName传给了SimpleTargetSource

(2)给ProxyFactory添加一个advice

在上面设置好了目标源之后,还有一个比较重要的就是添加advice增强器了,因为是代理对象,那么肯定就是对代理目标对象进行了一定的增强,而这里添加的增强器不是普通类型的增强器,而是Introduction类型的advice。那么什么是Introduction类型的代理增强?普通的代理增强是针对方法级别的增强,而Introduction类型的代理则可以针对类的增强,举个例子就是可以指定一个Introduction委托实现类,然后生成的代理对象就会实现这个Introduction委托实现类的所有接口,所以代理对象可以调用这些接口,而接口的实现就是指定的Introduction委托实现类对应实现的接口方法,关于Introduction代理之后会详细讲这一块的实现,这里大概知道它的功能就可以了,我们看下在ScopedProxyFactory中是怎么给ProxyFactory添加Introduction类型的advice:

// 创建ScopedObject实例
ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
// 给代理对象添加一个introduction类型的拦截器,并且把scopedObject作为introduction实现委托对象
pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));

这里创建了一个DefaultScopedObject实例作为DelegatingIntroductionInterceptor的构造函数参数传了进去,先来看下DelegatingIntroductionInterceptor这个类:

public class DelegatingIntroductionInterceptor extends IntroductionInfoSupportimplements IntroductionInterceptor {/*** 实现introduction接口的对象* 当代理类执行introduction接口的时候,具体调用的是该类上的实现*/@Nullableprivate Object delegate;/*** 无需子类继承,直接调用该构造方法创建一个实例放到ProxyFactory中,参数代表introduction实现委托类,可以随意指定* @param delegate the delegate that implements the introduced interfaces*/public DelegatingIntroductionInterceptor(Object delegate) {init(delegate);}/*** 如何会使用到这个构造方法:子类继承于DelegatingIntroductionInterceptor,在创建子类的同时就会调用该构造方法,也就是说子类就会成为introduction实现委托类*/protected DelegatingIntroductionInterceptor() {init(this);}private void init(Object delegate) {Assert.notNull(delegate, "Delegate must not be null");this.delegate = delegate;// 初始化introduction接口implementInterfacesOnObject(delegate);// We don't want to expose the control interfacesuppressInterface(IntroductionInterceptor.class);suppressInterface(DynamicIntroductionAdvice.class);}@Override@Nullablepublic Object invoke(MethodInvocation mi) throws Throwable {// 条件成立:要执行的方法属于introduction接口方法if (isMethodOnIntroducedInterface(mi)) {// 执行introduction实现委托类的方法Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments());// Massage return value if possible: if the delegate returned itself,// we really want to return the proxy.if (retVal == this.delegate && mi instanceof ProxyMethodInvocation) {Object proxy = ((ProxyMethodInvocation) mi).getProxy();if (mi.getMethod().getReturnType().isInstance(proxy)) {retVal = proxy;}}// 直接返回,不进行后续拦截器的链式调用return retVal;}// 不是introduction接口方法继续执行拦截器的链式调用return doProceed(mi);}protected Object doProceed(MethodInvocation mi) throws Throwable {// If we get here, just pass the invocation on.return mi.proceed();}}

这个类实现了IntroductionInterceptor接口,所以它是一个Introduction类型的增强器。在它的带参构造方法中,参数就表示Introduction委托实现,并且在调用构造方法的时候会调用init方法,在init方法中主要做的就是获取到Introduction委托实现的所有接口,目的就是当代理对象调用代理方法的时候能够去判断该方法是否是一个introduction接口方法,如果是的话就会去调用Introduction委托实现对应的接口方法,如果不是就继续拦截器的链式调用。所以可以知道通过@Scope注解作用域生成的代理对象,可以强转成ScopedObject,然后就可以调用ScopedObject接口下的方法了,对应的实现就是调用DefaultScopedObject,比如我们可以这样使用:

// testService是通过作用域代理产生的代理对象
ScopedObject scopedObject = (ScopedObject) testService;
System.out.println(scopedObject.getTargetObject());

当执行scopedObject.getTargetObject()这行代码的时候会发生什么呢?根据上面说的就会调用到对应的Introduction委托实现的getTargetObject方法,这个Introduction委托实现就是DefaultScopedObject,所以来看下DefaultScopedObject中的实现做了什么:

public class DefaultScopedObject implements ScopedObject, Serializable {private final ConfigurableBeanFactory beanFactory;private final String targetBeanName;public DefaultScopedObject(ConfigurableBeanFactory beanFactory, String targetBeanName) {Assert.notNull(beanFactory, "BeanFactory must not be null");Assert.hasText(targetBeanName, "'targetBeanName' must not be empty");this.beanFactory = beanFactory;this.targetBeanName = targetBeanName;}@Overridepublic Object getTargetObject() {return this.beanFactory.getBean(this.targetBeanName);}@Overridepublic void removeFromScope() {this.beanFactory.destroyScopedBean(this.targetBeanName);}}

可以看到如果我们把代理对象强转成ScopedObject类型,那么当调用getTargetObject方法的时候就能够调用到DefaultScopedObject的getTargetObject方法,通过这个方法就能够获取到原始bean,也就是目标代理对象

四.总结

当存在一个bean依赖另一个bean,并且被依赖的那个bean的作用域范围是小于依赖它的那个bean的作用域范围的时候,如果被依赖的bean不使用作用域代理的方式,那么被依赖的这个bean的生命周期就会变成与依赖它的那个bean的生命周期一致(具体参考上面单例bean依赖多实例bean的例子),而要想被依赖的这个bean继续拥有自己声明的生命周期,那么spring就提供了作用域代理这一种方式去解决这种问题。作用域代理的实现方式就是对标注了@Scope注解并且开启了proxyMode属性的bean在生成它的BeanDefinition的时候进行拦截,拦截的时候会生成一个新的BeanDefinition,但是这个新的BeanDefinition里面的class是ScopedProxyFactory,这个ScopedProxyFactory就会生成一个代理对象,当这个代理对象去调用代理方法的时候,就会从容器中拿到原始bean作为目标代理对象,也就是说每一次调用被依赖的bean的方法的时候就会从容器中获取一次对应最新的原始bean,这样就可以保证了被依赖的bean自己的生命周期不会受到依赖它的bean所影响了

深入了解Spring的@Scope注解中作用域代理proxyMode属性的实现相关推荐

  1. 通过反射获取Spring定时器@Scheduled注解中fixedDelay时间

    前言: 如何获取Spring的定时器注解@Scheduled中的时间值(即获取3000):@Scheduled(fixedDelay = 3000) 注:定时器类:org.springframewor ...

  2. 三、Spring的@Scope设置组件作用域

    还是和上节一样,首先来看下配置类:MainConfig2 @Configuration 名 public class MainConfig2 {@Scope("singleton" ...

  3. Spring之使用注解实例化Bean并注入属性

    1.准备工作 (1)导入jar包 除了上篇文章使用到的基本jar包外,还得加入aop的jar包,所有jar包如下 所需jar包 (2)配置xml <?xml version="1.0& ...

  4. 【Spring注解驱动开发】使用@Scope注解设置组件的作用域

    写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...

  5. Spring Cloud Alibaba 官方中文文档

    Spring Cloud Alibaba 官方中文文档 1. 介绍 Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案.此项目包含开发分布式应用服务的必需组件,方便开发者通 ...

  6. spring的@ControllerAdvice注解

    @ControllerAdvice注解是Spring3.2中新增的注解,学名是Controller增强器,作用是给Controller控制器添加统一的操作或处理. 对于@ControllerAdvic ...

  7. Spring配置文件applicationContext.xml中bean>>property>>name属性的含义

    Spring配置文件applicationContext.xml中bean>>property>>name属性表示的含义 首先我们知道property是bean元素的子元素,它 ...

  8. 一分钟学会spring注解之@Scope注解

    今天主要从以下几方面来介绍一下@Scope注解 @Scope注解是什么 @Scope注解怎么使用 @Scope注解的使用场景 1,@Scope注解是什么 @Scope注解是springIoc容器中的一 ...

  9. Spring系列(四):@Scope注解用法介绍

    今天给大家分享Spring中@Scope注解的用法,希望对大家能有所帮助! @Scope 定义以及作用 @Scope注解主要作用是调节Ioc容器中的作用域,在Spring IoC容器中主要有以下五种作 ...

  10. spring bean scope作用域及多线程安全问题场景分析

    2019独角兽企业重金招聘Python工程师标准>>> Scope作用域 在 Spring IoC 容器中具有以下几种作用域: singleton:单例模式,在整个Spring Io ...

最新文章

  1. concurrent.futures模块(进程池线程池)
  2. linux shell 提取txt文件到数组
  3. OpenCV放大图像:单输出
  4. 为Java应用程序编写数据驱动的测试
  5. js关闭setInterval及终止ajax请求
  6. 花了一晚上时间,终于把Python的基本用法归纳好了!
  7. java 日期 年数_关于java:为什么不赞成使用“新日期(整数年,整数月,整数日)”?...
  8. Excel的一些工作中不算是常见,但是遇到时常常不知所错的,问题,与解决方案。
  9. 杭电 2016 计算机组成原理,杭电计算机组成原理多功能ALU设计实验
  10. stringbuilder 拼接语句缺失右括号_C++编程基础: 6. 语句、表达式和运算符
  11. Mac上安装Mysql配置文件的添加及修改配置文件
  12. 终端conda install安装包 import 错误_Tecplot 360 2017软件安装包+安装教程
  13. no module named pytz(pycharm)
  14. 单片机和嵌入式的区别
  15. 【已解决】ubuntu无法进入启动页面,全屏出现【ok】并且不停闪烁
  16. oracle数据库处于recover,oracle数据库recover和restore的区别
  17. 几经沉浮,人工智能(AI)前路何方?
  18. 计算机开机响三短嘀嘀,电脑显示器不亮,开机2短3长报警音什么情况啊?:电脑...
  19. 苹果手机指纹识别坏了怎么办?维修需要多少钱?
  20. 全新的 Uber 应用设计

热门文章

  1. Android Studio新建工程syncing失败;Android studio Connection timed out: connect
  2. 图像条纹检测 python_光源在外观缺陷检测中的应用
  3. 【机器学习系列】变分推断第一讲:Variational Inference背景和用途
  4. 回文自动机算法+马拉车算法概述及习题【回文串系列问题】
  5. 如果一非零的整系数多项式能够分解成两个次数较低的有理系数多项式的乘积,那么它一定能分解成两个次数较低的整系数多项式的乘积
  6. wireshark过滤指定ip的数据包
  7. 使用BoobSnail生成任意Excel 4.0 XLM宏文件
  8. WinPmem:跨平台内存采集工具
  9. 引入log4j日志包为何会自动导入额外两个包(门面模式)
  10. win安装appium