Spring中Bean扫描原理详情

目录
  • 前言
  • 环境建设
  • 正式开始
  • configureScanner
    • 第一段代码
    • 第二段代码
    • 第三段代码
    • 第四段代码
    • parseTypeFilters
  • doScan
    • findCandidateComponents
    • For遍历每一个资源
    • isCandidateComponent(MetadataReader metadataReader)
    • 继续后面的逻辑
    • isCandidateComponent(AnnotatedBeanDefintion)
    • 继续后面的逻辑
    • doScan 继续后面的逻辑
    • doScan 最后一个IF
    • checkCandidate方法
    • 继续代码逻辑
  • 总结

前言

在上一章节Spring和Mybatis整合的原理详解中有写到Spring和MyBatis整合时用到的Bean扫描是Spring本身提供的。这一篇文章就写到Spring是如何实现Bean扫描的。
不得不说Bean扫描是一个很重要的技术,在SpringMVC中的Controller扫描,和SpringBoot中的Bean扫描,Component扫描,Configuration扫描,原理我这里猜测都是由这个实现的。

环境建设

由于创建包扫描的条件很简单,只要在Xml中配置一个属性即可。

正式开始

在我前面的文章的阅读基础,我们直接这里节省时间,直接定位到ComponentScanBaeanDefinitionParser类中的parse方法。

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
   String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
   basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
   String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
         ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
   // Actually scan for bean definitions and register them.
   // 实际上,扫描bean定义并注册它们。
   ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
   Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
   registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

   return null;
}

这个代码的前半部分比较简单,就是可能当前传进来的basePackage可能是多个,所以这里使用方法去处理这个字符串。比较重要的代码在下半部分。

也就是三个方法:

  • configureScanner:配置一个扫描器
  • doScan:使用扫描器去扫描
  • registerComponents:注册扫描到的BeanDefintion

configureScanner

第一段代码

boolean useDefaultFilters = true;
if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
   useDefaultFilters = Boolean.parseBoolean(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
}

这一段声明了个变量,默认为True,在下方的If中去会去修改这个值。由于我们在applicatio.xml中没有设置这个属性,这里还是默认值。

第二段代码

// Delegate bean definition registration to scanner class.
// 将 bean 定义注册委托给扫描程序类。
ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());

createScanner方法中,就是new了一个ClassPathBeanDefinitionScanner对象给返回回来了。 随后又为该扫描器加入了两个属性。

第三段代码

if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
   scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
}

这里判断有无配置ResourcePattern属性,有的话设置。

第四段代码

try {
   parseBeanNameGenerator(element, scanner);
}
catch (Exception ex) {
    // ...
}

try {
   parseScope(element, scanner);
}
catch (Exception ex) {
    // ...
}

这两个方法代码跟进去有个共性。都是判断有没有配置一个属性,然后给sanner设置属性,具体看下方代码截图。

这里这两个方法是干嘛的,我心里想了想,不知道,也不知道在什么地方会用到,所以这里接着往下看。

parseTypeFilters

protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) {
   // Parse exclude and include filter elements.
   ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
   NodeList nodeList = element.getChildNodes();
   for (int i = 0; i &lt; nodeList.getLength(); i++) {
      Node node = nodeList.item(i);
      if (node.getNodeType() == Node.ELEMENT_NODE) {
         String localName = parserContext.getDelegate().getLocalName(node);
         try {
            if (INCLUDE_FILTER_ELEMENT.equals(localName)) {
               TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
               scanner.addIncludeFilter(typeFilter);
            }
            else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
               TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
               scanner.addExcludeFilter(typeFilter);
            }
         }
         catch (ClassNotFoundException ex) {
            // ...
         }
         catch (Exception ex) {
            // ...
         }
      }
   }
}

首先看这个方法名parseTypeFilters,转换类型类型过滤器。

通过查看Spring的DTD文件,看到component-scan标签下还有两个子标签,想必就是对应上方的代码中解释了。

随后该方法运行完后,就把创建好的scanner对象,给返回回去了。

doScan

protected Set&lt;BeanDefinitionHolder&gt; doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set&lt;BeanDefinitionHolder&gt; beanDefinitions = new LinkedHashSet&lt;&gt;();
   for (String basePackage : basePackages) {
      Set&lt;BeanDefinition&gt; 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;
}

在第一行代码中,创建了个BeanDefinitions的Set,大概是用来存放结果的。

随后根据basePacage,查找到了所有的候选BeanDefinition,至于获取的方法我在下方有讲到。

随后遍历了刚刚获取到的BeanDefinition。

findCandidateComponents

private Set&lt;BeanDefinition&gt; scanCandidateComponents(String basePackage) {
   Set&lt;BeanDefinition&gt; candidates = new LinkedHashSet&lt;&gt;();
   try {
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
      // ...
      for (Resource resource : resources) {
         // ... 暂时不看
      }
   }
   catch (IOException ex) {
      // ... throw
   }
   return candidates;
}

上面一段代码,方法一进入,创建了一个set集合,这个set机会也就是方法最后的返回值,后续的代码中会向这个set去追加属性。

随后到了packageSearchPath,这里是通过拼接字符串的方式最终得到这个变量,拼接规则如下:

classpath*: + 转换后的xml路径 + **/*.class classpath*:org/springframework/study/**/*.class

随后根据resourceLoader,可以加载上方路径下的所有class文件。

随后进入For遍历环节。

For遍历每一个资源

当前的resource资源也就是读取到的class文件。

for (Resource resource: resources) {
    try {
        MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
        if (isCandidateComponent(metadataReader)) {
           ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
           sbd.setSource(resource);
           if (isCandidateComponent(sbd)) {
              // ... 打印日志
              candidates.add(sbd);
           }
           else {
              // ... 打印日志
           }
        }
        else {
           // ... 打印日志
        }
    }
    catch (FileNotFoundException ex) {
    // ... 打印日志
    }
    catch (Throwable ex) {
    // ... throw
    }
}

进入For后,首先获取metadataReader。这里代码简单追一下。

主要做的是两件事,一个new了一个SimpleMetaDataReader。然后把这个MetaDataReader放入了缓存中。随后返回了这个Reader对象。

然后就进入了第一个比较关键的方法代码,isCandidateComponent方法,仔细一看,这个方法怎么被调用了两次,因为这个if进入后还会调用isCandidateComponent方法,然后我看了看入参,不一致,一个入参事Reader,一个入参事BeanDefinition。我们第一个if中点用的Reader的isCandidateComponent方法。

isCandidateComponent(MetadataReader metadataReader)

protected boolean
isCandidateComponent(MetadataReader metadataReader) throws IOException {
   for (TypeFilter tf : this.excludeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return false;
      }
   }
   for (TypeFilter tf : this.includeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return isConditionMatch(metadataReader);
      }
   }
   return false;
}

上方的excludeFilters排除的我们不用看,主要是看下方的include

后续的代码我就不读了,大概实现我猜测是通过Reader去读到类上的注解,看看有没有当前filter中设置的注解。有的话返回true。

继续后面的逻辑

ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
   if (debugEnabled) {
      logger.debug("Identified candidate component class: " + resource);
   }
   candidates.add(sbd);
}

刚刚外层的If为True后,这里会创建一个ScannedGenericBeanDefinition,既然是BeanDefinition,那就可以被Spring加载。

后面把创建的BeanDefinition放入了isCandidateComponent方法。

isCandidateComponent(AnnotatedBeanDefintion)

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
   AnnotationMetadata metadata = beanDefinition.getMetadata();
   return (metadata.isIndependent() &amp;&amp; (metadata.isConcrete() ||
         (metadata.isAbstract() &amp;&amp; metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

@Override
public boolean isIndependent() {
   // enclosingClassName 为 null
   return (this.enclosingClassName == null || this.independentInnerClass);
}

default boolean isConcrete() {
   return !(isInterface() || isAbstract());
}

到这个方法基本第一个判断就返回了。isIndependent方法中一开始看到其中的两个单词我有点懵,enclosingClass和innerClass,可能是我英文不好的缘故或者基础差吧,百度搜了才知道的。我这里就不讲了,有兴趣你们可以自己搜索一下。自己搜索的记忆更深刻。只要是普通的Component的时候,这里为True。

至于下民的isConcrete方法,就是判断一下当前类是不是接口,或者抽象类。很明显如果是正常的Component,这里是false,随后取反为True。

继续后面的逻辑

if (isCandidateComponent(sbd)) {
   if (debugEnabled) {
      logger.debug("Identified candidate component class: " + resource);
   }
   candidates.add(sbd);
}

当把BeanDefinition传入后返回为True,进入If,也就是添加当前的BeanDefinition进入结果集,返回结果集。

doScan 继续后面的逻辑

Set&lt;BeanDefinition&gt; 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);
   }
}

把刚刚获取到BeanDefinition拿出来遍历.

第一步获取MetaData,这个在刚刚的代码中有写到。随后把他的ScopeName赋值给了MetaData。

接下来有两个if是对这个BeanDefinition设置一些参数的。可以简单扫一眼。捕捉一些关键信息即可。

这个里面设置一个属性,这里记录一下,后面有用到再看。

这个里面是针对类里添加的一些别的注解,来给BeanDefinition添加一些配置。看到几个比较眼熟的,Lazy,Primary,Description这些注解比较眼熟。

doScan 最后一个IF

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

粗略的扫一眼,这里可以看几个重要的地方,一个是进入If的条件,注册BeanDefinition。
至于applyScopedProxyMode方法,因为我没的类上没有加Scope注解,所以这里都是不会配置代理。也就是直接返回当前传入的BeanDefinition。

checkCandidate方法

protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
   if (!this.registry.containsBeanDefinition(beanName)) {
      return true;
   }
   BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
   BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
   if (originatingDef != null) {
      existingDef = originatingDef;
   }
   if (isCompatible(beanDefinition, existingDef)) {
      return false;
   }
   // ... throw Exception.
}

因为是通过Bean扫描进入的,也就是BeanDefinitionRegister当中是没有这个BeanDefinition的。所以这里直接就返回True,不会有走到下面的机会。
这个时候大家可以思考一下,如果走到下面了会怎么样。欢迎评论区讨论。

继续代码逻辑

beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);

接下来就去registerBeanDefinition了,然后还把registry传进入了方法,那很明显了。这里是去注册BeanDefinition了。

总结

由于在这个环节,扫描器把BeanDefinition放进Registry,那么在之后的Refresh方法中的finishBeanFactoryInitialization方法就会把BeanDefinition都实例化完毕。

到此这篇关于Spring中Bean扫描原理详情的文章就介绍到这了,更多相关Spring Bean扫描原理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring自动扫描无法扫描jar包中bean的解决方法

    发现问题 前几天用eclipse打包了一个jar包,jar包里面是定义的Spring的bean. 然后将jar包放到lib下,设置spring的自动扫描这个jar包中的bean,可谁知根本无法扫描到bean,显示错误就是找不到bean,当时就纳闷儿了,为什么扫描不到,结果搜索之后才发现,用eclipse打包jar包要勾选"Add directory entries"才能被Spring正确扫描到,居然有这个说法,呵呵- 不知道 勾选"Add directory entries&

  • Spring整合Mybatis 扫描注解创建Bean报错的解决方案

    目录 Spring整合Mybatis 扫描注解创建Bean报错 springboot+mybatis使用注解方式,出现错误创建dao层bean Spring整合Mybatis 扫描注解创建Bean报错 情景: LZ在整合Spring 和Mybatis 的时候,整合之后部署到tomcat报错 报错信息: Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name

  • 浅谈Spring装配Bean之组件扫描和自动装配

    Spring从两个角度来实现自动化装配: 组件扫描:Spring会自动发现应用上下文中所创建的bean. 自动装配:Spring自动满足bean之间的依赖. 案例:音响系统的组件.首先为CD创建CompactDisc接口及实现类,Spring会发现它并将其创建为一个bean.然后,会创建一个CDPlayer类,让Spring发现它,并将CompactDisc bean注入进来. 创建CompactDisc接口: package soundsystem; public interface Comp

  • springboot 无法扫描到父类模块中Bean的原因及解决

    目录 springboot 无法扫描到父类模块中Bean 现象: 如何解决 解决方案 spring boot 启动就自动关闭 之 找不到bean 解决方法: 原因: 以下收集别的解释: 所以有两种解决办法: springboot 无法扫描到父类模块中Bean 现象: 我定义了两个模块 A 和 B .B模块依赖A模块 A模块中我定义了一个@Component 却发现在B模块中我无法扫描到这个Bean导入注入失败 如何解决 查阅得知,在springboot中的bean扫描是扫描同级目录或者下级目录,

  • Spring IOC容器Bean注解创建对象组件扫描

    目录 Spring IOC Bean注解对象组件扫描 一.spring 针对 bean 管理中创建对象提供注解 1. 引入依赖 2. 开启组件扫描 3. 创建类,并添加注解来创建对象 4. 测试一下 二.组件扫描的其他过滤条件 1. include-filter 2. exclude-filter Spring IOC Bean注解对象组件扫描 什么是注解? 注解是代码里的特殊标记,格式: @注解名称(属性名称=属性值, 属性名称2=属性值...) 可以作用在:类.方法.属性上面. 使用注解的目

  • Spring Bean的包扫描的实现方法

    我们知道,Spring可以通过包扫描将使用@Component注解定义的Bean定义到容器中.今天就来探究下他实现的原理. 首先,找到@Component注解的处理类 注解的定义,一般都需要配套的对注解的处理才能完成注解所代表的功能.所以我们通过@Component注解的用到的地方,来查找可能的处理逻辑; 我们先进入Spring的项目,在IDEA里面用Ctrl和鼠标左键点击Component注解的名称,IDEA会显示出使用到这个类的位置,我们从弹出的列表中找到一个名称像的类,去看类上面的注释说明

  • springboot自动扫描添加的BeanDefinition源码实例详解

    1. springboot启动过程中,首先会收集需要加载的bean的定义,作为BeanDefinition对象,添加到BeanFactory中去. 由于BeanFactory中只有getBean之类获取bean对象的方法,所以将将BeanDefinition添加到BeanFactory中,是通过BeanDefinitionRegistry接口的void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) t

  • springboot bean扫描路径的实现

    1:默认扫描启动类所在路径下所有的bean 2:可以在启动类中添加注解,手动指定扫描路径: @ComponentScan(basePackages = {"com.xxx.service1.*","com.xxx.service2.**"}) 补充:SpringBoot 是如何通过 @SpringBootApplication 扫描项目中的 Bean 原因 首先因为 XXXXXXXApplication 附带 @SpringBootApplication 注解,而

  • Spring中Bean扫描原理详情

    目录 前言 环境建设 正式开始 configureScanner 第一段代码 第二段代码 第三段代码 第四段代码 parseTypeFilters doScan findCandidateComponents For遍历每一个资源 isCandidateComponent(MetadataReader metadataReader) 继续后面的逻辑 isCandidateComponent(AnnotatedBeanDefintion) 继续后面的逻辑 doScan 继续后面的逻辑 doScan

  • java JSP开发之Spring中Bean的使用

    java JSP开发之Spring中Bean的使用 在传统的Java应用中,bean的生命周期很简单.使用Java关键字new进行bean实例化,然后bean就可以被使用了,一旦该bean不再使用,Java就自动进行垃圾回收.然而,在Spring中,bean的生命周期就比较复杂了.下面是一个bean装载到Spring应用上下文的过程: 如图所示:在你准备调用bean之前,bean工厂执行了若干启动步骤: 1.Spring对bean进行实例化: 2.Spring将值和bean的引用注入到bean对

  • Spring中bean的初始化和销毁几种实现方式详解

    Bean的生命周期 : 创建bean对象 – 属性赋值 – 初始化方法调用前的操作 – 初始化方法 – 初始化方法调用后的操作 – --- 销毁前操作 – 销毁方法的调用. [1]init-method和destroy-method 自定义初始化方法和销毁方法两种方式:xml配置和注解. ① xml配置 <bean id="person" class="com.core.Person" scope="singleton" init-meth

  • 详解Spring 中 Bean 的生命周期

    前言 这其实是一道面试题,是我在面试百度的时候被问到的,当时没有答出来(因为自己真的很菜),后来在网上寻找答案,看到也是一头雾水,直到看到了<Spring in action>这本书,书上有对Bean声明周期的大致解释,但是没有代码分析,所以就自己上网寻找资料,一定要把这个Bean生命周期弄明白! ​ 网上大部分都是验证的Bean 在面试问的生命周期,其实查阅JDK还有一个完整的Bean生命周期,这同时也验证了书是具有片面性的,最fresh 的资料还是查阅原始JDK!!! 一.Bean 的完整

  • Spring中Bean的加载与SpringBoot的初始化流程详解

    目录 前言 第一章 Spring中Bean的一些简单概念 1.1 SpingIOC简介 1.2 BeanFactory 1.2.1 BeanDefinition 1.2.2 BeanDefinitionRegistry 1.2.3 BeanFactory结构图 1.3 ApplicationContext 第二章 SpringBoot的初始化流程 2.1 准备阶段 2.2 运行阶段 2.2.1 监听器分析 2.2.2 refreshContext 2.3 总结 前言 一直对它们之间的关系感到好奇

  • Java Spring中Bean的作用域及生命周期

    目录 1.Bean的作用域 1.1 被修改的Bean案例 1.2 为什么使用单例模式作为默认作用域 1.3 作用域 1.4 Bean的6种作用域 1.5 设置作用域 2.Spring执行流程和Bean的生命周期 2.1 Bean的生命周期 2.1.1生命周期演示 2.1.2 为什么要先设置属性,在进行初始化 1.Bean的作用域 1.1 被修改的Bean案例 原因:Bean的作用域默认是单例模式的,也就是说所有⼈的使⽤的都是同⼀个对象!之前我们学单例模式的时候都知道,使⽤单例可以很⼤程度上提⾼性

  • Spring中的aware接口详情

    Spring中有很多继承于aware中的接口,这些接口到底是做什么用到的. aware,翻译过来是知道的,已感知的,意识到的,所以这些接口从字面意思应该是能感知到所有Aware前面的含义. 先举个BeanNameAware的例子,实现BeanNameAware接口,可以让该Bean感知到自身的BeanName(对应Spring容器的BeanId属性)属性,举个例子: BeanNameAware接口的定义: public interface BeanNameAware extends Aware

  • 浅析Spring 中 Bean 的理解与使用

    目录 一.定义 二.控制反转(IoC) 1.什么是依赖注入与控制反转呢?先通过一个例子来理解一下 2.让 Spring 控制类构建过程 3.这就是 IOC 三. @Bean 注解的使用 1.使用说明 2.Bean 名称 2.1.默认情况下 Bean 名称就是方法名(首字母小写),比如下面 Bean 名称便是 myBean 2.2.@Bean 注解支持设置别名.比如下面除了主名称 myBean 外,还有个别名 myBean1(两个都可以使用) 2.3.@Bean 注解可以接受一个 String 数

  • Spring中bean的继承与抽象代码示例

    我们在应用Spring时,在一般的设计时,肯定要用的抽象类.那在Spring中怎么样配置这些抽象Bean呢.请看下面: 如果两个bean 之间的配置信息非常相似,可利用继承来减少重复配置工作. 继承是指子bean 定义可从父bean 定义继承部分配置信息,也可覆盖特定的配置信息,或者添加一些配置.使用继承配置可以节省很多的配置工作.在实际应用中,通用配置会被配置成模板,可供子bean 继承. 使用abstract 属性 正如前面所介绍的,通用的配置会被配置成模板,而模板不需要实例化,仅仅作为子b

  • 浅谈Spring中Bean的作用域、生命周期

    本文主要探究的是关于Bean的作用域.生命周期的相关内容,具体如下. Bean的作用域 Spring 3中为Bean定义了5中作用域,分别为singleton(单例).prototype(原型).request.session和global session,5种作用域说明如下: 1.singleton:单例模式,Spring IoC容器中只会存在一个共享的Bean实例,无论有多少个Bean引用它,始终指向同一对象.Singleton作用域是Spring中的缺省作用域,也可以显示的将Bean定义为

随机推荐