Springソース読解1:springコンテナを初期化するときにpackageをスキャンするプロセス
6949 ワード
注:spring version 4.2.0.RELEASE
まずspring管理注記beanコンテナは主に以下のとおりです. AnnotationConfigApplicationContext(org.springframework.context.annotation) AnnotationConfigWebApplicationContext (org.springframework.web.context.support)
後者は、Web環境におけるA n o t i o n t i o n C o f i g ApplicationContext(This is essentially the equivalent of A n o t i o n C o f i g ApplicationContext for a web environment.)に相当する.
そこで主にAnnotationConfigApplicationContextを見てみましょう.
このクラスでスキャンする作業は主に
ここでscannerに対応するクラスはC l a s P h T h BeanDefinitionScannerであり、親クラスのC l a s P h T h S c a n n n i n g CandidateComponentProviderとともにpackagesをスキャンするタスクを完了することも今回の読書のポイントです
具体的には
doScanメソッドは、findCandidateComponentsが親のメソッドであるスキャナスキャンで指定されたpackageを起動します.
この時また1つのresourcePatternResolverが現れて、それはspringの中でローカルの資源の解析器のインタフェースの1つの実現で、人はPathMatchingResourcePatternResolverと呼ばれて、これは私達がプロファイルの中で書くclasspath*:などのものを解析するために用いて、そのgetResourcesの方法を見てみましょう
ここではいくつかの状況を分類します.は、「classpath*:」を接頭辞としてワイルドカード(?または*)を含む場合、findPathMatchingResourcesを呼び出します.そうでなければfindAllClassPathResources を呼び出します.で接頭辞がない場合、ワイルドカードがある場合は、同じでなければ、これは単一のリソースパスであり、直接取得されます.
findAllClassPathResourcesでスキャンを完了したのはdoFindAllClassPathResourcesです
あのClassLoaderは私たちが普段使っているものです.currentThread().getContextClassLoader()、springはそれをClassUtilsにカプセル化し、このクラスは直接ツールクラスとして使用することもできます.
したがって,ここまで来るとclassloaderで読みながら経路下の各種リソースを指定することに相当する.
ワイルドカードの扱い方を見てみましょう
まずパスをルートディレクトリとサブディレクトリに分解し、getResourcesを使用してルートディレクトリの下のすべてのディレクトリを取得し、それらを巡り、異なるタイプに応じてサブディレクトリに基づいて1つずつ一致します.そのURLはともかくPROTOCOL_VFSは、JBOSS VFS APIと関係があるようですが、以前のバージョンのspringではこれはありませんでした.ではdofindPathMatchingJarResourcesでは主に
java.util.jar.JarFileはjarのリソースを取得し、dofindPathMatchingFileResourcesでは主にjavaを使用する.io.File
パッケージをスキャンする過程は大体このようにして、様々な状況を考慮して、Springがjavaプロジェクトのパスをどのように処理するか、ファイルIO操作を学ぶことができます.
まずspring管理注記beanコンテナは主に以下のとおりです.
後者は、Web環境におけるA n o t i o n t i o n C o f i g ApplicationContext(This is essentially the equivalent of A n o t i o n C o f i g ApplicationContext for a web environment.)に相当する.
そこで主にAnnotationConfigApplicationContextを見てみましょう.
このクラスでスキャンする作業は主に
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.scanner.scan(basePackages);
}
ここでscannerに対応するクラスはC l a s P h T h BeanDefinitionScannerであり、親クラスのC l a s P h T h S c a n n n i n g CandidateComponentProviderとともにpackagesをスキャンするタスクを完了することも今回の読書のポイントです
具体的には
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) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
//
}
}
return beanDefinitions;
}
doScanメソッドは、findCandidateComponentsが親のメソッドであるスキャナスキャンで指定されたpackageを起動します.
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + this.resourcePattern;
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
//
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
この時また1つのresourcePatternResolverが現れて、それはspringの中でローカルの資源の解析器のインタフェースの1つの実現で、人はPathMatchingResourcePatternResolverと呼ばれて、これは私達がプロファイルの中で書くclasspath*:などのものを解析するために用いて、その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 {
// Only look for a pattern after a prefix here
// (to not get fooled by a pattern symbol in a strange prefix).
int prefixEnd = 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)};
}
}
}
ここではいくつかの状況を分類します.
findAllClassPathResourcesでスキャンを完了したのはdoFindAllClassPathResourcesです
protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
Set<Resource> result = new LinkedHashSet<Resource>(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は私たちが普段使っているものです.currentThread().getContextClassLoader()、springはそれをClassUtilsにカプセル化し、このクラスは直接ツールクラスとして使用することもできます.
したがって,ここまで来るとclassloaderで読みながら経路下の各種リソースを指定することに相当する.
ワイルドカードの扱い方を見てみましょう
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<Resource>(16);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));
}
else if (isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
}
else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isDebugEnabled()) {
logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(new Resource[result.size()]);
}
まずパスをルートディレクトリとサブディレクトリに分解し、getResourcesを使用してルートディレクトリの下のすべてのディレクトリを取得し、それらを巡り、異なるタイプに応じてサブディレクトリに基づいて1つずつ一致します.そのURLはともかくPROTOCOL_VFSは、JBOSS VFS APIと関係があるようですが、以前のバージョンのspringではこれはありませんでした.ではdofindPathMatchingJarResourcesでは主に
java.util.jar.JarFileはjarのリソースを取得し、dofindPathMatchingFileResourcesでは主にjavaを使用する.io.File
パッケージをスキャンする過程は大体このようにして、様々な状況を考慮して、Springがjavaプロジェクトのパスをどのように処理するか、ファイルIO操作を学ぶことができます.