简介
在之前的文章里,我们讨论了spring IOC容器的大致结构和构建bean对象关系网的流程。在那里,bean对象的加载和解析主要是通过纯xml文件解析实现的。但是从spring 2.0以后,对于bean对象的构建和关联可以通过annotation来实现。那么,这一部分的实现细节是怎么样的呢?这就是本文要探讨的重点。
应用示例
还是老样子,我们先看一个示例,根据这个示例的结果再来讨论引出的问题。我们创建一个普通的工程,它的依赖如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.yunzero</groupId> <artifactId>componentscansample</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>componentscansample</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java-version>1.8</java-version> <log4j.version>2.10.0</log4j.version> <spring.version>5.0.7.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>${log4j.version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>${log4j.version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>${project.artifactId}-${project.version}</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>${java-version}</source> <target>${java-version}</target> </configuration> </plugin> </plugins> </build> </project>
主要就是添加了对spring的依赖。里面定义有类CustomDAO如下:
package com.yunzero.dao; import org.springframework.stereotype.Component; @Component public class CustomerDAO { @Override public String toString() { return "Hello , This is CustomerDAO"; } }
这部分代码里我们在类的定义前面加了一个Component annotation。然后,我们再定义一个类CustomerService:
package com.yunzero.services; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.yunzero.dao.CustomerDAO; @Component public class CustomerService { @Autowired private CustomerDAO customerDAO; @Override public String toString() { return "CustomerService [customerDAO=" + customerDAO + "]"; } }
这部分代码里稍微特殊一点的是在于它除了在类定义前添加了Component annotation,里面对成员变量customerDAO也增加了一个Autowired annotation。
现在,我们再添加一个xml配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.yunzero"/> </beans>
它的配置可以说是相当的简单,里面就是说明了一个component-scan的属性。
现在,我们在主程序里添加如下内容:
package com.yunzero; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.yunzero.services.CustomerService; public class App { public static void main( String[] args ) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); CustomerService cust = context.getBean(CustomerService.class); System.out.print(cust); } }
如果运行程序,将的到如下的输出结果:
CustomerService [customerDAO=Hello , This is CustomerDAO]
和前面一篇文章里介绍的内容不同的地方在于,这里没有在xml文件里定义任何bean。程序里居然可以找到和加载bean对象。为什么会这样呢?在前面的讨论里,我们已经看到了一些不一样的地方了。比如xml文件里component-scan的设置;@Autowired, @Component这些annotation的应用。好像施加了魔法一样,spring居然可以找到需要构建的bean对象。从表面上看来,ComponnentScan指定了base-package信息,那么spring可能通过它在这个指定的范围内查找bean对象的构造信息。而@Component则相当于告诉spring,这就是一个可选的bean对象。而@Autowired则起到一个告诉spring组装bean对象的效果。那么,我们就按照这个猜想往下走。
ComponentScan支持
既然前面猜想到是spring框架识别一些annotation然后做了一些操作。那么,我们就先从spring框架中去寻找一些蛛丝马迹。在前面讨论spring IOC框架基本结构的文章里,我们简单的列了一下它读取配置文件,解析和构造bean关系图的过程。 那里讲到读取和解析文件的流程时,有这么一个顺序图:
这里有一个很关键的类BeanDefinitionDocumentReader,它有一个实现是DefaultBeanDefinitionDocumentReader,里面的registerBeanDefinitions方法就是问题的要点。我们这里先简单的列一下这部分代码:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); }
这部分代码将真正解析的内容代理到方法doRegisterBeanDefinitions里:
protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
上述的代码主要是用来解析xml文件里<beans>里面的内容。相当于在一个文件里定义的一个个bean对象了。详细的实现在parseBeanDefinitions里:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
在之前的文章里我们对这段代码的一个分支进行了分析。里面的parseDefaultElement方法就开始解析里面具体的<bean>结构对象了。但是这里还有另外一个分支,就是delegate.parseCustomElement。在判断出当前节点的默认元素是否属于当前默认的namespace之后,它会调用这个方法。因为在程序里,默认的是beans的namespace,它的定义是:
http://www.springframework.org/schema/beans
而前面的示例里我们可以看到,像component-scan,它的前面是有context修饰的,说明它的默认namespace是在context里,context namespace的定义是:
http://www.springframework.org/schema/context
我们再跟进去看看parseCustomElement方法的实现:
@Nullable public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); } @Nullable public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } 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的对象了。而这个具体的parse方法是在NamespaceHandler的一个子类里实现的,也就是NamespaceHandlerSupport:
public BeanDefinition parse(Element element, ParserContext parserContext) { BeanDefinitionParser parser = findParserForElement(element, parserContext); return (parser != null ? parser.parse(element, parserContext) : null); } @Nullable 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; }
绕了半天,原来这里最终是通过一个从它本地的一个HashMap类型的parsers里获取到的一个parser来处理的。那么,这些parser是怎么注册到里面去的呢?我们怎么找到合适的parser呢?这部分先搁置一下,我们后面会讨论。
从代码里看到的,它返回的是一个BeanDefinitionParser的接口,在它的实际实现里有一个类ComponentScanBeanDefinitionParser。它的实现里就是对这部分的具体解析了:
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. ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }
这方法里前面部分是解析里面的"base-package"部分,然后得到一组设定的包名,它是一个数组。然后通过configureScanner方法来设置一个scanner对象,再由它来进行进一步的扫描工作。所以,最终的的扫描包的职责就由这个scanner对象来做了。我们先来看看configureScanner方法:
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) { boolean useDefaultFilters = true; if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) { useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)); } // Delegate bean definition registration to scanner class. ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters); scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults()); scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns()); if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) { scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE)); } try { 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; }
这段代码里,前面的部分只是创建一个ClassPathBeanDefinitionScanner。然后的parseBeanNameGenerator方法解析文件里“name-generator”的配置项,如果有的话,则配置相应的BeanNameGenerator。后面的parseScope方法则是解析配置文件中有“scope-resolver”,"scoped-proxy"等项时的配置解析工作。后面的parseTypeFilters方法则是解析文件里如果含有"include-filter", "exclude-filter"等配置项时的工作。看这部分的时候,可能会有点迷糊,为什么要解析这么多有的没的?因为这些都是@ComponentScan里的配置项,我们只要看看这个annotation里的定义就明白了。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan { @AliasFor("basePackages") String[] value() default {}; @AliasFor("value") String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class; ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN; boolean useDefaultFilters() default true; Filter[] includeFilters() default {}; Filter[] excludeFilters() default {}; boolean lazyInit() default false; @Retention(RetentionPolicy.RUNTIME) @Target({}) @interface Filter { FilterType type() default FilterType.ANNOTATION; @AliasFor("classes") Class<?>[] value() default {}; @AliasFor("value") Class<?>[] classes() default {}; String[] pattern() default {}; } }
从ComponentScan annotation的定义里就可以看到,它包含有"base-packages", "nameGenerator", "scopedProxy"等属性,所以前面的那些代码就是对它们的解析。
总的来说,这部分代码中规中矩,没什么特殊的。我们接着看看scanner.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) { 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); registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
这部分代码里,主要的逻辑就是通过findCandidateComponents方法来找到候选的组件。这个方法的实现细节我们会接着跟进。第6行的for循环里,针对每个BeanDefinition进行解析处理,比如获取它的scope信息,设置它的scope信息和生成beanName等信息。
我们接着来看findCandidateComponents方法,它定义在类ClassPathScanningCandidateComponentProvider里:
public Set<BeanDefinition> findCandidateComponents(String basePackage) { if (this.componentsIndex != null && indexSupportsIncludeFilters()) { return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { return scanCandidateComponents(basePackage); } }
在这个方法里,第一个判断条件是如果componentsIndex不为空而且indexSupportsIncludeFilters时,走addCandidateComponentsForIndex方法的流程,否则走scanCandidateComponents方法的流程。其中addCandidateComponentsForIndex的流程实现如下:
private Set<BeanDefinition> addCandidateComponentsFromIndex(CandidateComponentsIndex index, String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { Set<String> types = new HashSet<>(); for (TypeFilter filter : this.includeFilters) { String stereotype = extractStereotype(filter); if (stereotype == null) { throw new IllegalArgumentException("Failed to extract stereotype from "+ filter); } types.addAll(index.getCandidateTypes(basePackage, stereotype)); } boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); for (String type : types) { MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(type); if (isCandidateComponent(metadataReader)) { AnnotatedGenericBeanDefinition sbd = new AnnotatedGenericBeanDefinition( metadataReader.getAnnotationMetadata()); if (isCandidateComponent(sbd)) { if (debugEnabled) { logger.debug("Using candidate component class from index: " + type); } candidates.add(sbd); } else { if (debugEnabled) { logger.debug("Ignored because not a concrete top-level class: " + type); } } } else { if (traceEnabled) { logger.trace("Ignored because matching an exclude filter: " + type); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; }
上述代码的第5到第10行主要是通过CandidateComponentsIndex的getCandidateTypes方法来获取备选的类型信息。这里获取到的是备选的类型字符串信息。其中getCandidateTypes方法的实现如下:
public Set<String> getCandidateTypes(String basePackage, String stereotype) { List<Entry> candidates = this.index.get(stereotype); if (candidates != null) { return candidates.parallelStream() .filter(t -> t.match(basePackage)) .map(t -> t.type) .collect(Collectors.toSet()); } return Collections.emptySet(); }
这个方法里通过index这个Map<K, List<V>>的数据类型获取到给定类型原型信息的一组Entry。然后根据它的包名是否匹配来返回符合的类型集合。
我们接着前面addCandidateComponentsFromIndex方法来看。第15行开始的for循环里主要做的事就是通过给定的type String信息,找到对应的MetadataReader,并用它来尝试解析获取到的元数据信息。里面getMetadataReader方法会根据传入的type String去找到它对应于classpath的resource信息。这里的resource信息正好和我们之前文章里讨论的Resource接口对应上了。在它的底层实现里,它使用了ClassLoader来尝试加载设定好的类。然后,再通过metadataReader.getAnnotationMetadata来获取到对应的BeanDefinition信息。如果这个beanDefinition对象是候选的component,则加入到candidates集合里。
另外一种扫描component的路径则在前面提到的方法scanCandidateComponents里:
private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; Resource[] resources = getResourcePatternResolver().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 = getMetadataReaderFactory().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; }
这部分代码算是系统实现里面比较核心的部分。我们仔细的看过来。其中第4行的代码用来拼凑包搜索的路径,它将包名中的.符号替换成/符号。这样相当于构成了一个路径的结构。这样,假如我们要扫描一个com.abc这样的包,它将被组装成classpath*:com/abc/**/*.class这样的一个字符串。第6行的代码则是解析这个路径来获得对应的Resource。这个获取对应Resource的过程在类PathMatchingResourcePatternResolver的getResources方法里:
public Resource[] getResources(String locationPattern) throws IOException { Assert.notNull(locationPattern, "Location pattern must not be null"); if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { // a class path resource (multiple resources for same name possible) if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) { // a class path resource pattern return findPathMatchingResources(locationPattern); } else { // all class path resources with the given name return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length())); } } else { // Generally only look for a pattern after a prefix here, // and on Tomcat only after the "*/" separator for its "war:" protocol. int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(':') + 1); if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { // a file pattern return findPathMatchingResources(locationPattern); } else { // a single resource with the given name return new Resource[] {getResourceLoader().getResource(locationPattern)}; } } }
这部分代码里,首先会判断传入的路径字符串是否以classpath*:开头,如果是的,则走第5行判断后面的字符串是否包含有*或者?这些通配符。如果包含这些的话,表示这里是匹配的多个resource,接着会调用findPathMatchingResources这个方法。否则会调用findAllClassPathResources这个方法。这两个方法我们接着会详细看。在第14行的else条件里,表示传入的路径并没有包含classpath*:的开头。在后面也是判断,在给定的前缀字符串war:协议或者*/之后是否包含有多个resource,如果是的话,调用findPathMatchingResources这个方法。否则表示指定的是一个单一的resource,可以直接初始化它。
我们先来看看findPathMatchingResources的实现:
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException { String rootDirPath = determineRootDir(locationPattern); String subPattern = locationPattern.substring(rootDirPath.length()); Resource[] rootDirResources = getResources(rootDirPath); Set<Resource> result = new LinkedHashSet<>(16); for (Resource rootDirResource : rootDirResources) { rootDirResource = resolveRootDirResource(rootDirResource); URL rootDirUrl = rootDirResource.getURL(); if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) { URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl); if (resolvedUrl != null) { rootDirUrl = resolvedUrl; } rootDirResource = new UrlResource(rootDirUrl); } if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher())); } else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) { result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern)); } else { result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern)); } } if (logger.isDebugEnabled()) { logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result); } return result.toArray(new Resource[0]); }
这一段代码看起来有一个比较容易让人困惑的地方,就是这里递归的调用了getResources方法。在determineRootDir方法中会截取最后一个'/'符号前的串作为resource的根目录。结合前面getResources方法的细节来看,它第4行的代码里会判断截取了"classpath*:"之后的字符串是否还有*,?等通配符。如果没有的话,则会进入到findAllClassPathResources方法里了。而里面如果还有这些通配符才会继续这个方法进行进一步的解析。第6行开始的代码则是在得到了所有的Resource之后解析各种协议下的Resource,比如bundle, jar, vfs以及普通文件。最后返回实现Resource接口的特定Resource对象。比如说基于文件的Resource, 它就是通过扫描给定的path,然后将符合条件的文件构造成FileSystemResource对象。其他协议的也类似。
现在,我们接着来看findAllClassPathResources。前面递归的形式里最终还是要走到这个方法里来:
protected Resource[] findAllClassPathResources(String location) throws IOException { String path = location; if (path.startsWith("/")) { path = path.substring(1); } Set<Resource> result = doFindAllClassPathResources(path); if (logger.isDebugEnabled()) { logger.debug("Resolved classpath location [" + location + "] to resources " + result); } return result.toArray(new Resource[0]); }
这部分代码主要还是委托doFinadAllClassPathResources方法来做。
protected Set<Resource> doFindAllClassPathResources(String path) throws IOException { Set<Resource> result = new LinkedHashSet<>(16); ClassLoader cl = getClassLoader(); Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path)); while (resourceUrls.hasMoreElements()) { URL url = resourceUrls.nextElement(); result.add(convertClassLoaderURL(url)); } if ("".equals(path)) { // The above result is likely to be incomplete, i.e. only containing file system references. // We need to have pointers to each of the jar files on the classpath as well... addAllClassLoaderJarRoots(cl, result); } return result; }
很显然,这里其实是通过classloader来加载所有的resource。只是这里的resource是URL的类型,然后需要转换成Resource对象。在这里,第9行的方法则是用来处理如果传入的路径为空字符串,则说明这个传入的文件可能在系统classpath所在的某个jar包里。这里就需要根据addAllClassLoaderJarRoots方法来进一步解析jar包的内容了。显然,到这一步的时候,大部分加载resource的流程就走完了。概括起来说就是最终通过classloader找到了这些指定base-package的资源并加载进来。
上述过程中牵涉到的类关系如下图:
从图中比较容易看到它们更多的是一种依赖的关系。而且,在前面的讨论里已经看到,它本身的实现细节就是通过代理关系来实现的。在知道牵涉到的类和对象之后,我们再针对前面的调用过程稍微总结一下。它的整体顺序图如下:
从最开始的DefaultBeanDefinitionDocumentReader,到BeanDefinitionParserDelegate一直到最后的ClassLoader。它们基本上就是这么一路代理的过程下来的。在跟踪代码的过程中会显得很痛苦。
其他annotation的解析流程
AnnotationConfigApplicationContext
除了我们上述讨论的一个流程中对@ComponentScan的支持,在spring本身对annotation的支持上,我们也有一些专门的类来做相关的工作。这个就是AnnotationConfigApplicationContext。它本身的逻辑并不复杂,在理解了前面的流程之后,相对就好很多了。
在它本身的定义里有两个成员变量:
private final AnnotatedBeanDefinitionReader reader; private final ClassPathBeanDefinitionScanner scanner;
在它的功能里,提供了两种基于annotation查找bean的方法。一种方法的定义如下:
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { this(); register(annotatedClasses); refresh(); }
这种方式是通过提供一些特定的class,然后通过register方法来注册bean。这里的关键点在于register方法,在通过这个方法注册完bean之后,再调用refresh方法来执行其他的操作。而在前面的文章里我们已经知道,refresh方法是整个流程的最关键点。
还有另外一种方法是scan,这个和前面的@ComponentScan类似,我们指定basePackages,然后去扫描指定目录下的class。它所在的构造函数方法如下:
public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); refresh(); }
我们接着深入看看register方法和scan方法的实现。
public void register(Class<?>... annotatedClasses) { Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified"); this.reader.register(annotatedClasses); } public void register(Class<?>... annotatedClasses) { for (Class<?> annotatedClass : annotatedClasses) { registerBean(annotatedClass); } }
在register方法里,它的详细实现细节是通过AnnotatedBeanDefinitionReader实现的。里面真正关键的实现在方法registerBean里:
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } abd.setInstanceSupplier(instanceSupplier); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); if (qualifiers != null) { for (Class<? extends Annotation> qualifier : qualifiers) { if (Primary.class == qualifier) { abd.setPrimary(true); } else if (Lazy.class == qualifier) { abd.setLazyInit(true); } else { abd.addQualifier(new AutowireCandidateQualifier(qualifier)); } } } for (BeanDefinitionCustomizer customizer : definitionCustomizers) { customizer.customize(abd); } BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }
上述代码第10行主要解析AnnotatedGenericBeanDefinition里的scope metadata。因为是通过Annotation修饰的bean对象,我们可能在Annotation里指定了它的scope信息,所以这部分就是做这个的。
第12行代码主要通过beanGenerator来生成一个唯一的名字,保证名字不会有冲突。
第14行代码的processCommonDefinitionAnnotations方法主要处理BeanDefinition里定义的Lazy, Primary, DependsOn, Description等元数据信息的。这些和xml文件定义的内容相似,无非就是解析的方式有点不一样。
第15到第27行主要是用来处理传入的qualifier参数的。这些传入的qualiier都是一些Annotation class。
第28到30行则是用来处理传入的definitionCustomizers参数。如果有一些自定义的对beanDefinition的处理,可以在这里传入它的实现。
剩下的代码则是通过BeanDefinitionReaderUtils里的registerBeanDefinition方法来将解析后的BeanDefinitionHolder注册到registry里。这样,在后续使用的时候,我们可以直接访问到这些bean了。这里针对registerBeanDefinition再多说一句。我们通过BeanDefinitionRegistry来注册的BeanDefinition最终是保存在一个类型为Map<String, BeanDefinition>的map里。倒也没什么特别稀奇的东西。
现在我们再来看看scan方法的实现。既然这里就是指定basePackages,然后去查找里面的candidates,有了之前的分析,我们可以猜想,他们的最终实现应该是一样的。scan方法的实现如下,在ClassPathBeanDefinitionScanner里:
public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); doScan(basePackages); // Register annotation config processors, if necessary. if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); }
上面代码里的doScan方法正好就是我们前面讨论分析过的流程里的一部分。这样,这部分其实就合并到前面的流程里了。基本上没什么区别。最终还是通过classloader来加载resource。还有一个值得分析一下的就是后面第7行里的registerAnnotationConfigProcessors:
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(4); if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor. if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); } return beanDefs; }
这部分的代码看起来比较长,他其实主要做的事情就是注册各种postProcessor。里面四个主要的就是CommonAnnotationBeanPostProcessor, RequiredAnnotationBeanPostProcessor, AutowiredAnnotationBeanPostProcessor, ConfigurationClassPostProcessor。通过这几个processor的实现分析,我们前面扫描之后的到的resource是怎么识别成bean对象以及是怎么组合在一起的就很明白了。我们针对它们每个实现都详细了解一下。
CommonAnnotationBeanPostProcessor
这个beanPostProcessor主要用来处理一些比较常用的annotation。像@PostConstructor, @PreDestroy, @Resource还有web service相关的@WebServiceRef, @EJB这些的支持实现都在这里。它相关的类结构图如下:
它的初始化和销毁对象的定义继承自InitDestroyAnnotationBeanPostProcessor。它的构造函数和初始化对象的实现如下:
//WebService关于JAX-WS的相关注解 private static Class<? extends Annotation> webServiceRefClass = null; //EJB相关的注解 private static Class<? extends Annotation> ejbRefClass = null; //静态初始化块 static { //获取当前类的类加载器 ClassLoader cl = CommonAnnotationBeanPostProcessor.class.getClassLoader(); try { //使用类加载器加载WebService相关的类 webServiceRefClass = (Class) cl.loadClass("javax.xml.ws.WebServiceRef"); } catch (ClassNotFoundException ex) { webServiceRefClass = null; } try { //使用类加载器加载EJB相关的类 ejbRefClass = (Class) cl.loadClass("javax.ejb.EJB"); } catch (ClassNotFoundException ex) { ejbRefClass = null; } } //构造方法 public CommonAnnotationBeanPostProcessor() { setOrder(Ordered.LOWEST_PRECEDENCE - 3); //设置初始的注解类型为@PostConstruct setInitAnnotationType(PostConstruct.class); //设置消耗的注解为@ PreDestroy setDestroyAnnotationType(PreDestroy.class); //当使用@Resource注解时,忽略JAX-WS的资源类型 ignoreResourceType("javax.xml.ws.WebServiceContext"); }
这里比较重要的代码是对属性的处理部分,它的详细实现如下:
@Override public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex); } return pvs; } private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) { // Fall back to class name as cache key, for backwards compatibility with custom callers. String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // Quick check on the concurrent map first, with minimal locking. InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata != null) { metadata.clear(pvs); } metadata = buildResourceMetadata(clazz); this.injectionMetadataCache.put(cacheKey, metadata); } } } return metadata; } private InjectionMetadata buildResourceMetadata(final Class<?> clazz) { LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>(); Class<?> targetClass = clazz; do { final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>(); ReflectionUtils.doWithLocalFields(targetClass, field -> { if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) { if (Modifier.isStatic(field.getModifiers())) { throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields"); } currElements.add(new WebServiceRefElement(field, field, null)); } else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) { if (Modifier.isStatic(field.getModifiers())) { throw new IllegalStateException("@EJB annotation is not supported on static fields"); } currElements.add(new EjbRefElement(field, field, null)); } else if (field.isAnnotationPresent(Resource.class)) { if (Modifier.isStatic(field.getModifiers())) { throw new IllegalStateException("@Resource annotation is not supported on static fields"); } if (!ignoredResourceTypes.contains(field.getType().getName())) { currElements.add(new ResourceElement(field, field, null)); } } }); ReflectionUtils.doWithLocalMethods(targetClass, method -> { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods"); } if (method.getParameterCount() != 1) { throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method); } PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new WebServiceRefElement(method, bridgedMethod, pd)); } else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException("@EJB annotation is not supported on static methods"); } if (method.getParameterCount() != 1) { throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method); } PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new EjbRefElement(method, bridgedMethod, pd)); } else if (bridgedMethod.isAnnotationPresent(Resource.class)) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException("@Resource annotation is not supported on static methods"); } Class<?>[] paramTypes = method.getParameterTypes(); if (paramTypes.length != 1) { throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method); } if (!ignoredResourceTypes.contains(paramTypes[0].getName())) { PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new ResourceElement(method, bridgedMethod, pd)); } } } }); elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clazz, elements); }
上述这个过程看起来比较长,主要是通过findResourceMetadata和buildResourceMetadata来查找对应的元数据。它的处理过程和AutowiredAnnotationBeanPostProcessor的过程基本一样。除了查找属性值的过程不一样。AutowiredAnnotationBeanPostProcessor主要通过解析里面的required配置来获取依赖的属性值,这里则通过解析@Resource属性来获取相关信息。
在findResourceMetadata方法里,首先通过查找injectionMetadataCache来查找对应的属性是否在缓存里,如果有就直接返回了。否则针对这个缓存加同步块,并重新构建针对给定key的metadata。
这些方法里,重点就是buildResourceMetadata。它主要由两个doWithLocalFields方法组成,第一个方法判断当前的field是webServiceRefClass, ejbRefClass或者Resource class。如果是上述的某一种,就构造对应类型的Element对象。这里有WebServiceRefElement, EjbRefElement, ResourceElement等几种,它们都是最终继承的InjectionMetadata.InjectedElement。接着的第二个doWithLocalFields方法主要针对方法来查找对应的注解。它也是类似的查找当前的方法是否被webServiceRefClass, ejbRefClass或者Resource class修饰,如果是的话,则构建对应的Element对象。最后将这些数据收集起来构造InjectionMetadata对象。上述代码中有一个细节就是对于方法上面的注解,EJB和WebService相关注解以及@Resource只能在单个参数的方法上配置,否则会有异常抛出。
代码里还有一个比较重要的部分,就是根据给定名称或者类型获取资源对象。它的实现在getResource方法里。它的实现被WebService, Ejb, Resource三种Element的实现给调用。详细的实现如下:
protected Object getResource(LookupElement element, @Nullable String requestingBeanName) throws BeansException { if (StringUtils.hasLength(element.mappedName)) { return this.jndiFactory.getBean(element.mappedName, element.lookupType); } if (this.alwaysUseJndiLookup) { return this.jndiFactory.getBean(element.name, element.lookupType); } if (this.resourceFactory == null) { throw new NoSuchBeanDefinitionException(element.lookupType, "No resource factory configured - specify the 'resourceFactory' property"); } return autowireResource(this.resourceFactory, element, requestingBeanName); } protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName) throws BeansException { Object resource; Set<String> autowiredBeanNames; String name = element.name; if (this.fallbackToDefaultTypeMatch && element.isDefaultName && factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) { autowiredBeanNames = new LinkedHashSet<>(); resource = ((AutowireCapableBeanFactory) factory).resolveDependency( element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null); if (resource == null) { throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object"); } } else { resource = factory.getBean(name, element.lookupType); autowiredBeanNames = Collections.singleton(name); } if (factory instanceof ConfigurableBeanFactory) { ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory; for (String autowiredBeanName : autowiredBeanNames) { if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) { beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName); } } } return resource; }
在getResource方法里,它会首先去判断当前的element对象,如果有映射的名字,会尝试用jndiFactory来获取bean对象,否则尝试resourceFactory。使用resourceFactory的话,会通过autowireResource方法来最终获取到需要的bean对象。
autowireResource的实现相对比较直接,首先判断如果是通过类型匹配来查找bean对象的话,它通过对应的beanFactory来解析给定的依赖关系,将依赖直接返回。如果不是则通过factory来查找这个对象。对于特定的ConfigurableBeanFactory,还需要注册依赖的bean。
RequiredAnnotationBeanPostProcessor
public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { //如果容器缓存中没有指定Bean名称 if (!this.validatedBeanNames.contains(beanName)) { //如果指定Bean定义中没有设置skipRequiredCheck属性 if (!shouldSkip(this.beanFactory, beanName)) { List<String> invalidProperties = new ArrayList<>(); for (PropertyDescriptor pd : pds) { //如果属性添加了@Required注解,且属性集合中不包含指定名称的属性 if (isRequiredProperty(pd) && !pvs.contains(pd.getName())) { //当前属性为无效的属性 invalidProperties.add(pd.getName()); } } if (!invalidProperties.isEmpty()) { throw new BeanInitializationException(buildExceptionMessage(invalidProperties, beanName)); } } //将Bean名称缓存到容器中 this.validatedBeanNames.add(beanName); } //返回经过验证的属性值 return pvs; } protected boolean isRequiredProperty(PropertyDescriptor propertyDescriptor) { //获取给定属性的写方法(setter方法) Method setter = propertyDescriptor.getWriteMethod(); //检查给定属性方法上是否存在指定类型的注解 return (setter != null && AnnotationUtils.getAnnotation(setter, getRequiredAnnotationType()) != null); } protected boolean shouldSkip(@Nullable ConfigurableListableBeanFactory beanFactory, String beanName) { if (beanFactory == null || !beanFactory.containsBeanDefinition(beanName)) { return false; } BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); if (beanDefinition.getFactoryBeanName() != null) { return true; } Object value = beanDefinition.getAttribute(SKIP_REQUIRED_CHECK_ATTRIBUTE); return (value != null && (Boolean.TRUE.equals(value) || Boolean.valueOf(value.toString()))); }
AutowiredAnnotationBeanPostProcessor
这个postProcessor就是用来处理@Autowired, @Value, @Inject这几个主要的annotation的。其中@Inject是JSR-330里的标准annotation。在看它的详细实现之前,我们可以看一下它要处理的@Autowired的详细定义:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { boolean required() default true; }
在这个详细的定义里,我们可以看到它可以被用到构造函数、方法、传递的参数,声明的字段以及annotation上。所以,在实现的时候,我们可以猜想一下,这里会有相关的方法针对这几个方面进行处理。
AutowiredAnnotationBeanPostProcessor相关的类结构图如下:
在这个图里可以看到,它实现了BeanPostProcessor, Ordered, Aware等几个接口。而里面最重要的就是BeanPostProcessor里的方法。正是因为通过这里的方法,它可以对前面定义的bean对象做一些进一步的处理。我们现在对里面的一些实现细节了解一下。
它本身的构造函数如下:
public AutowiredAnnotationBeanPostProcessor() { this.autowiredAnnotationTypes.add(Autowired.class); this.autowiredAnnotationTypes.add(Value.class); try { this.autowiredAnnotationTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader())); logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }
这部分代码一开始就对它需要处理的annotation type做一个整理。首先,Autowired, Value两个class是必须要处理的。而如果能找到javax.inject.Inject 类的话,它也要被加进来进行处理,否则就忽略它。
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeanCreationException { // Let's check for lookup methods here.. if (!this.lookupMethodsChecked.contains(beanName)) { try { ReflectionUtils.doWithMethods(beanClass, method -> { Lookup lookup = method.getAnnotation(Lookup.class); if (lookup != null) { Assert.state(beanFactory != null, "No BeanFactory available"); LookupOverride override = new LookupOverride(method, lookup.value()); try { RootBeanDefinition mbd = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName); mbd.getMethodOverrides().addOverride(override); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(beanName, "Cannot apply @Lookup to beans without corresponding bean definition"); } } }); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Lookup method resolution failed", ex); } this.lookupMethodsChecked.add(beanName); } // Quick check on the concurrent map first, with minimal locking. Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass); if (candidateConstructors == null) { // Fully synchronized resolution now... synchronized (this.candidateConstructorsCache) { candidateConstructors = this.candidateConstructorsCache.get(beanClass); if (candidateConstructors == null) { Constructor<?>[] rawCandidates; try { rawCandidates = beanClass.getDeclaredConstructors(); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Resolution of declared constructors on bean Class [" + beanClass.getName() + "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex); } List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length); Constructor<?> requiredConstructor = null; Constructor<?> defaultConstructor = null; Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass); int nonSyntheticConstructors = 0; for (Constructor<?> candidate : rawCandidates) { if (!candidate.isSynthetic()) { nonSyntheticConstructors++; } else if (primaryConstructor != null) { continue; } AnnotationAttributes ann = findAutowiredAnnotation(candidate); if (ann == null) { Class<?> userClass = ClassUtils.getUserClass(beanClass); if (userClass != beanClass) { try { Constructor<?> superCtor = userClass.getDeclaredConstructor(candidate.getParameterTypes()); ann = findAutowiredAnnotation(superCtor); } catch (NoSuchMethodException ex) { // Simply proceed, no equivalent superclass constructor found... } } } if (ann != null) { if (requiredConstructor != null) { throw new BeanCreationException(beanName, "Invalid autowire-marked constructor: " + candidate + ". Found constructor with 'required' Autowired annotation already: " + requiredConstructor); } boolean required = determineRequiredStatus(ann); if (required) { if (!candidates.isEmpty()) { throw new BeanCreationException(beanName, "Invalid autowire-marked constructors: " + candidates + ". Found constructor with 'required' Autowired annotation: " + candidate); } requiredConstructor = candidate; } candidates.add(candidate); } else if (candidate.getParameterCount() == 0) { defaultConstructor = candidate; } } if (!candidates.isEmpty()) { // Add default constructor to list of optional constructors, as fallback. if (requiredConstructor == null) { if (defaultConstructor != null) { candidates.add(defaultConstructor); } else if (candidates.size() == 1 && logger.isWarnEnabled()) { logger.warn("Inconsistent constructor declaration on bean with name '" + beanName + "': single autowire-marked constructor flagged as optional - " + "this constructor is effectively required since there is no " + "default constructor to fall back to: " + candidates.get(0)); } } candidateConstructors = candidates.toArray(new Constructor<?>[0]); } else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) { candidateConstructors = new Constructor<?>[] {rawCandidates[0]}; } else if (nonSyntheticConstructors == 2 && primaryConstructor != null && defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) { candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor}; } else if (nonSyntheticConstructors == 1 && primaryConstructor != null) { candidateConstructors = new Constructor<?>[] {primaryConstructor}; } else { candidateConstructors = new Constructor<?>[0]; } this.candidateConstructorsCache.put(beanClass, candidateConstructors); } } } return (candidateConstructors.length > 0 ? candidateConstructors : null); }
在前面的第5到第28行里,主要是看里面有没有Lookup的annotation。如果有这个的话,通过LookupOverride对象来设置对应的BeanDefinition。
第31行的代码检查candidateConstructorsCache里是否有给定beanClass的constructor。如果没有,则先设置线程同步保证cache里的数据一致性,然后通过第38行的beanClass.getDeclaredConstructors来获取声明的构造函数。
在得到所有的这些constructorCandidate之后,第50行遍历这个列表。遍历这个列表的目的主要是第57行的findAutowiredAnnotation方法。通过它来确认是否添加了Required属性。其中findAutowiredAnnotation方法根据给定的AccessibleObject来获取它被Autowired修饰后的元素。它的详细实现我们会在后面分析。
接着的第58到第70行主要是处理当无法通过当前的class得到它的构造函数的话,尝试去获取它的父类的构造函数。
第71行到89行用于判断它是否添加了required属性。针对各种错误情况进行处理。后面的第90行里的条件则是针对如果没有任何参数的话,就设定当前的这个函数作为默认的构造函数。
我们接着看看findAutowiredAnnotation方法的详细实现:
private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) { if (ao.getAnnotations().length > 0) { for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) { AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type); if (attributes != null) { return attributes; } } } return null; }
在之前的方法里,我们是针对constructor进行分析。在java里,constructor, field等都是继承的AccessibleObject。表示它们都是可以被访问的对象。我们可以针对某些方面将他们一视同仁。它的流程相对比较简单点,主要是通过遍历现有的annotation,并和给定的constructor里的annotation进行merge处理,然后如果得到一个非空的属性则返回。
前面这部分的代码是针对构造函数有Autowired annotation时是怎么处理的分析。它在整个bean框架中被调用的顺序如下图:
从上图中可以看到,其实在前面构造bean对象的时候,这部分的方法会被调用处理。所以这里相当于前面处理解析各种文件和构造bean对象过程中某一个处理的点。接着的这部分代码是对方法和属性的依赖注入:
public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException { InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; } public void processInjection(Object bean) throws BeanCreationException { Class<?> clazz = bean.getClass(); InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz, null); try { metadata.inject(bean, null, null); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException( "Injection of autowired dependencies failed for class [" + clazz + "]", ex); } }
这两个方法是被调用的比较多的。他们主要的流程都是通过findAutowiringMetadata来获取到注入的元数据信息。然后再通过metadata的inject方法来注入指定的bean对象、类和相关的属性。所以关键点在于这两个方法。我们先来看findAutowiringMetadata方法:
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) { // Fall back to class name as cache key, for backwards compatibility with custom callers. String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // Quick check on the concurrent map first, with minimal locking. InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata != null) { metadata.clear(pvs); } metadata = buildAutowiringMetadata(clazz); this.injectionMetadataCache.put(cacheKey, metadata); } } } return metadata; }
这个方法里前面通过injectionMetadataCache来查找给定的beanName或className。如果没找到,则通过buildAutowiringMetadata方法来获取metadata。获取到之后再将这部分metadata放到前面的cache里。我们再看buildAutowiringMetadata方法:
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>(); Class<?> targetClass = clazz; do { final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>(); ReflectionUtils.doWithLocalFields(targetClass, field -> { AnnotationAttributes ann = findAutowiredAnnotation(field); if (ann != null) { if (Modifier.isStatic(field.getModifiers())) { if (logger.isWarnEnabled()) { logger.warn("Autowired annotation is not supported on static fields: " + field); } return; } boolean required = determineRequiredStatus(ann); currElements.add(new AutowiredFieldElement(field, required)); } }); ReflectionUtils.doWithLocalMethods(targetClass, method -> { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { if (logger.isWarnEnabled()) { logger.warn("Autowired annotation is not supported on static methods: " + method); } return; } if (method.getParameterCount() == 0) { if (logger.isWarnEnabled()) { logger.warn("Autowired annotation should only be used on methods with parameters: " + method); } } boolean required = determineRequiredStatus(ann); PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new AutowiredMethodElement(method, required, pd)); } }); elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clazz, elements); }
这个方法看起来比较复杂,不过仔细整理下来,也并不是想象的那么困难。首先是在第2行里建立一个包含所有annotation元素的集合elements。在第5行开始的循环里,第8行到第20行是用来处理本地的成员变量。通过前面讨论的findAutowiredAnnotation方法找到被autowired annotation修饰的字段。然后判断它是否为static以及required的属性是否设置了。然后将找到的字段加入到当前的列表中。
从第22行到第45行的代码里,则是针对本地方法的。它找到原来的方法之后,然后针对包含有Autowired annotation修饰的属性的方法做进一步的处理。然后判断required属性看是否为必须的。对于必须的部分再通过findPropertyForMethod来找到对应的属性描述部分。最后再构建一个对应的元素。经过这样的一通查找后,将所有合格的结果加入到返回的列表中。
当然,在这个方法中,它会不断的从当前类到当前类的父类去重复上述的过程。第48行就是来改变当前类的。
前面方法里还有一个依赖的实现方法findAutowiredAnnotation:
private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) { if (ao.getAnnotations().length > 0) { for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) { AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type); if (attributes != null) { return attributes; } } } return null; }
如果留意到前面构造函数里对autowiredAnnotationTypes进行初始化的部分,我们就会发现。前面就是对这个列表添加了Autowired.class, Value.class, Inject.class这三种类型的annotation。然后在这个循环里来尝试提取这些annotation属性。如果有的话,则将它们返回回来。
在这里,针对字段的注入处理有专门的一个类AutowiredFieldElement,它继承了InjectionMetadata.InjectedElement。它对具体字段的注入实现在inject方法里:
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Field field = (Field) this.member; Object value; if (this.cached) { value = resolvedCachedArgument(beanName, this.cachedFieldValue); } else { DependencyDescriptor desc = new DependencyDescriptor(field, this.required); desc.setContainingClass(bean.getClass()); Set<String> autowiredBeanNames = new LinkedHashSet<>(1); Assert.state(beanFactory != null, "No BeanFactory available"); TypeConverter typeConverter = beanFactory.getTypeConverter(); try { value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); } synchronized (this) { if (!this.cached) { if (value != null || this.required) { this.cachedFieldValue = desc; registerDependentBeans(beanName, autowiredBeanNames); if (autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName) && beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { this.cachedFieldValue = new ShortcutDependencyDescriptor( desc, autowiredBeanName, field.getType()); } } } else { this.cachedFieldValue = null; } this.cached = true; } } } if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } }
上述代码的实现过程如下。首先第4行的代码判断给定的bean是否已经被缓存了,如果是的,则通过resolvedCacheArgument方法来直接取得这个cached的值。否则从第8行起,构造一个DependencyDescriptor对象,然后通过beanFactory和构造的typeConverter来解析出来这个值。这部分的实现逻辑在第14行。第19行开始的部分同步来处理autowiredBeanNames。如果有,则默认按照类型注入。如果没有获取到的值为空或者本身这个值不是必须的,则设置这个cachedFieldValue为空。
最后第40行的部分,如果依赖的值不为空,则将对应的bean设置为获取到的这个value值。
因为Autowired应用的范围比较广,这里针对方法里面的字段注入主要放在类AutowiredMethodElement里。它也是继承实现的InjectionMetadata.InjectedElement。这个方法的实现也叫inject:
//对方法进行注入 protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { //如果属性被显式设置为skip,则不进行注入 if (checkPropertySkipping(pvs)) { return; } //获取注入元素对象 Method method = (Method) this.member; try { Object[] arguments; //如果容器对当前方法缓存 if (this.cached) { //获取缓存中指定Bean名称的方法参数 arguments = resolveCachedArguments(beanName); } //如果没有缓存 else { //获取方法的参数列表 Class[] paramTypes = method.getParameterTypes(); //创建一个存放方法参数的数组 arguments = new Object[paramTypes.length]; DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length]; Set<String> autowiredBeanNames = new LinkedHashSet<String>(paramTypes.length); //获取容器的类型转换器 TypeConverter typeConverter = beanFactory.getTypeConverter(); for (int i = 0; i < arguments.length; i++) { //创建方法参数对象 MethodParameter methodParam = new MethodParameter(method, i); //解析方法的输入参数和返回值类型 GenericTypeResolver.resolveParameterType(methodParam, bean.getClass()); //为方法参数创建依赖描述符 descriptors[i] = new DependencyDescriptor(methodParam, this.required); //根据容器中Bean定义解析依赖关系,获取方法参数依赖对象 arguments[i] = beanFactory.resolveDependency( descriptors[i], beanName, autowiredBeanNames, typeConverter); //如果容器解析的方法参数为null,且方法required属性为false if (arguments[i] == null && !this.required) { //设置方法的参数列表为null arguments = null; break; } } //线程同步,以确保容器中数据一致性 synchronized (this) { //如果当前方法没有被容器缓存 if (!this.cached) { //如果方法的参数列表不为空 if (arguments != null) { //为容器中缓存方法参数的对象赋值 this.cachedMethodArguments = new Object[arguments.length]; for (int i = 0; i < arguments.length; i++) { this.cachedMethodArguments[i] = descriptors[i]; } //为指定Bean注册依赖Bean registerDependentBeans(beanName, autowiredBeanNames); //如果依赖对象集合大小等于方法参数个数 if (autowiredBeanNames.size() == paramTypes.length) { Iterator<String> it = autowiredBeanNames.iterator(); //为方法参数设置依赖对象 for (int i = 0; i < paramTypes.length; i++) { String autowiredBeanName = it.next(); //如果容器中存在指定名称的Bean对象 if (beanFactory.containsBean(autowiredBeanName)) { //如果参数类型和依赖对象类型匹配 if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) { //创建一个依赖对象的引用,复制给方法相应的参数 this.cachedMethodArguments[i] = new RuntimeBeanReference(autowiredBeanName); } } } } } //如果方法参数列表为null,则设置容器对该方法参数的缓存为null else { this.cachedMethodArguments = null; } //设置容器已经对该方法缓存 this.cached = true; } } } //如果方法参数依赖对象不为null if (arguments != null) { //使用JDK的反射机制,显式设置方法的访问控制权限为允许访问 ReflectionUtils.makeAccessible(method); //调用Bean的指定方法 method.invoke(bean, arguments); } } catch (InvocationTargetException ex) { throw ex.getTargetException(); } catch (Throwable ex) { throw new BeanCreationException("Could not autowire method: " + method, ex); } }
ConfigurationClassPostProcessor
还有一个比较重要而且常用的annotation就是Configuration了。在基于annotation开发的方法里,如果我们有必要自定义一些bean以及对应的配置信息,我们通常会使用到@Configuration来修饰这个定义bean相关信息的类。它相当于取代了对应的xml文件。那么,它的处理流程又是怎么样的呢?
下图是对这个annotation处理的类结构。
相应的,在ConfigurationClassPostProcessor里有具体的实现。它里面有两个方法牵涉到对应的解析处理工作。一个是postProcessBeanDefinitionRegistry, 一个是postProcessBeanFactory。这两个方法有一个共同依赖的方法processConfigBeanDefinitions。所有的代码如下,我们详细的分析一下:
@Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId); processConfigBeanDefinitions(registry); } public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; } // Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } // Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); } if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } } public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) { Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>(); for (String beanName : beanFactory.getBeanDefinitionNames()) { BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) { if (!(beanDef instanceof AbstractBeanDefinition)) { throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" + beanName + "' since it is not stored in an AbstractBeanDefinition subclass"); } else if (logger.isWarnEnabled() && beanFactory.containsSingleton(beanName)) { logger.warn("Cannot enhance @Configuration bean definition '" + beanName + "' since its singleton instance has been created too early. The typical cause " + "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " + "return type: Consider declaring such methods as 'static'."); } configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); } } if (configBeanDefs.isEmpty()) { // nothing to enhance -> return immediately return; } ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(); for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) { AbstractBeanDefinition beanDef = entry.getValue(); // If a @Configuration class gets proxied, always proxy the target class beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); try { // Set enhanced subclass of the user-specified bean class Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader); if (configClass != null) { Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader); if (configClass != enhancedClass) { if (logger.isDebugEnabled()) { logger.debug(String.format("Replacing bean definition '%s' existing class '%s' with " + "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName())); } beanDef.setBeanClass(enhancedClass); } } } catch (Throwable ex) { throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex); } } }
我们先来重点的看一下processConfigBeanDefinitions的实现。首先会通过对应的BeanDefinitionRegistry来取得对应的beanDefinitionNames列表。然后再遍历这个列表来获取对应的beanDefinition。如果它们已经是fullConfigurationClass,表示它们已经被处理过了,则直接返回。在这个循环里同时也检查当前的这个beanDefinition是否为ConfigureClass的候选。如果是,则加入到configCandiates列表里。
在后面的步骤里会把这个configCandidates列表按照它里面定义的order进行排序。接着会判断当前的registry是否为SingletonBeanRegistry。如果是的,则通过它来获取对应的beanNameGenerator。后面会接着定义一个ConfigurationClassParser对象。在接着的这个大while循环里,每次它会对当前的candidates调用parse和validate方法。在处理完之后会将当前处理完的ConfigurationClass放到一个集合里,以后每次将当前处理过的加入到alreadyParsed里面来。在这个循环里,registry每次还会获取新的beanDefinitionNames,然后将新获得还没被处理的beanDefinition加到当前的candidates里来。一直重复上述的过程直到candidates列表为空。
总结
对annotation的支持主要包含有对常用的一些annotation的解析和处理。它们大部分是通过 componentScan来扫描加载指定目录范围下的类层次,将对应的类族加载进来。然后对于常用的annotation像@Required, @Configuration, @Resource, @PostConstruct, @PreDestroy等等的处理都是通过BeanPostProcessor来处理。这样,对于新的实现的支持,也可以通过实现对应的接口加入注册到BeanPostProcessor列表里面来。在ApplicationContext的refresh方法过程里有专门遍历调用这些postProcessor的过程。
参考材料
https://blog.csdn.net/xieyuooo/article/details/9089441
https://blog.csdn.net/qq_27529917/article/details/78454929
https://blog.csdn.net/qq_27529917/article/details/78454912
https://muyinchen.github.io/2017/08/23/Spring5%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-@Autowired/
https://www.mkyong.com/spring/spring-auto-scanning-components/
https://blog.csdn.net/xieyuooo/article/details/8002321
https://stackoverflow.com/questions/6827752/whats-the-difference-between-component-repository-service-annotations-in
https://blog.csdn.net/chjttony/article/details/6301523
https://blog.csdn.net/chjttony/article/details/6301591
相关推荐
最后,对于提供的`interceptortest - 副本`文件,这可能是某个测试案例或示例代码,但具体的内容并未提供,因此无法给出具体的代码分析和解决方案。如果能提供这部分代码,我们将能够更具体地讨论冲突的解决方法。在...
在Spring MVC框架中,`mvc:annotation-driven`是Spring MVC配置中的一个重要元素,它使得我们的应用能够支持基于注解的控制器、数据绑定、格式化转换器和服务端验证等功能。这篇博客将深入探讨`mvc:annotation-...
`mvc:annotation-driven`是一个Spring MVC的XML配置元素,它简化了对注解驱动的控制器的支持。通过使用这个元素,我们可以启用Spring MVC自动扫描并处理带有`@Controller`注解的类,以及类中的`@RequestMapping`、`@...
Spring 的 Annotation-Driven 配置事务管理器详解(多数据源配置) Spring 框架提供了强大的事务管理机制,通过使用 Annotation-Driven 配置,可以方便地管理事务。在多数据源配置中,spring 的 Annotation-Driven...
Spring对注解(Annotation)处理源码分析 解析和注入注解配置的资源 源码级别的分析
本实例将详细探讨如何通过注解(Annotation)来实现Spring AOP的方法拦截。 一、Spring AOP基础 Spring AOP是Spring框架的一部分,它提供了一种在运行时织入横切关注点(如日志、事务管理等)到目标对象的能力。AOP...
Spring 框架中 @Transactional 注解的工作原理分析 在 Spring 框架中,@Transactional 注解是一个非常重要的概念,经常用于数据库操作。那么,@Transactional 注解是如何工作的呢?让我们深入源码分析。 首先,从 ...
【Spring Annotation简介一】 ...在实际项目中,结合源码分析,可以更深入地理解Spring框架的工作原理,提升自己的技能水平。通过工具,如IDEA的Spring插件,可以方便地查看和管理注解,进一步优化开发流程。
在Spring框架中,元注解(Meta-Annotation)是一种用于注解其他注解的特殊注解,它使得Spring能够提供更灵活...通过深入理解Spring源码中的元注解实现,开发者可以更好地利用Spring框架,定制适合自己项目的注解体系。
花了些时间做了一个实验,彻底弄懂了spring Annotation注入的方式。凡带有@Component,@Controller,@Service,@Repository 标志的等于告诉Spring这类将自动产生对象,而@Resource则等于XML配置中的ref,告诉spring此处...
本文将深入探讨如何通过注解(Annotation)和`@Resource`来实现自动装配,以及其背后的源码实现。 ### 一、注解驱动的自动装配 在Spring 2.5引入了注解支持后,开发者可以使用注解来声明Bean的属性、方法或构造...
- `@RunWith(SpringRunner.class)`: 使用Spring JUnit支持运行测试类。 - `@SpringBootTest`: 创建一个Spring Boot应用上下文,可用于测试整个应用。 - `@WebMvcTest`: 专注于Spring MVC层的测试,只启动Web相关...
在Spring框架中,事务管理是实现业务逻辑的重要组成部分,它确保了数据的一致性和完整性。Spring提供了多种方式来处理事务,其中包括编程式事务管理和声明式事务管理。本篇主要聚焦于"Spring 常用 Transaction ...
Spring 框架是Java开发领域中的一个核心框架,它为构建高质量的、松耦合的应用程序提供了...对于想要深入理解Spring框架的开发者来说,研究其源代码是极有价值的,能够帮助他们更好地运用和定制Spring,提升开发技能。
在实际项目中,我们不仅要理解这些基本用法,还要学会根据具体需求进行调整和优化,以实现最佳的代码结构和性能。对于初学者来说,参考提供的博客链接(https://shmilyaw-hotmail-com.iteye.com/blog/2354678)会是...
这篇“学习Spring笔记_AOP_Annotation实现和XML实现”主要探讨了如何在Spring中利用注解和XML配置来实现AOP的概念。 AOP,全称Aspect-Oriented Programming,是一种编程范式,旨在将关注点分离,让开发者可以更专注...
3. **代码生成**:利用注解处理器在编译时自动生成代码,比如使用 Google 的 AutoValue 库来自动生成不可变对象的源代码。 4. **性能优化**:如使用 `@Cacheable` 来缓存方法的结果以提高性能。 5. **安全认证**:...
【hibernate和spring源代码】的分析与详解 Hibernate是一个强大的对象关系映射(ORM)框架,它在Java开发中被广泛使用,为开发者提供了便捷的数据持久化服务。3.3.2.GA版本是Hibernate的一个稳定版本,它包含了对...