深入纠结maven的资源过滤

关于maven的资源过滤,官方文档有个例子:

<project>
...
<build>
...
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>**/*.xml</exclude>
</excludes>
</resource>
...
</resources>
...
</build>
...
</project>


刚开始我很困惑:
include=true与exclude=false难道不是等价的吗?这样配置不是重复了吗?

后来通过这篇文章[url=http://blog.csdn.net/bluishglc/article/details/6640889]这篇文章[/url]找到正确的理解方式:
1.在src/main/resources目录下,xml文件都是资源文件,且需要被过滤
2.在src/main/resources目录下,除了xml文件以外的其他文件,也是资源文件,但它们不需要被过滤

但这样,好像还是有点绕。

看看[url=https://github.com/apache/maven-plugins/tree/trunk/maven-resources-plugin]源码[/url]吧。

ResourcesMojo类的execute方法为入口:

public void execute()
throws MojoExecutionException
{
//...
mavenResourcesFiltering.filterResources( mavenResourcesExecution );
//...

}


找到org.apache.maven.shared.filtering.DefaultMavenResourcesFiltering.filterResources:



public void filterResources( MavenResourcesExecution mavenResourcesExecution )
throws MavenFilteringException {




for ( Resource resource : mavenResourcesExecution.getResources() )
{


String targetPath = resource.getTargetPath();

File resourceDirectory = new File( resource.getDirectory() );

Scanner scanner = buildContext.newScanner( resourceDirectory, ignoreDelta );

//扫描
setupScanner( resource, scanner, mavenResourcesExecution.isAddDefaultExcludes() );

scanner.scan();

//获取符合条件的文件
List<String> includedFiles = Arrays.asList( scanner.getIncludedFiles() );


for ( String name : includedFiles )
{

File source = new File( resourceDirectory, name );

File destinationFile = getDestinationFile( outputDirectory, targetPath, name, mavenResourcesExecution );

boolean filteredExt =
filteredFileExtension( source.getName(), mavenResourcesExecution.getNonFilteredFileExtensions() );

//替换和拷贝
mavenFileFilter.copyFile( source, destinationFile, resource.isFiltering() && filteredExt,
mavenResourcesExecution.getFilterWrappers(),
mavenResourcesExecution.getEncoding(),
mavenResourcesExecution.isOverwrite() );
}
}
}


步骤很清晰:
1.扫描指定目录,找出哪些文件是符合条件的
2.遍历符合条件的文件,如果需要过滤则进行过滤否则保持原样,最后拷贝到目标文件夹。

有几个关键的参数:
resource.isFiltering():是否过滤
mavenResourcesExecution.isOverwrite():是否覆盖

看看如何定义为“符合条件的文件”:


org.codehaus.plexus.util.DirectoryScanner.scan() {

if ( isIncluded( "", tokenizedEmpty ) )
{

if ( !isExcluded( "", tokenizedEmpty ) )
{
if ( isSelected( "", basedir ) )
{
dirsIncluded.addElement( "" );
}
else
{
dirsDeselected.addElement( "" );
}
}
else
{
dirsExcluded.addElement( "" );
}
}
else
{
dirsNotIncluded.addElement( "" );
}
}


看到这里就明白了:
满足<include></include>的条件,且不满足<exclude></exclude>的条件,就是“符合条件的文件”,也就是资源文件。
由代码也可看出,如果某个文件同时满足include和exclude,则结果为exclude。也就是exclude的优先级高。

再看看DirectoryScanner的注释:


* The segments of the name and the pattern are then matched against each
* other. When '**' is used for a path segment in the pattern, it matches
* zero or more path segments of the name.
* <p/>
* There is a special case regarding the use of <code>File.separator</code>s
* at the beginning of the pattern and the string to match:<br>
* When a pattern starts with a <code>File.separator</code>, the string
* to match must also start with a <code>File.separator</code>.
* When a pattern does not start with a <code>File.separator</code>, the
* string to match may not start with a <code>File.separator</code>.
* When one of these rules is not obeyed, the string will not
* match.
* <p/>
* When a name path segment is matched against a pattern path segment, the
* following special characters can be used:<br>
* '*' matches zero or more characters<br>
* '?' matches one character.
* <p/>
* Examples:
* <p/>
* "**\*.class" matches all .class files/dirs in a directory tree.
* <p/>
* "test\a??.java" matches all files/dirs which start with an 'a', then two
* more characters and then ".java", in a directory called test.
* <p/>
* "**" matches everything in a directory tree.
* <p/>
* "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where
* there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
* <p/>


从注释中可知:

1.当<resouces>没有指定include时,默认为包括目录下所有文件; 如果没有指定exclude时,默认为不排除任一文件。
2.**表示匹配任意个目录
例如**/*.xml匹配以下:
a.xml
b/b.xml
c/d/f.xml

最后举个生产上的例子:
需求:
1.所有配置项都在properties/config.properties文件里,打包时需要根据不同的profile替换成真正的数值(例如数据库地址,生产跟测试是不同的)
2.其他spring文件里引用config.properties的变量(通过org.springframework.beans.factory.config.PropertyPlaceholderConfigurer),不需要替换成真正的值。这样的好处就是万一生产上要改配置,就只改config.properties,不需要到各个spring配置文件里修改
3.eboxx-client.xml也要过滤,因为它不是基于spring的,不能引用config.properties的变量

配置:

 <resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>properties/config.properties</include>
<include>eboxx-client.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>


模拟一下maven进行资源过滤的过程:
1.读取第一个resource标签,找到符合条件的文件:config.properties文件和eboxx-client.xml
2.根据filtering=true得知,这两个文件需要过滤
3.过滤完成后,拷贝文件到默认目录(${project.build.outputDirectory},也就是target/classes目录)

4.读取第二个标签,由于没有指定include和exclude,所有文件都符合条件
5.根据filtering=false得知,这些文件不需要过滤
6.开始拷贝文件。拷贝过程中发现config.properties和eboxx-client.xml已经在默认目录存在了,由于overwrite默认为false,也就是不覆盖
7.结束

关于overwrite的默认值可以在ResourcesMojo.java中找到:


/**
* Overwrite existing files even if the destination files are newer.
*
* @since 2.3
*/
@Parameter( property = "maven.resources.overwrite", defaultValue = "false" )
private boolean overwrite;


事实上,为满足上面的需求,有个简单的办法:

1.pom.xml
<properties>
<p.jdbc.username>RTFM</p.jdbc.username>
</properties>


2.config.properties
jdbc_user=${p.jdbc.username}


3.beans.xml
 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 
<property name="username" value="${jdbc_user}" />
</bean>


在配置资源过滤时,默认过滤所有文件。
由于beans.xml与pom.xml的变量名不一样,因为即使beans.xml被过滤了,里面的变量也不会被替换。

这样,或许人生会更简单一些^_^
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值