当前位置: 首页 > Java > 正文

SpringMVC context:component-scan实现原理

关键字:
1 星2 星3 星4 星5 星 (暂无评分)
Loading ... Loading ...
baidu_share

SpringMVC项目中,一般使用注解和配置xml来使用bean.

先来看看常用注解。@Controller标注web控制器,@Service标注Service层的服务,@Respository标注DAO层的数据访问。@Component是通用标注,只是定义为一个类为Bean,SpringMVC会把所有添加@Component注解的类作为使用自动扫描注入配置路径下的备选对象。@Controller、@Service、@Respository只是更加的细化,都是被@Component标注,所以我们比较不推荐使用@Component。

SpringMVC启动时怎么被自动扫描然后解析并注册到Bean工厂中去(放到DefaultListableBeanFactory中的Map beanDefinitionMap中 以BeanName为key)?

1.首先来看看@Component源码:

@Target({ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
public @interface Service {  
    String value() default "";  
}  
 
@Target({ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
public @interface Controller {  
    String value() default "";  
}  
 
@Target({ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
public @interface Repository {  
    String value() default "";  
}  
 
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {
 
	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any
	 */
	String value() default "";
 
}

都标示有@Component。
我们在配置文件中,标示配置需要扫描哪些包下,也可以配置对某个包下不扫描,代码如下:

<context:component-scan base-package="com.qq">  
        <context:exclude-filter type="regex" expression="com.qq.*.*.service"/>  
        <context:include-filter type="regex" expression="com.qq.*.*.dao"/>  
</context:component-scan>

如果配置了那么标签就可以不用再xml中配置了,因为前者包含了后者。有一个use-default-filters属性,改属性默认为true,这就意味着会扫描指定包下的全部的标有@Component的类,并注册成bean.也就是@Component的子注解@Service,@Reposity等。
Use-dafault-filters=”false”的情况下:指定的不扫描,指定的扫描

SpringMVC先读取配置文件,然后根据context:component-scan中属性base-package去扫描指定包下的class和jar文件,把标示@Controller标注web控制器,@Service标注Servicec层的服务,@Respository标注DAO层的数据访问等注解的都获取,并注册为Bean类放到Bean工厂。

spring内部是如何实现的呢?

DefaultBeanDefinitionDocumentReader类doRegisterBeanDefinitions这个方法实现解析配置文件的Bean,这边已经读取进来形成Document 形式存储,然后开始解析Bean,是由BeanDefinitionParserDelegate类实现的,BeanDefinitionParserDelegate完成具体Bean的解析(例如:bean标签、import标签等)

注解属于扩展的标签,是由NamespaceHandler和BeanDefinitionParser来解析。源代码如下:

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {  
        String namespaceUri = getNamespaceURI(ele);  
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);  
        if (handler == null) {  
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);  
            return null;  
        }  
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));  
    }

NamespaceHandler这边这边起到了什么作用,根据不同的Namespace获取不同的NamespaceHandler,因为我们在Beans标签配置了命名空间,然后就可以配置对应的标签,解析标签时,比较有自己的所实现的NamespaceHandler来解析,如图所示:
spring-context

NamespaceHandler中的parse方法是它的子类类NamespaceHandlerSupport实现的,获取通过findParserForElement方法获取BeanDefinitionParser 对象,这个对象在工程初始化时就直接实例化放在缓存中Map,然后通过localName获取,源代码如下:

public BeanDefinition parse(Element element, ParserContext parserContext) {  
    return findParserForElement(element, parserContext).parse(element, parserContext);  
}  
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {  
    String localName = parserContext.getDelegate().getLocalName(element);  
    BeanDefinitionParser parser = this.parsers.get(localName);  
    if (parser == null) {  
        parserContext.getReaderContext().fatal(  
                "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);  
    }  
    return parser;  
}

为什么要获取BeanDefinitionParser ,因为BeanDefinitionParser 类是解析配置文件中的,等标签,但是不同的标签是由不同的BeanDefinitionParser来进行解析的,如图所示:
spring-component

标签的解析是由ComponentScanBeanDefinitionParser类解析的,接下来我们要分析它怎么解析注解的Bean,并把Bean注册到Bean工厂,源代码如下:

public BeanDefinition parse(Element element, ParserContext parserContext) {  
       //获取context:component-scan 配置的属性base-package的值  
    String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),  
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);  
       //创建扫描对应包下的class文件的对象  
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);  
       //扫描对应包下的class文件并有注解的Bean包装成BeanDefinition  
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);  
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);  
    return null;  
}

说明:
(1)获取context:component-scan 配置的属性base-package的值,然后放到数组。
(2)创建扫描对应包下的class和jar文件的对象ClassPathBeanDefinitionScanner ,由这个类来实现扫描包下的class和jar文件并把注解的Bean包装成BeanDefinition。
(3)BeanDefinition注册到Bean工厂。

1:扫描是由ComponentScanBeanDefinitionParser的doScan方法来实现的,源代码如下:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {  
       //新建队列来保存BeanDefinitionHolder  
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();  
       //循环需要扫描的包  
    for (String basePackage : basePackages) {  
           //进行扫描注解并包装成BeanDefinition  
        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);  
                   //对BeanDefinition进行注册  
                registerBeanDefinition(definitionHolder, this.registry);  
            }  
        }  
    }  
    return beanDefinitions;  
}

进行扫描注解并包装成BeanDefinition是ComponentScanBeanDefinitionParser由父类ClassPathScanningCandidateComponentProvider的方法findCandidateComponents实现的,源代码如下:

public Set<BeanDefinition> findCandidateComponents(String basePackage) {  
    Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();  
    try {  
          //base-package中的值替换为classpath*:cn/test/**/*.class  
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +  
                resolveBasePackage(basePackage) + "/" + this.resourcePattern;  
           //获取所以base-package下的资源  
        Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);  
           boolean traceEnabled = logger.isTraceEnabled();  
        boolean debugEnabled = logger.isDebugEnabled();  
        for (Resource resource : resources) {  
            if (traceEnabled) {  
                logger.trace("Scanning " + resource);  
            }  
            if (resource.isReadable()) {  
                try {  
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);  
                      if (isCandidateComponent(metadataReader)) {  
                          //包装BeanDefinition  
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);  
                        sbd.setResource(resource);  
                        sbd.setSource(resource);  
                        if (isCandidateComponent(sbd)) {  
                            if (debugEnabled) {  
                                logger.debug("Identified candidate component class: " + resource);  
                            }  
                            candidates.add(sbd);  
                        }  
                        else {  
                            if (debugEnabled) {  
                                logger.debug("Ignored because not a concrete top-level class: " + resource);  
                            }  
                        }  
                    }  
                    else {  
                        if (traceEnabled) {  
                            logger.trace("Ignored because not matching any filter: " + resource);  
                        }  
                    }  
                }  
                catch (Throwable ex) {  
                    throw new BeanDefinitionStoreException(  
                            "Failed to read candidate component class: " + resource, ex);  
                }  
            }  
            else {  
                if (traceEnabled) {  
                    logger.trace("Ignored because not readable: " + resource);  
                }  
            }  
        }  
    }  
    catch (IOException ex) {  
        throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);  
    }  
      return candidates;  
}

Bean注册到Bean工厂源码

protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {  
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);  
}  
 
 public static void registerBeanDefinition(  
        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)  
        throws BeanDefinitionStoreException {  
 
    // Register bean definition under primary name.  
    String beanName = definitionHolder.getBeanName();  
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());  
 
    // Register aliases for bean name, if any.  
    String[] aliases = definitionHolder.getAliases();  
    if (aliases != null) {  
        for (String aliase : aliases) {  
            registry.registerAlias(beanName, aliase);  
        }  
    }  
}  
 
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)  
        throws BeanDefinitionStoreException {  
 
    Assert.hasText(beanName, "Bean name must not be empty");  
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");  
 
    if (beanDefinition instanceof AbstractBeanDefinition) {  
        try {  
            ((AbstractBeanDefinition) beanDefinition).validate();  
        }  
        catch (BeanDefinitionValidationException ex) {  
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,  
                    "Validation of bean definition failed", ex);  
        }  
    }  
 
    synchronized (this.beanDefinitionMap) {  
        Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);  
        if (oldBeanDefinition != null) {  
            if (!this.allowBeanDefinitionOverriding) {  
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,  
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +  
                        "': There is already [" + oldBeanDefinition + "] bound.");  
            }  
            else {  
                if (this.logger.isInfoEnabled()) {  
                    this.logger.info("Overriding bean definition for bean '" + beanName +  
                            "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");  
                }  
            }  
        }  
        else {  
            this.beanDefinitionNames.add(beanName);  
            this.frozenBeanDefinitionNames = null;  
        }  
        this.beanDefinitionMap.put(beanName, beanDefinition);  
    }  
 
    resetBeanDefinition(beanName);  
}

本文固定链接: http://www.chepoo.com/springmvc-context-component-scan.html | IT技术精华网

SpringMVC context:component-scan实现原理:等您坐沙发呢!

发表评论