SPRing context命名空間有property-placeholder、property-override、annotation-config、component-scan、load-time-weaver、spring-configured、mbean-export和mbean-server 8個標簽。
property-placeholder和property-override標簽的解析見property-placeholder和property-override標簽的解析,annotation-config標簽的解析見解析context命名空間之annotation-config標簽。這一節來探討component-scan標簽的解析與用途。
component-scan標簽擁有同annotation-config標簽一樣的作用,但它比annotation-config標簽更強大。annotation-config標簽主要作用是注冊后處理來對已創建的BeanDefintion對象和實例化的bean做加工,component-scan標簽作用不僅于此,還可以把特定包下被指定注解類標注的類對象封裝成BeanDefinition對象并注冊到BeanDefinitionRegistry對象中,這樣大大簡化了xml文件的內容。
component-scan標簽的解析器類為ComponentScanBeanDefinitionParser類,它直接實現了BeanDefinitionParser接口,下面是它實現的parse方法的源代碼。
@Override public BeanDefinition parse(Element element, ParserContext parserContext) { String basePackage = element.getAttribute("base-package"); // 處理base-package屬性值中被“${”、“}”包圍的變量 // 比如,base-package="${package}",如果上下文環境對象中有一個Properties對象的key為package // ->那么,base-package的值就為這個key對應的value值 basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); // 如果有多個根包需要掃描,那么以“,; /t/n”中的一個隔開 String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,",; /t/n"); // Actually scan for bean definitions and register them. // 創建ClassPathBeanDefinitionScanner對象,這個對象用于掃描base-package指定的包下面的類對象 // ->并把匹配的類對象使用BeanDefinition對象封裝,然后注冊到BeanDefinitionRegistry中 ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); // 注冊ComponentDefinition和注解配置處理器(和annotation-config標簽的一樣) registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }ComponentScanBeanDefinitionParser的parser方法,首先創建并初始化ClassPathBeanDefinitionScanner對象;然后使用前面scanner對象掃描指定包下匹配的類對象并注冊相應BeanDefinition對象;最后調用registerComponents方法,如果屬性annotation-config的值為true,則同annotation-config標簽一樣會注冊處理注解配置的各種后處理器。我們先看看最后一步中的registerComponents方法,后面我們在來詳細探討spring如何掃描和匹配包下的類對象。
protected void registerComponents( XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) { Object source = readerContext.extractSource(element); CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source); for (BeanDefinitionHolder beanDefHolder : beanDefinitions) { compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder)); } // Register annotation config processors, if necessary. boolean annotationConfig = true; if (element.hasAttribute("annotation-config")) { annotationConfig = Boolean.valueOf(element.getAttribute("annotation-config")); } if (annotationConfig) { Set<BeanDefinitionHolder> processorDefinitions = AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source); for (BeanDefinitionHolder processorDefinition : processorDefinitions) { compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition)); } } readerContext.fireComponentRegistered(compositeDef); }registerComponents方法代碼很簡單,它主要是拿去component-scan標簽的annotation-config屬性值,這個屬性值默認為true的,如果annotation-config值為true,則注冊注解配置的各個處理器,這個注冊過程在上一節——“解析annotation-config標簽”中已經探討了,這里就不用再說了。
這一節我們詳細討論spring如何使用component-scan標簽來掃描和匹配包下的類對象。
(1)創建并初始化ClassPathBeanDefinitionScanner對象 ComponentScanBeanDefinitionParser的parser方法調用configureScanner(ParserContext parserContext, Element element)方法來創建初始化并返回一個ClassPathBeanDefinitionScanner對象,這個方法的源碼如下。
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) { // 設置是否自動檢測被@Component、@Repository、@Service或者@Controller注解標注的類 // 默認為true。如果為false,那么上面4個注解將沒有作用,因此一般都不會設置這個屬性 boolean useDefaultFilters = true; if (element.hasAttribute("use-default-filters")) { useDefaultFilters = Boolean.valueOf(element.getAttribute("use-default-filters")); } // 把注冊BeanDefinition的任務委托給scanner ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters); scanner.setResourceLoader(parserContext.getReaderContext().getResourceLoader()); scanner.setEnvironment(parserContext.getReaderContext().getEnvironment()); // 讓scanner持有component-scan標簽的父節點<beans>的屬性默認值 scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults()); scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns()); // 設置資源匹配模式,默認為**/*.class if (element.hasAttribute("resource-pattern")) { scanner.setResourcePattern(element.getAttribute("resource-pattern")); } try { // 設置Bean名稱生成器 parseBeanNameGenerator(element, scanner); } catch (Exception ex) { parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause()); } try { // 設置作用域 parseScope(element, scanner); } catch (Exception ex) { parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause()); } // 設置過濾器,即用于指定哪些類需要被處理,哪些類需要被忽略 parseTypeFilters(element, scanner, parserContext); return scanner; }configureScanner方法有4步,第一步是調用createScanner方法來創建一個ClassPathBeanDefinitionScanner 對象,并使用ParserContext對象來初始化它;第二步是調用parseBeanNameGenerator方法;第三步是調用parseScope方法,第四步是調用parseTypeFilters方法。下面我們分別介紹這4個方法。
1)調用createScanner方法創建ClassPathBeanDefinitionScanner 對象,代碼如下。
/** * 創建并返回ClassPathBeanDefinitionScanner對象 **/ protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) { return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters); }2)調用parseBeanNameGenerator方法設置Bean名稱生成器
protected void parseBeanNameGenerator(Element element, ClassPathBeanDefinitionScanner scanner) { if (element.hasAttribute("name-generator")) { // 生成一個BeanNameGenerator對象 BeanNameGenerator beanNameGenerator = (BeanNameGenerator) instantiateUserDefinedStrategy( element.getAttribute("name-generator"), BeanNameGenerator.class, scanner.getResourceLoader().getClassLoader()); scanner.setBeanNameGenerator(beanNameGenerator); } }parseBeanNameGenerator為scanner對象設置一個BeanNameGenerator接口對象。spring提供了兩個這樣的對象,其一是AnnotationBeanNameGenerator,它被用于獲取被@Component等注解的類對象的bean名稱。其二是DefaultBeanNameGenerator,它被用于獲取一般bean的名稱。
生成BeanNameGenerator對象調用了ClassPathBeanDefinitionScanner類的私有方法instantiateUserDefinedStrategy,這個方法用于實例化一個指定類型的對象,源碼如下。
/** * 根據指定的class名稱實例化一個對象,這個對象必須為指定的strategyType類型。 **/ private Object instantiateUserDefinedStrategy(String className, Class<?> strategyType, ClassLoader classLoader) { Object result; try { result = classLoader.loadClass(className).newInstance(); } catch (ClassNotFoundException ex) { throw new IllegalArgumentException("Class [" + className + "] for strategy [" + strategyType.getName() + "] not found", ex); } catch (Exception ex) { throw new IllegalArgumentException("Unable to instantiate class [" + className + "] for strategy [" + strategyType.getName() + "]: a zero-argument constructor is required", ex); } if (!strategyType.isAssignableFrom(result.getClass())) { throw new IllegalArgumentException("Provided class name must be an implementation of " + strategyType); } return result; }3)調用parseScope方法設置作用域解析器或者作用域的默認代理模式
protected void parseScope(Element element, ClassPathBeanDefinitionScanner scanner) { // 如果scope-resolver有值,則注冊ScopeMetadataResolver if (element.hasAttribute("scope-resolver")) { if (element.hasAttribute("scoped-proxy")) { throw new IllegalArgumentException( "Cannot define both 'scope-resolver' and 'scoped-proxy' on <component-scan> tag"); } ScopeMetadataResolver scopeMetadataResolver = (ScopeMetadataResolver) instantiateUserDefinedStrategy( element.getAttribute("scope-resolver"), ScopeMetadataResolver.class, scanner.getResourceLoader().getClassLoader()); scanner.setScopeMetadataResolver(scopeMetadataResolver); } // 設置作用域的默認代理模式ScopedProxyMode if (element.hasAttribute("scoped-proxy")) { String mode = element.getAttribute("scoped-proxy"); if ("targetClass".equals(mode)) { scanner.setScopedProxyMode(ScopedProxyMode.TARGET_CLASS); } else if ("interfaces".equals(mode)) { scanner.setScopedProxyMode(ScopedProxyMode.INTERFACES); } else if ("no".equals(mode)) { scanner.setScopedProxyMode(ScopedProxyMode.NO); } else { throw new IllegalArgumentException("scoped-proxy only supports 'no', 'interfaces' and 'targetClass'"); } } }注:scope-resolver和scoped-proxy不能同時定義。
4)調用parseTypeFilters方法設置類型過濾器 parseTypeFilters方法主要是用來component-scan標簽的include-filter和exclude-filter子節點。include-filter標簽用來表示必須包含的類,不管這個類是否被默認的注解類標注。exclude-filter標簽用來表示掃描時必須忽略的類,即使這個類被默認的注解標注了。但include-filter和exclude-filter兩個元素不能共存。
protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) { // 解析exclude和include過濾器元素 ClassLoader classLoader = scanner.getResourceLoader().getClassLoader(); NodeList nodeList = element.getChildNodes(); // 遍歷component-scan標簽下的子節點 for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { String localName = parserContext.getDelegate().getLocalName(node); try { if ("include-filter".equals(localName)) { // 解析include元素 TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext); scanner.addIncludeFilter(typeFilter); } else if ("exclude-filter".equals(localName)) { 解析exclude元素 TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext); scanner.addExcludeFilter(typeFilter); } } catch (Exception ex) { parserContext.getReaderContext().error( ex.getMessage(), parserContext.extractSource(element), ex.getCause()); } } } }parseTypeFilters方法首先判斷區分出include-filter和exclude-filter標簽,然后調用createTypeFilter方法來解析他們并返回TypeFilter,scanner把他們的指代的TypeFilter對象保存到它的includeFilters和excludeFilters列表中。
/** * 解析component-scan標簽的include-filter和exclude-filter子節點,并返回TypeFilter對象 **/ protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader, ParserContext parserContext) { String filterType = element.getAttribute("type"); String expression = element.getAttribute("expression"); expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression); try { if ("annotation".equals(filterType)) { // 指定過濾的注解。expression=類全名稱 return new AnnotationTypeFilter((Class<Annotation>) classLoader.loadClass(expression)); } else if ("assignable".equals(filterType)) { // 指定過濾的類,它的子類或者實現類也包括。expression=類全名稱 return new AssignableTypeFilter(classLoader.loadClass(expression)); } else if ("aspectj".equals(filterType)) { // 指定aspectj表達式來過濾類。expression=aspectj表達式字符串 return new AspectJTypeFilter(expression, classLoader); } else if ("regex".equals(filterType)) { // 通過指定的正則表達式來過濾類。expression=正則表達式字符串 return new RegexPatternTypeFilter(Pattern.compile(expression)); } else if ("custom".equals(filterType)) { // 用戶自定義過濾器類型 Class<?> filterClass = classLoader.loadClass(expression); if (!TypeFilter.class.isAssignableFrom(filterClass)) { throw new IllegalArgumentException( "Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression); } return (TypeFilter) BeanUtils.instantiateClass(filterClass); } else { throw new IllegalArgumentException("Unsupported filter type: " + filterType); } } catch (ClassNotFoundException ex) { throw new FatalBeanException("Type filter class not found: " + expression, ex); } }(2)使用ClassPathBeanDefinitionScanner對象的doScan(String… basePackages)方法開始掃描包,這個方法的源碼如下。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>(); // 遍歷指定的包 for (String basePackage : basePackages) { // 獲取包下的所有候選BeanDefinition對象 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); // 遍歷所有候選BeanDefinition對象 for (BeanDefinition candidate : candidates) { // 獲取一個ScopeMetadata對象,默認為AnnotationScopeMetadataResolver // 如果目標類未被@Scope注解,則返回一個默認的ScopeMetadata ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); // 使用bean名稱生成器生成bean名稱,默認生成器為AnnotationBeanNameGenerator // 首先是以注解的value為bean名稱,如果注解的value沒有值,則使用默認的名稱 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { // 進一步處理BeanDefinition對象,比如,bean是否可以被用于自動注入的備選bean postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { // 處理定義在目標類上的注解,包括@Lazy, @Primary, @DependsOn, @Role, @Description AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // 檢查beanName是否已經存在BeanDefinitionRegistry中存在 if (checkCandidate(beanName, candidate)) { // beanName還沒被使用過 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); // 如果有必要,則創建作用域代理 // 如果創建了代理,則返回表示代理對象的BeanDefinitionHolder definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // 注冊BeanDefinitionHolder對象 registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }關于doScan方法所調用的方法,我只探討其中的三個,其一是ClassPathBeanDefinitionScanner的findCandidateComponents方法;其二是AnnotationConfigUtils的processCommonDefinitionAnnotations靜態方法;其三是AnnotationConfigUtils的applyScopedProxyMode靜態方法。
1) findCandidateComponents方法繼承自ClassPathScanningCandidateComponentProvider類,是用于查找指定包及其子包下所有匹配的類,并返回這些類對應的BeanDefinition對象。它的源碼如下。
public Set<BeanDefinition> findCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>(); try { // 聲明有CLASSPATH_ALL_URL_PREFIX="classpath*:" // resourcePattern默認為"**/*.class" String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + "/" + this.resourcePattern; // 根據模式匹配來搜索包下面匹配的所有類 // 默認的ResourcePatternResolver實現類為PathMatchingResourcePatternResolver類 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 { // 默認的MetadataReaderFactory實現類為CachingMetadataReaderFactory // 默認返回的是SimpleMetadataReader對象 MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); // 通過過濾器來判斷類對象是否為候選類 if (isCandidateComponent(metadataReader)) { 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; }findCandidateComponents方法首先獲取指定包及其子包下所有類的資源對象,然后使用CachingMetadataReaderFactory對象獲取一個SimpleMetadataReader的MetadataReader對象,SimpleMetadataReader使用ClassReader對象從Resource對象持有的類文件輸入流中讀取類信息,ClassReader允許通過ClassVisitor對象來訪問它持有的類信息。而SimpleMetadataReader使用AnnotationMetadataReadingVisitor訪問器把ClassReader存有的類信息分解成類元數據ClassMetadata對象和注解元數據AnnotationMetadata對象。 因此findCandidateComponents方法才可以調用isCandidateComponent(MetadataReader metadataReader)方法來通過過濾器判斷當前類是否會被排除在候選類之外,下面是isCandidateComponent方法的源碼。
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { // 首先使用excludeFilters中的過濾器檢查當前類是否應該排除 // 通過exclude-filter標簽配置 for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { return false; } } // 最后使用includeFilters來查看當前類是否候選類 // 通過include-filter標簽配置。 // 默認為注解類型的過濾器,注解類為spring定義的@Component for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { return isConditionMatch(metadataReader); } } return false; }如果當前類是候選類,那么為當前類生成一個ScannedGenericBeanDefinition對象,ScannedGenericBeanDefinition繼承自GenericBeanDefinition并實現了AnnotatedBeanDefinition接口。 如果類不是一個具體且獨立的類,比如抽象類、接口或者非靜態內部類,通過反射實例化一個對象的時候是會拋出異常的。因此spring還會判斷一次,在findCandidateComponents方法中調用isCandidateComponent(AnnotatedBeanDefinition beanDefinition)方法,它的源碼如下。
/** * 判斷類是否是一個具體類,并且是一個可以獨立實例化的類。 **/ protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent()); }通過以上判斷排除,最終獲得一個ScannedGenericBeanDefinition對象?,F在我們第2點ClassPathBeanDefinitionScanner類的 doScan(String… basePackages)方法拿到這個對象后通過調用AnnotationConfigUtils工具類processCommonDefinitionAnnotations靜態方法還做了些什么。
2) AnnotationConfigUtils的processCommonDefinitionAnnotations靜態方法的源碼如下。
public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) { processCommonDefinitionAnnotations(abd, abd.getMetadata()); }processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd)方法把解析類上的注解任務轉交給它的另一個重載方法processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata),這是一個只有包訪問權限的靜態方法,源碼如下。
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { // 解析@Lazy注解,設置是否延遲加載 if (metadata.isAnnotated(Lazy.class.getName())) { abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value")); } else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) { abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean("value")); } // 解析@Primary注解 if (metadata.isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } // 解析@DependOn注解 if (metadata.isAnnotated(DependsOn.class.getName())) { abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value")); } if (abd instanceof AbstractBeanDefinition) { AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd; // 解析@Role注解 if (metadata.isAnnotated(Role.class.getName())) { absBd.setRole(attributesFor(metadata, Role.class).getNumber("value").intValue()); } // 解析@Description注解 if (metadata.isAnnotated(Description.class.getName())) { absBd.setDescription(attributesFor(metadata, Description.class).getString("value")); } } }3) doScan(String… basePackages)方法還調用AnnotationConfigUtils工具類的applyScopedProxyMode靜態方法,這也是一個只有包訪問權限的方法,它主要是處理標注在類上的@Scope注解,源碼如下。
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); // 創建一個scope代理 return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass); }ScopedProxyCreator類的createScopedProxy靜態方法源碼如下
public static BeanDefinitionHolder createScopedProxy( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) { // 調用ScopedProxyUtils工具類的createScopedProxy方法 return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass); }工具類ScopedProxyUtils的createScopedProxy靜態方法源碼如下。
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition, BeanDefinitionRegistry registry, boolean proxyTargetClass) { String originalBeanName = definition.getBeanName(); BeanDefinition targetDefinition = definition.getBeanDefinition(); // targetBeanName格式為scopedTarget. + originalBeanName String targetBeanName = getTargetBeanName(originalBeanName); // Create a scoped proxy definition for the original bean name, // "hiding" the target bean in an internal target definition. // 創建一個ScopedProxyFactoryBean類對應BeanDefinition對象 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); } else { // 設置為根據接口做做代理 proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE); } // 根據代理目標BeanDefinition設置是否可以為自動注入的候選bean proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate()); proxyDefinition.setPrimary(targetDefinition.isPrimary()); if (targetDefinition instanceof AbstractBeanDefinition) { proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition); } // 隱藏被代理的bean targetDefinition.setAutowireCandidate(false); targetDefinition.setPrimary(false); // 注冊被代理的bean的BeanDefinition對象 registry.registerBeanDefinition(targetBeanName, targetDefinition); // 返回代理bean的BeanDefinitionHolder對象 return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases()); }(1)component-scan不只是擁有annotation-config的作用,它還用于掃描注冊指定包下特定的bean。因此,在xml配置文件中定義了component-scan就不需要再定義annotation-config。
(2)component-scan大大簡化了我們的xml配置。如果我們需要把一個對象托管給Spring容器,只需要在類上添加@Component注解或者被@Component標注的注解(比如,@Controller、@Service、@Repository)
(3)component-scan的兩個子標簽include-filter和exclude-filter不能共存。這個并不是從spring的java源碼中體現的,而是在schema文件中體現的。(spring為什么這樣設計,我實在是不懂的了)
新聞熱點
疑難解答