首先查看下@Scope注解的定义信息,其共有三个方法,分别为value、scopeName、proxyMode,其中value和scopeName利用了Spring的显性覆盖,这两个方法的作用是一样的,只不过scopeName要比value更具有语义性。重点是proxyMode方法,其默认值为ScopedProxyMode.DEFAULT。

@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;}

ScopedProxyMode是一个枚举类,该类共定义了四个枚举值,分别为NO、DEFAULT、INTERFACE、TARGET_CLASS,其中DEFAULT和NO的作用是一样的。INTERFACES代表要使用JDK的动态代理来创建代理对象,TARGET_CLASS代表要使用CGLIB来创建代理对象。

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

解析@Scope注解的入口有很多,但它们的行为都是一致的,这里以扫描组件信息入口为例来进行分析。

在ClassPathBeanDefinitionScanner的doScan方法中,对于findCandidateComponents方法返回值进行遍历时,会首先调用ScopeMetadataResolver的resolveScopeMetadata方法,传入BeanDefinition对象。该方法会返回一个ScopeMetadata对象,然后将该对象设置到BeanDefinition中去,通过BeanDefinition的setScope方法。

接下来便是通过BeanNameGenerator的generatedBeanName方法来生成BeanName,判断BeanDefinition对象(以下简称为candidate)是否是AbstractBeanDefinition,如果判断成立,则调用postProcessBeanDefinition方法(该方法主要用来设置BeanDefinition的一些默认值),判断candidate是否是AnnotatedBeanDefinition,如果判断成立则调用AnnotationConfigUtils的processCommonDefinitionAn-notations方法(通过方法名也可以看出,该方法主要用来解析一些通用的注解)。

调用checkCandidate方法,如果该方法返回值为true(该方法用来判断当前(注意,不是层级查找)IoC容器中是否指定BeanName的BeanDefinition信息,如果包含,则进行兼容性比对)。

创建BeanDefinitionHolder实例,然后调用AnnotationConfigUtils的applyScopedProxyMode方法来根据前面解析好的ScopeMetadata对象来处理BeanDefinitionHolder,注意这里传了BeanDefinitionRegistry实例,最后调用registerBeanDefinition方法将AnnotationConfigUtils的applyScopedProxyMode方法返回值注册到BeanDefinition到IoC容器中。

private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
// 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) {// 根据指定包路径扫描Bean资源并加载Set<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {// 使用AnnotationScopeMetadataResolver的resolveScopeMeatdata方法来根据Bean中@Scope(如果有)注解创建ScopeMeatdata对象ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());// 调用AnnotationBeanNameGenerator的generatorBeanName方法生成beanNameString beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);// 如果BeanDefinition是AbstractBeanDefinition类型的,设置BeanDefinition的默认值if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}// 如果BeanDefinition是AnnotatedBeanDefinition类型,解析通用注解if (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}// 如果BeanDefinition可以兼容if (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);// 解析Bean中的@Scope注解definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}

首先分析下ScopeMetadataResolver这个接口。该接口存在两个实现类,分别为AnnotationScopeM-etadataResolver和Jsr330ScopeMetadataResolver。

AnnotationScopeMetadataResolver用来处理Spring的@Scope注解,而Jsr330ScopeMetadataResolver则用来处理Jsr-330规范中提出的@Scope注解。ClassPathBeanDefinitionScanner默认使用的是AnnotationScopeMetadataResolver。

在AnnotationScopeMetadataResolver的resolveScopeMetadata方法中,首先创建ScopeMetadata实例,然后判断传入的BeanDefinition是否是AnnotatedBeanDefinition类型的。这里需要说明下通过Cl-assPathBeanDefinitionScanner扫描的类信息并创建的BeanDefinition都是ScannedGenericBeanDefin-ition类型的,该类型实现了AnnotatedBeanDefinition接口,因此这里的判断成立。

判断成立后首先将BeanDefinition强制转型为AnnotatedBeanDefinition,调用AnnotationConfigUtils的attributesFor方法,传入从注解元数据(AnnotationMetadata)以及@Scope注解的类型,返回AnnotationAttributes对象(以下简称attributes),如果返回的对象不为空,则设置ScopeMetadata的scopeName(通过调用atributes的getString方法),调用attributes的getEnum方法来获取@Scope注解中proxyMode方法的返回值,如果返回的proxyMode等等于ScopeProxyMode的DEFAULT,则将proxyMode重置为ScopedProxyMode.NO(这也是前面讲到的DEFAULT和NO的作用是一样的),将proxyMode设置到metadata中。

最后返回设置好的metadata。

public AnnotationScopeMetadataResolver() {this.defaultProxyMode = ScopedProxyMode.NO;
}
// AnnotationScopeMetadataResolver#resolveScopeMetadata
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;
}

在AnnotationConfigUtils的applyScopedProxyMode方法中,通过传入的ScopeMetadata实例的getScopedProxyMode方法来获取ScopedProxyMode,如果获取到的ScopedProxyMode等于ScopedProxyMode.NO,则直接原样返回。

接下来则是判断获取到的scopedProxyMode是否等于ScopedProxyMode.TARGET_CLASS,并将比较结果赋值给proxyTargetClass,调用ScopedProxyCreator的createScopeProxy方法。

// AnnotationConfigUtils#applyScopedProxyMode
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);
}

在ScopedProxyCreator的createScopedProxy方法中直接委派给ScopedProxyUtils的createdScopedProxy方法实现。

// ScopedProxyCreator#createScopedProxy
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
}

在ScopedProxyUtils的createScopedProxy方法中,调用传入的BeanDefinitionHolder的getBeanName方法获取beanName并赋值给originalBeanName,调用传入的BeanDefinitionHolder的getBeanDefinition方法来获取BeanDefinition并赋值给targetBeanDefinition变量,调用getTargetBeanName方法来处理originalBeanName并赋值给targetBeanName变量(该方法的处理逻辑就是在传入的beanName前拼接上“scopedTarget.”)。

重点是接下来创建的RootBeanDefinition-proxyDefinition,传入的beanClass为ScopedProxyFactoryBean的Class,根据targetBeanDefinition以及targetBeanName来创建BeanDefinitionHolder并设置到proxyDefinition的decoratedDefinition属性中,设置targetDefinition到proxyDefinition的originatingBeanDefinition属性中,获取proxyDefinition的属性元数据(getPropertyValues方法),将其targetBea-nName的属性值设置为targetBeanName。设置…。将targetBeanDefinition的autowireCandidate以及primary设置为false(设置这两个属性的意义在后面会分析到)。

通过调用传入的BeanDefinitionRegistry的registerBeanDefinition方法,来注册targetDefinition,需重点关注的是,在注册targetDefinition时,传递的beanName为targetBeanName(即拼接上“scopedTarget.”前缀的beanName)。

最后创建BeanDefinitionHolder,指定的beanName却为originalBeanName(即未拼接上“scopedTarget.”前缀的beanName)。返回该实例。

// ScopedProxyUtils#createScopedProxy
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,BeanDefinitionRegistry registry, boolean proxyTargetClass) {String originalBeanName = definition.getBeanName();BeanDefinition targetDefinition = definition.getBeanDefinition();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());
}private static final String TARGET_NAME_PREFIX = "scopedTarget.";public static String getTargetBeanName(String originalBeanName) {return TARGET_NAME_PREFIX + originalBeanName;
}

把目光回到调用入口处-ClassPathBeanDefinitionScanner的doScan方法中,在该方法的最后将Annot-ationConfigUtils的applyScopedProxyMode方法返回的BeanDefinitionHolder注册到BeanDefinitionR-egistry中。

这意味着如果某个Bean添加了@Scope注解,并且将proxyMode设置为非DEFAULT、NO时,IoC容器中将会存在该Bean的两个实例,一个名为“scopedTarget.beanName”其对应的是真正的Bean实例,另一个为“beanName”其对应的是ScopedProxyFactoryBean创建出来的目标Bean的代理对象。

BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);

代码验证

使用启动类来进行@Scope的proxyMode属性测试。

@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ScopedBeanDemo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(ScopedBeanDemo.class);context.refresh();String[] beanDefinitionNames = context.getBeanDefinitionNames();Stream.of(beanDefinitionNames).forEach(beanName -> {Class<?> beanType = context.getType(beanName);System.out.printf("beanName : %s -----> beanType:%s \n",beanName,beanType.getName());});}
}

运行结果如下:

可以发现,IoC容器中的确存在ScopedBeanDemo的两个BeanDefinition数据,一个beanName为“scopedTarget.scopedBeanName”,另一个为“scopeBeanDemo”。

相同类型的Bean,谁生效?

如上面的分析结果,IoC容器中存在两个相同类型的Bean,那么当我们通过BeanFactory的getBean(Class)方法来查找时,是会抛出异常呢?还是正常返回呢?如果正常返回,那么该返回那个呢?

下面我们先来编写代码测试下:

import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;import java.beans.Introspector;
import java.lang.reflect.Field;@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ScopedBeanDemo {public static void main(String[] args) throws Exception {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(ScopedBeanDemo.class);context.refresh();// 根据 ScopedBeanDemo 类型来查找ScopedBeanDemo byType = context.getBean(ScopedBeanDemo.class);// 根据 ScopedBeanDemo 在IoC容器中的BeanName来进行查找 -> 其底层也是通过 Java Beans 中的// Introspector#decapitalize方法来生成BeanNameScopedBeanDemo byName =(ScopedBeanDemo) context.getBean(Introspector.decapitalize("ScopedBeanDemo"));// 在 ScopedBeanDemo 在IoC容器中的BeanName 前面拼接上 ScopedProxyUtils#TARGET_NAME_PREFIX 字段的值Field field = ScopedProxyUtils.class.getDeclaredField("TARGET_NAME_PREFIX");field.setAccessible(true);Object value = field.get(null);ScopedBeanDemo byScopedName =(ScopedBeanDemo)context.getBean(value + Introspector.decapitalize("ScopedBeanDemo"));System.out.println("根据ScopedBeanDemo类型查找到的:" + byType.getClass());System.out.println("根据ScopedBeanDemo名称查找到的:" + byName.getClass());System.out.println("根据scopedTarget.ScopedBeanDemo名称查找到的:" + byScopedName.getClass());// 关闭Spring 应用上下文context.close();}
}

运行结果:

可以发现无论是根据类型还是根据beanName来进行IoC容器返回的始终是是代理后的对象。只有按其拼接的规则来拼接beanName后(在beanName前拼接上“scopedTarget.”前缀),再使用BeanFactory的getBean(String)方法来查找才会返回原始对象。

按照beanName来进行查找,IoC容器会返回代理对象,这点可以理解,因为在ScopedProxyUtils的createScopedProxy方法偷梁换柱,将原始的beanName对应的BeanDefinition替换为代理BeanDefinition,所以查找根据原始beanName查找出来的bean为代理Bean就不奇怪了,那么为什么根据类型来查找返回的依然是代理Bean呢?

这里先说下结论:是因为前面在ScopedProxyUtils的createScopedProxy方法中将原始的BeanDefinit-ion(targetDefinition)的autowireCandidate设置为false导致的。

// The target bean should be ignored in favor of the scoped proxy.
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);

下面我们来分析下BeanFactory的getBean(Class)方法。AsbtractApplicationContext实现了该方法,在该方法中首先来对BeanFactory实现类实例的存活状态进行校验。之后就是调用BeanFactory实现类实例的getBean方法,传入要获取的Class。

// AbstractApplicationContext#getBean(java.lang.Class<T>)
public <T> T getBean(Class<T> requiredType) throws BeansException {assertBeanFactoryActive();return getBeanFactory().getBean(requiredType);
}

在DefaultListableBeanFactory实现的getBean方法中,调用resolveBean方法来根据类型获取,如果该方法的返回值为null,抛出NoSuchBeanDefinitionException异常。可以看出的是resolveBean方法并不会主动抛出异常,而是getBean方法抛出的异常,这一点很重要,因为包括BeanFactory提供的安全查找Bean的getBeanProvider方法底层也是基于该方法进行实现,这里就不再展开分析了。

// DefaultListableBeanFactory#getBean(java.lang.Class<T>, java.lang.Object...)
public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {Assert.notNull(requiredType, "Required type must not be null");Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);if (resolved == null) {throw new NoSuchBeanDefinitionException(requiredType);}return (T) resolved;
}

在resolveBean方法中,调用resolveNamedBean方法来进行查找,如果该方法返回值不为null,则直接返回,否则获取当前IoC容器的父容器(如果有),层层查找。

// DefaultListableBeanFactory#resolveBean
private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);if (namedBean != null) {return namedBean.getBeanInstance();}BeanFactory parent = getParentBeanFactory();if (parent instanceof DefaultListableBeanFactory) {return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);}else if (parent != null) {ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);if (args != null) {return parentProvider.getObject(args);}else {return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());}}return null;
}

在resolveNamedBean方法中,首先根据getNamesForType方法来获取指定类型的所有beanName,该方法的返回值是一个数组。结合前面的代码可以得出这里获取到的candidateNames有两个,分别为:scopedTarget.scopedBeanDemo和scopedBeanDemo。

因此会进入第一个判断即candidateNames的长度大于1,遍历candidateNames集合,对于遍历到的每一个beanName,通过containsBeanDefinition方法来判断当前IoC容器中是否包含指定beanName的BeanDefinition数据(注意这里是对结果进行取反,因此判断失败),第二个判断是根据beanName获取到对应的BeanDefinition实例后,然后调用其isAutowireCandidate方法,注意前面我们已经分析过在ScopedProxyUtils的createScopedProxy方法将targetDefinition的autowireCandidate属性设置为false,因此真正的BeanDefinition是不会被作为候选的BeanDefinition,反而是代理BeanDefinition会作为候选的BeanDefinition。

next,判断candidateNames数组的长度是否等等于1,如果判断成立,则调用getBean方法来根据beanName获取,并将方法返回结果构建为NamedBeanHolder返回。

// DefaultListableBeanFactory#resolveNamedBean
private <T> NamedBeanHolder<T> resolveNamedBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {Assert.notNull(requiredType, "Required type must not be null");// getBean(ScopedBeanDemo.class) -> candidateNames 中存在两个beanName,分别为// “scopedTarget.scopedBeanDemo”以及“scopedBeanDemo”。String[] candidateNames = getBeanNamesForType(requiredType);if (candidateNames.length > 1) {List<String> autowireCandidates = new ArrayList<>(candidateNames.length);for (String beanName : candidateNames) {// 由于真正的ScopedBeanDemo的BeanDefinition的autowireCandidate属性被设置为false,// 因此这里被保存到autowireCandidates集合中的是代理Bean的BeanDefinition if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {autowireCandidates.add(beanName);}}if (!autowireCandidates.isEmpty()) {candidateNames = StringUtils.toStringArray(autowireCandidates);}}// 如果candidateNames的长度为1,通过getBean方法来触发初始化或者从缓存中获取并构建为// NamedBeanHolder 对象返回。if (candidateNames.length == 1) {String beanName = candidateNames[0];return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));} else if (candidateNames.length > 1) {Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.length);for (String beanName : candidateNames) {if (containsSingleton(beanName) && args == null) {Object beanInstance = getBean(beanName);candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));} else {candidates.put(beanName, getType(beanName));}}String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());if (candidateName == null) {candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());}if (candidateName != null) {Object beanInstance = candidates.get(candidateName);if (beanInstance == null || beanInstance instanceof Class) {beanInstance = getBean(candidateName, requiredType.toClass(), args);}return new NamedBeanHolder<>(candidateName, (T) beanInstance);}if (!nonUniqueAsNull) {throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());}}return null;
}

总结

@Scope注解中的proxyMode方法值指示了IoC容器要不要为Bean创建代理,如何创建代理,是使用JDK的动态代理还是使用CGLIB?

我们通过源码也了解到ScopedProxyMode的DEFAULT和NO作用是一样的,如果配置为INTERFACES或TARGET_CLASS,在ScopedProxyUtils的createScopedProxy方法中将会为目标Bean创建一个ScopedProxyFactoryBean的BeanDefinition,并使用目标Bean的beanName来注册这个BeanDefinition,将目标Bean的beanName拼接上“SscopedTarget.”前缀来注册目标Bean的BeanDefinition。

同时将目标BeanDefinition的autowireCandidate属性设置为false,以此来确保IoC容器在查找该类型的单个Bean时(getBean方法)不会返回原始Bean实例,而是返回经过代理后的Bean实例。

@Scope注解的proxyMode的作用以及如何影响IoC容器的依赖查找相关推荐

  1. Spring中的scope配置和@scope注解

    Scope,也称作用域,在 Spring IoC 容器是指其创建的 Bean 对象相对于其他 Bean 对象的请求可见范围.在 Spring IoC 容器中具有以下几种作用域:基本作用域(single ...

  2. 【Spring】IOC:基于注解的IOC容器初始化源码分析

    从 Spring2.0 以后的版本中,Spring 也引入了基于注解(Annotation)方式的配置,注解(Annotation)是 JDK1.5 中引入的一个新特性,用于简化 Bean 的配置,可 ...

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

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

  4. @Scope注解设置创建bean的方式和生命周期

    1.1.1            Scope注解创建bean的方式和生命周期 作用 Scope设置对象在spring容器(IOC容器)中的生命周期,也可以理解为对象在spring容器中的创建方式. 取 ...

  5. 1. spring5源码 -- IOC容器设计理念和核心注解的作用

    可以学习到什么? 0. spring整体脉络 1. 描述BeanFactory 2. BeanFactory和ApplicationContext的区别 3. 简述SpringIoC的加载过程 4. ...

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

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

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

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

  8. Spring @Autowired、@Resource、@Required、@Component、@Repository、@Service、@Controller注解的用法和作用...

    Spring @Autowired,@Resource,@Required注解的用法和作用 Spring中 @Autowired标签与 @Resource标签 的区别 Spring注解@Compone ...

  9. Spring @scope注解

    @scope注解使用方式: @scope("prototype") @scope注解可取值: singleton prototype request session global ...

  10. java 代码scope注解_Spring学习(15)--- 基于Java类的配置Bean 之 @Bean @Scope 注解

    默认@Bean是单例的,但可以使用@Scope注解来覆盖此如下: @Configuration public class MyConfiguration { @Bean @Scope("pr ...

最新文章

  1. 用简单术语让你看到贝叶斯优化之美
  2. 旋转角度_办公娱乐新神器!这款稳固的创意支架,360°旋转随便换角度
  3. 把在win7系统下,把笔记本的无线网卡变成路由器,共享上网。
  4. MongoDB基础教程系列--目录结构
  5. 涉密机房建设方案如何规划?
  6. 07_数据库创建,添加c3p0操作所需的jar包,编写c3p0-config.xml文件,编写User.java,编写jdbcUtils.java实现操作数据库的模板工具类,UserDao编写,Dao
  7. Oracle Sql 语法收集.
  8. mc服务器如何开修改物品开挂,如何在我的世界电脑服务器开挂
  9. pytorch切片,numpy切片的总结,以及数组切片常用操作的总结
  10. kerberos 下运行spark 报错 Requested user hdfs is banned
  11. Node.js DNS 模块
  12. Hbuilder开发app实战-识岁06-face++的js实现【完结】
  13. Flex 4 [HostComponent] class xxx not found (AS code)
  14. Atitit java onvif 开源类库 getProfiles getStreamUri
  15. Javascript培训PPT
  16. 网站SEO优化数据分析之跳出率+停留时间
  17. Breeze魔兽编程交流论坛
  18. ERA5气象数据 :数据中相对湿度、边界层高度、温度、风向、地面气压等参数下载详细教程
  19. 2018贵州省大学生程序设计竞赛参赛感言
  20. 直播换脸后,我们来搞搞微信QQ聊天换脸!| avatarify

热门文章

  1. HarmonyOS官网壁纸图片,华为鸿蒙系统HarmonyOS目前收集到的壁纸分享
  2. matlab仿真环境运行,第7章 Simulink仿真环境.ppt
  3. Softmax 回归的从零开始实现 pytorch
  4. 容器技术Docker K8s 25 容器服务ACK基础与进阶-监控管理
  5. 算法:逆序局部链表 Reverse Linked List II
  6. android ble 写失败,Android低功耗蓝牙BLE写入数据很大几率会失败 求解
  7. 2021-09-07218. 天际线问题
  8. python sys.exc_info()详解
  9. 模拟https类型的get,post请求时,碰到证书不信任,无法正常获取返回内容的异常
  10. 文本关键词提取算法总结