注:spring version 4.2.0.RELEASE
首先,spring 管理注解bean容器主要是:
- AnnotationConfigApplicationContext(org.springframework.context.annotation)
- AnnotationConfigWebApplicationContext (org.springframework.web.context.support)
后者是相当于在web环境下的AnnotationConfigApplicationContext(This is essentially the equivalent of AnnotationConfigApplicationContext 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对应的类是ClassPathBeanDefinitionScanner,它和它的父类ClassPathScanningCandidateComponentProvider共同完成扫描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方法启动扫描器扫描指定的package ,其中findCandidateComponents是其父类的方法
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; }
这时候又冒出来一个resourcePatternResolver,它是spring中本地资源解析器接口的一个实现,人称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)}; } } }
这里分类几种情况:
- 以”classpath*:“为前缀并且包含了通配符(?或*),则调用findPathMatchingResources,否则调用findAllClassPathResources
- 而没有该前缀的情况,如果还是有通配符,同上,否则这是一个单个资源路径,就直接获取。
在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就是我们平时用的Thread.currentThread().getContextClassLoader(),spring把它封装到了ClassUtils中,这个类也可以直接拿来当工具类使用。
所以,到了这里,就相当于用classloader去读一边指定路径下的各种资源。
再看一下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<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获得根目录下的所有目录,然后遍历它们,根据不同的类型,根据子目录逐条匹配。暂且不去管那个URL_PROTOCOL_VFS,貌似和JBOSS VFS API有关,之前版本的spring并没有这个。那么在doFindPathMatchingJarResources中主要是使用的
java.util.jar.JarFile去获取jar中的资源,而在doFindPathMatchingFileResources中主要是使用java.io.File
扫描package的过程大致就如此了,考虑了各种情况,可以学习到Spring是如何处理java项目中的路径,以及文件IO操作。
相关推荐
2、IOC初始化 声明一个IOC容器,Map 3、scan-package 配置一个包路径,扫描到相关的类 4、实例化 将扫描到的相关类,利用反射机制实例化,并且保存到IOC容器之中 5、依赖注入 (DI)自动给IOC容器中的对象,需要自动...
mybatis实战教程mybatis in action之五与spring3集成附源码 mybatis实战教程mybatis in action之六与Spring MVC 的集成 mybatis实战教程mybatis in action之七实现mybatis分页源码下载 mybatis实战教程mybatis in ...
我们前面已经指出Oracle的Lob字段和一般类型的字段在操作上有一个明显的区别--那就是你必须首先通过Oracle的empty_blob()/empty_clob()初始化Lob字段,然后获取该字段的引用,通过这个引用更改其值。所以要完成对...
11. 修复表单配置中的swtich组件初始化数据可能存在的渲染问题 12. 优化小程序下单时模版消息可能不生效的原因(防抖) 13. 优化分类模版销量展示 14. 修复富文本组件可能存在输入光标不准确的问题 15. 修复Java导出...
11. 修复表单配置中的swtich组件初始化数据可能存在的渲染问题 12. 优化小程序下单时模版消息可能不生效的原因(防抖) 13. 优化分类模版销量展示 14. 修复富文本组件可能存在输入光标不准确的问题 15. 修复Java导出...
初始化数据库:找到项目数据库文件:docs/db/pb_cms_base.sql,执行 pb_cms_base.sql 安装Redis:Redis最低版本支持 3.2 修改(resources/application.yml)配置文件 修改数据库链接相关连接串、用户名和密码(可搜索...
初始化数据库:找到项目数据库文件:docs/db/pb_cms_base.sql,执行 pb_cms_base.sql 安装Redis:Redis最低版本支持 3.2 修改(resources/application.yml)配置文件 修改数据库链接相关连接串、用户名和密码(可搜索...
11. 修复表单配置中的swtich组件初始化数据可能存在的渲染问题 12. 优化小程序下单时模版消息可能不生效的原因(防抖) 13. 优化分类模版销量展示 14. 修复富文本组件可能存在输入光标不准确的问题 14. 修复富文本...
11. 修复表单配置中的swtich组件初始化数据可能存在的渲染问题 12. 优化小程序下单时模版消息可能不生效的原因(防抖) 13. 优化分类模版销量展示 14. 修复富文本组件可能存在输入光标不准确的问题 15. 修复Java导出...
11. 修复表单配置中的swtich组件初始化数据可能存在的渲染问题 12. 优化小程序下单时模版消息可能不生效的原因(防抖) 13. 优化分类模版销量展示 14. 修复富文本组件可能存在输入光标不准确的问题 15. 修复Java导出...
导入数据库初始化脚本src\main\sql\aaplan_test.sql。 源码使用Maven构建,确保安装Maven的情况下,在文件目录运行 mvn clean package -DskipTests 把war包拷到tomcat目录下的Webapp目录下,运行tomcat。
DWR已经默认定义和初始化了常用的Converter,他们分别如下: class="uk.ltd.getahead.dwr.convert.NullConverter"/> class="uk.ltd.getahead.dwr.convert.PrimitiveConverter"/> class="uk.ltd.getahead.dwr....
一 Jeecms安装过程 将解压后得到的jeecms-3.0.2-final文件夹下的root文件夹更名为jeecms拷贝到tomcat 安装目录下的webapps 文件夹下(例如: D:\Tomcat 6.0\webapps\),启动tomcat,在地址栏中输入...