/*
* Copyright 2009-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License i distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.paoding.rose.jade.context.spring;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import net.paoding.rose.jade.annotation.DAO;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.Assert;
/**
* {@link JadeComponentProvider}用于查找一个目录或jar包下符合Jade规范的DAO接口。
*
* @author Mark Fisher
* @author Juergen Hoeller
* @author Ramnivas Laddad
* @author 王志亮 [qieqie.wang@gmail.com]
* @author 廖涵 [in355hz@gmail.com]
*
* @see JadeComponentProvider
* @see org.springframework.core.type.classreading.MetadataReaderFactory
* @see org.springframework.core.type.AnnotationMetadata
* @see ScannedGenericBeanDefinition
*/
public class JadeComponentProvider implements ResourceLoaderAware {
/**
* 日志记录器
*/
private final Log logger = LogFactory.getLog(JadeComponentProvider.class);
/**
*
*/
private String resourcePattern = "**/*DAO.class";
/**
*
*/
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
/**
*
*/
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(
resourcePatternResolver);
/**
*
*/
private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();
/**
*
*/
private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
/**
*
*/
public JadeComponentProvider() {
includeFilters.add(new AnnotationTypeFilter(DAO.class));
}
/**
* Set the ResourceLoader to use for resource locations. This will
* typically be a ResourcePatternResolver implementation.
* <p>
* Default is PathMatchingResourcePatternResolver, also capable of
* resource pattern resolving through the ResourcePatternResolver
* interface.
*
* @see org.springframework.core.io.support.ResourcePatternResolver
* @see org.springframework.core.io.support.PathMatchingResourcePatternResolver
*/
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourcePatternResolver = ResourcePatternUtils
.getResourcePatternResolver(resourceLoader);
this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
}
/**
* Return the ResourceLoader that this component provider uses.
*/
public final ResourceLoader getResourceLoader() {
return this.resourcePatternResolver;
}
/**
* Set the resource pattern to use when scanning the classpath. This
* value will be appended to each base package name.
*
* @see #findCandidateComponents(String)
*/
public void setResourcePattern(String resourcePattern) {
Assert.notNull(resourcePattern, "'resourcePattern' must not be null");
this.resourcePattern = resourcePattern;
}
/**
* Add an exclude type filter to the <i>front</i> of the exclusion
* list.
*/
public void addExcludeFilter(TypeFilter excludeFilter) {
this.excludeFilters.add(0, excludeFilter);
}
/**
* 查找并返回一个目录或jar包下符合Jade规范的DAO接口。
* <p>
* 所返回的每一个BeanDefinition代表一个符合规范的DAO接口,我们可以通过
* {@link BeanDefinition#getBeanClassName()} 得到对应的DAO接口的类名
* <p>
* 所返回的BeanDefinition代表的是一个接口,不能直接注册到Spring容器中,必须先做额外的转化!
*/
public Set<BeanDefinition> findCandidateComponents(String uriPrefix) {
if (!uriPrefix.endsWith("/")) {
uriPrefix = uriPrefix + "/";
}
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
String packageSearchPath = uriPrefix + this.resourcePattern;
boolean traceEnabled = logger.isDebugEnabled();
boolean debugEnabled = logger.isDebugEnabled();
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
if (debugEnabled) {
logger.debug("[jade/find] find " + resources.length + " resources for "
+ packageSearchPath);
}
for (int i = 0; i < resources.length; i++) {
Resource resource = resources[i];
if (traceEnabled) {
logger.trace("[jade/find] scanning " + resource);
}
// resourcePatternResolver.getResources出来的classPathResources,metadataReader对其进行getInputStream的时候为什么返回null呢?
// 不得不做一个exists判断
if (!resource.exists()) {
if (debugEnabled) {
logger.debug("Ignored because not exists:" + resource);
}
} else if (resource.isReadable()) {
MetadataReader metadataReader = metadataReaderFactory
.getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(
metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (sbd.getMetadata().isInterface() && sbd.getMetadata().isIndependent()) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
} else {
if (traceEnabled) {
logger.trace("Ignored because not a interface top-level class: "
+ resource);
}
}
} else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
} else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
} catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during jade scanning", ex);
}
return candidates;
}
/**
* Determine whether the given class does not match any exclude filter
* and does match at least one include filter.
*
* @param metadataReader the ASM ClassReader for the class
* @return whether the class qualifies as a candidate component
*/
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return true;
}
}
return false;
}
}