maven产品化支持

包含内容

  • JAVA混淆
  • JS、CSS文件压缩
  • 项目版本号及相关版本信息自动设置
  • 不同环境下配置文件修改

开发环境

       项目:基于Spring、SpringMVC和JPA框架搭建。

       Proguard:4.9,开源混乱器,支持脚本控制,可以使用GUI界面,重命名a-z A-Z等单个字符名称。字符串不加密,支持 J2ME。
       Proguard插件:com.github.wvengen.proguard-maven-plugin.2.0.6

       YUICompressor:基于Rhino 对JavaScript源文件进行分析和切词,可以是去掉JavaScript文件和Css文件中冗余的空白字符(空格,换行符,制表符),对于JavaScript文件还可以对其进行混淆,更改局部变量的名称;对于CSS,还有采用优化0值属性值的表示,优化颜色值的方法压缩文件;能够融合多个js或css文件为一个文件。

       yuicompressor-maven-plugin:在maven中调用yuicompressor,参考页面:https://github.com/davidB/yuicompressor-maven-plugin。

       maven-antrun-plugin:能够在maven中运行ant任务,例如:文件目录操作、文件内容替换等。参考页面:http://maven.apache.org/plugins/maven-antrun-plugin/index.html

 

参考信息

Maven core :http://maven.apache.org/ref/3.2.3/maven-core/,介绍maven生命周期等核心概念。
Maven中profile基本使用方法:http://lpyyn.iteye.com/blog/2110927
Maven Assembly:maven打包插件,http://maven.apache.org/plugins/maven-assembly-plugin/
Proguard插件资源库地址:http://repository.pentaho.org/artifactory/repo/

ant中任务命令参考:http://ant.apache.org/manual/Tasks/,ant命令分为task和type,task是直接可以运行的命令,而type是在task中属性信息。

混淆过程

在打包前混淆class类及相关依赖类,在打包时使用assembly插件重新打带混淆的war包。
具体实现过程如下:
1.    定义profile,在打包过程中可以根据参数选择正常打包或混淆打包。
2.    配置proguard插件。在package执行proguard目标。
3.    配置assembly插件。在package执行single命令。

4.    调用方式。在根项目路径下执行mvn clean package -Pobfuscate,在子项目的pom中如果存在id=obfuscate的profile,则执行profile中对应的内容,否则不执行。

5.    混淆后的类及内容示例如下。
maven产品化支持

pom.xml中的profile的内容如下:

<profiles>
		<profile>
			<id>obfuscate</id>
			<activation>
				<activeByDefault>false</activeByDefault>
			</activation>
			<pluginRepositories>
				<pluginRepository>
					<id>pentaho-releases2</id>
		    		<url>http://repository.pentaho.org/artifactory/repo/</url>
				</pluginRepository>
			</pluginRepositories>
			<build>
				<plugins>
					<plugin>
				        <groupId>com.github.wvengen</groupId>
				        <artifactId>proguard-maven-plugin</artifactId>
				        <version>2.0.6</version>
				        <executions>
							<execution>
							    <phase>package</phase>
							    <goals>
							    	<goal>proguard</goal>
						    	</goals>
							</execution>
				        </executions>
				        
				        <configuration>
					        <target>1.7</target>
					        <encoding>UTF-8</encoding>
					        <obfuscate>true</obfuscate>
							<injar>classes</injar>
							<outjar>obfuscate-classes</outjar>
		                    <proguardInclude>${basedir}/proguard.pro</proguardInclude>
		                    <options>
								<option>-dontshrink</option>
								<option>-dontoptimize</option>
								<!-- 保存jar directories -->
								<option>-keepdirectories</option>
								<!-- 保留注解 -->
								<option>-keepattributes **</option>
							</options>
		                    <assembly>
		                    	<inclusions>
		                            <inclusion>
		                                <groupId>com.projects.synergy</groupId>
		  								<artifactId>synergy-common</artifactId>
		                            </inclusion>
		                            <inclusion>
		                                <groupId>com.projects.synergy</groupId>
		  								<artifactId>synergy-system</artifactId>
		                            </inclusion>
		                            <inclusion>
		                                <groupId>com.projects.synergy</groupId>
		  								<artifactId>synergy-wfmservice</artifactId>
		                            </inclusion>
		                        </inclusions>
		                    </assembly>
							<libs>
						        <lib>${java.home}\lib\rt.jar</lib>
						        <lib>${java.home}\lib\jsse.jar</lib>
							</libs>
				        </configuration>
				        
				        <dependencies>
				        	 <dependency>
					           <groupId>net.sf.proguard</groupId>
					           <artifactId>proguard</artifactId>
					           <version>4.9</version>
					           <scope>runtime</scope>
					         </dependency>
				        </dependencies>
				    </plugin>
				    <!-- 使用target\${project.build.finalName}文件夹下的内容进行压缩,避免修改源文件-->
				    <plugin>
						<groupId>net.alchim31.maven</groupId>
				        <artifactId>yuicompressor-maven-plugin</artifactId>
				        <version>1.4.0</version>
				        <executions>
			                <execution>
			               	 	<phase>package</phase>
			                    <goals>
			                        <goal>compress</goal>
			                    </goals>
			                </execution>
			            </executions>
				        <configuration>
				        	<encoding>UTF-8</encoding>
				        	<!-- 不显示js文件中的错误信息 -->
				        	<jswarn>false</jswarn>
				        	<!-- 不带压缩后缀 -->
				        	<nosuffix>true</nosuffix>
				        	<!-- 只压缩,不混淆 -->
				        	<nomunge>false</nomunge>
				        	<!-- js文件的目录,默认为src\main\js -->
				        	<sourceDirectory>${project.build.directory}\${project.build.finalName}</sourceDirectory>
				        	<!-- 不包括没有被压缩的文件 -->
				        	<excludes>
				        		<exclude>**/*min.js</exclude>
				        		<exclude>**/*min.css</exclude> 
				        	</excludes>
				        </configuration>
					</plugin>
					<plugin>
				        <groupId>org.apache.maven.plugins</groupId>
				        <artifactId>maven-antrun-plugin</artifactId>
				        <version>1.7</version>
				        <executions>
                                                <execution>
				        		<id>prepare-package-antrun</id>
			                                <phase>prepare-package</phase>  
			                                <configuration>
                                                                 <!--在控制台提示输入当前的版本号信息,并根据输入的版本号替换jsp页面中相关版本信息-->
			                                       <target name="get new version">  
										<input addproperty="new.verion" defaultvalue="1.0.0">please input the new project version,default version is : </input>
										<!-- 创建临时文件存储输入的版本号信息 -->
										<propertyfile file="${project.build.directory}\version.properties">
											<entry key="new.verion" value="${new.verion}"/>
										</propertyfile>
			                     		<!-- 替换掉整个项目中jsp页面中的版本号 -->
			                     		<replaceregexp match="COMPANY V\d+(\.\d+)*" replace="COMPANY V${new.verion}" flags="g" encoding="utf-8">
			                               <fileset dir="${basedir}" includes="**/*.jsp *.jsp"/>  
			                            </replaceregexp>
			                     </target>   
			                                </configuration>
			                   <goals>  
			                     <goal>run</goal>  
			                   </goals>  
			               </execution>
				          <execution>
				            <phase>package</phase>
				            <configuration>
				            	<!-- 所有的ant任务必须在target中执行 -->
				              <target name="replace js file content and rename aggregate file.">
				              		<!-- 替换js文件中使用ajax加载js文件的操作 -->
									<replaceregexp match="UtilMisc\.loadJS\(\S*\);" replace="" flags="g" encoding="utf-8">
									    <fileset dir="${project.build.directory}\${project.build.finalName}" includes="**/*.js *.js"/>
									</replaceregexp>
									<!-- 合并js文件,删除合并前的js子文件-->
									<concat destfile="${project.build.directory}\${project.build.finalName}\resources\general\core\adstaffairs\synergy.adstaffairs.js"
										encoding="utf-8" outputencoding="utf-8" fixlastline="true" append="true">
										<fileset dir="${project.build.directory}\${project.build.finalName}\resources\general\core\adstaffairs" 
											includes="**/*.js *.js" excludes="synergy.adstaffairs.js"/>
									</concat>
									<delete dir="${project.build.directory}\${project.build.finalName}\resources\general\core\adstaffairs" includeemptydirs="true"
										includes="**/*.js *.js" excludes="synergy.adstaffairs.js"/>
										
									<concat destfile="${project.build.directory}\${project.build.finalName}\resources\general\core\docmanage\synergy.docmanage.js"
										encoding="utf-8" outputencoding="utf-8" fixlastline="true" append="true">
										<fileset dir="${project.build.directory}\${project.build.finalName}\resources\general\core\docmanage" 
											includes="**/*.js *.js" excludes="synergy.docmanage.js"/>
									</concat>
									<delete dir="${project.build.directory}\${project.build.finalName}\resources\general\core\docmanage" includeemptydirs="true"
										includes="**/*.js *.js" excludes="synergy.docmanage.js"/>
                                                                        <!-- 读取版本信息 -->
									<property file="${project.build.directory}\version.properties"></property>
		                      		<echo message="war包版本信息->${new.verion}"/>
          				            <!-- 替换掉整个项目pom文件中的版本号信息 -->
		                            <replaceregexp match="&lt;synergy.version&gt;\d+(\.\d+)*&lt;/synergy.version&gt;" replace="&lt;synergy.version&gt;${new.verion}&lt;/synergy.version&gt;" flags="g" encoding="utf-8">
		                               <fileset dir="${basedir}\..\" includes="**/pom.xml pom.xml"/>  
		                            </replaceregexp>
									<!-- 替换掉target中生成文件中的版本号 -->
									<move file="${project.build.directory}" todir="" encoding="utf-8" outputencoding="utf-8">  
		                                <fileset dir="${project.build.directory}" includes="**/*">
		                                	<depth max="1"/>
		                                </fileset>
		                                <mapper type="glob" from="*-${project.version}.war" to="*-${new.verion}.war"/>
		                            </move>
		                            <!-- 删除临时文件版本号信息 -->
		                            <delete file="${project.build.directory}\version.properties"/>
<!-- 删除默认生成的war包,避免和新生成的混淆war包重名 -->
		                            <delete file="${project.build.directory}\${project.artifactId}.war"/>
				                                </target>
				            </configuration>
				            <goals>
				              <goal>run</goal>
				            </goals>
				          </execution>
				        </executions>
			        </plugin>
					<plugin>
						<groupId>org.apache.maven.plugins</groupId>
						<artifactId>maven-assembly-plugin</artifactId>
						<configuration>
<!--最终的war包名称不包括版本号信息-->
							<finalName>${project.artifactId}-obfuscated</finalName>
							<appendAssemblyId>false</appendAssemblyId>
						    <descriptors>
								<descriptor>obfuscate_war.xml</descriptor>
							</descriptors>
						</configuration>
						<executions>
							<execution>
								<phase>package</phase>
								<goals>
									<goal>single</goal>
								</goals>
							</execution>
						</executions>
					</plugin>
				</plugins>
			</build>
		</profile>
<profile>
			<id>test</id>
			<activation>
				<activeByDefault>false</activeByDefault>
			</activation>
			<build>
				<plugins>
					<plugin>
						<groupId>com.juvenxu.portable-config-maven-plugin</groupId>
						<artifactId>portable-config-maven-plugin</artifactId>
						<version>1.1.5</version>
						<executions>
						  <execution>
						    <goals>
						      <goal>replace-package</goal>
						    </goals>
						  </execution>
						</executions>
						<configuration>
						  <portableConfig>${basedir}/test.xml</portableConfig>
						</configuration>
					</plugin>
				</plugins>
				<!-- 指定finalName,否则portable-config-maven-plugin会寻找默认带版本号的war包 -->
				<finalName>${project.artifactId}</finalName>
			</build>
		</profile>
		<profile>
			<id>production</id>
			<activation>
				<activeByDefault>false</activeByDefault>
			</activation>
			<build>
				<plugins>
					<plugin>
						<groupId>com.juvenxu.portable-config-maven-plugin</groupId>
						<artifactId>portable-config-maven-plugin</artifactId>
						<version>1.1.5</version>
						<executions>
						  <execution>
						    <goals>
						      <goal>replace-package</goal>
						    </goals>
						  </execution>
						</executions>
						<configuration>
						  <portableConfig>${basedir}/production.xml</portableConfig>
						</configuration>
					</plugin>
				</plugins>
				<!-- 指定finalName,否则portable-config-maven-plugin会寻找默认带版本号的war包 -->
				<finalName>${project.artifactId}</finalName>
			</build>
		</profile>
	</profiles>

 proguard.pro配置信息

-keeppackagenames **.web,**.entity,**.dao,**.generator,**.timertask,**.interceptor,**.tag,**.system,**.wfmservice,**.common

-keep class **.web.**{*;}
-keep class **.entity.**{*;}
-keep class **.dao.**{*;}
-keep class **.timertask.**{*;}
-keep class **.interceptor.**{*;}
-keep class **.generator.**{*;}
-keep class **.tag.**{*;}
-keep class **.common.**{*;}
-keep class **.system.**{*;}
-keep class **.wfmservice.**{*;}

-keepclassmembers class **.dao.** {  
    <fields>;
    <methods>; 
}
-keepclassmembers class **.web.** {  
    <fields>;
    <methods>; 
}
-keepclassmembers class **.entity.** {  
    <fields>;
    <methods>; 
}
-keepclassmembers class **.timertask.** {  
    <fields>;
    <methods>; 
}
-keepclassmembers class **.interceptor.** {  
    <fields>;
    <methods>; 
}
-keepclassmembers class **.generator.** {  
    <fields>;
    <methods>; 
}
-keepclassmembers class **.tag.** {  
    <fields>;
    <methods>; 
}
-keepclassmembers class **.system.** {  
    <fields>;
    <methods>; 
}
-keepclassmembers class **.common.** {  
    <fields>;
    <methods>; 
}
-keepclassmembers class **.wfmservice.** {  
    <fields>;
    <methods>; 
}

       -keeppackagenames:需要保留的包列表。

       -keep:需要保留的类。

       -keepclassmembers:需要保留的类成员信息。

       对于当前项目来说,需要保留类包括实体类(保存与数据库的映射)、Dao类(使用JPA,需要按规则定义方法名)、Controller类(@requestParam等注解根据变量名称映射变量值)以及其它在配置文件中显式声明的类。

 

 assembly打包配置:obfuscate_war.xml
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
	<id>package_project</id>
	<formats>
		<format>war</format>
	</formats>
	<!-- war包中不包括项目根目录名称 -->
	<includeBaseDirectory>false</includeBaseDirectory>
	<dependencySets>
		<dependencySet>
			<!-- 不包括当前项目已构建的包 -->
			<useProjectArtifact>false</useProjectArtifact>
			<!-- 不包括jar包依赖,使用混淆后的依赖 -->
			<excludes>
				<exclude>com.projects.synergy:synergy-common</exclude>
				<exclude>com.projects.synergy:synergy-system</exclude>
				<exclude>com.projects.synergy:synergy-wfmservice</exclude>
			</excludes>
			<outputDirectory>WEB-INF\lib</outputDirectory>
		</dependencySet>
	</dependencySets>
	<fileSets>
		<!-- 拷贝当前项目中的混淆的class类 -->
		<fileSet>
			<directory>\target\obfuscate-classes\</directory>
			<outputDirectory>WEB-INF\classes</outputDirectory>
			<excludes>
				<exclude>*.jar</exclude>
			</excludes>
		</fileSet>
		<!-- 拷贝当前项目混淆后的依赖类 -->
		<fileSet>
			<directory>\target\obfuscate-classes\</directory>
			<outputDirectory>WEB-INF\lib</outputDirectory>
			<includes>
				<include>*.jar</include>
			</includes>
		</fileSet>
		<!-- 从\target\${project.build.finalName}中拷贝出来jar包和classes类之外的内容 -->
		<fileSet>
			<directory>\target\${project.build.finalName}\</directory>
			<outputDirectory></outputDirectory>
			<excludes>
				<exclude>\WEB-INF\lib\**</exclude>
				<exclude>\WEB-INF\classes\**</exclude>
			</excludes>
		</fileSet>
	</fileSets>
</assembly>
  使用portable-config-maven-plugin修改不同环境的配置文件
        portable-config-maven-plugin 与 resource filter 对比 :http://fuxueliang.com/tool/2013/08/16/portable-config-maven-plugin-vs-resource-or-war-filter/
        portable-config-maven-plugin插件:https://github.com/juven/portable-config-maven-plugin
        不同环境中的配置文件修改可以使用 maven本身提供的resource filtermaven插件portable-config-maven-plugin。
        使用resource filter:配置文件中的变量必须使用占位符,maven resource filter会根据占位符替换相应的内容。
        使用portable-config-maven-plugin:根据properties文件或xml文件中的关键字替换关键字值的内容,在properties和xml文件中的内容具有默认值。对war文件中的配置文件修改时,war包名称默认为${project.artifactid}-${project.version}.war。
<profile>
			<id>test</id>
			<activation>
				<activeByDefault>false</activeByDefault>
			</activation>
			<build>
				<plugins>
					<plugin>
						<groupId>com.juvenxu.portable-config-maven-plugin</groupId>
						<artifactId>portable-config-maven-plugin</artifactId>
						<version>1.1.5</version>
						<executions>
						  <execution>
						    <goals>
						      <goal>replace-package</goal>
						    </goals>
						  </execution>
						</executions>
						<configuration>
						  <portableConfig>${basedir}/test.xml</portableConfig>
						</configuration>
					</plugin>
				</plugins>
				<!-- 指定finalName,否则portable-config-maven-plugin会寻找默认带版本号的war包 -->
				<finalName>${project.artifactId}</finalName>
			</build>
		</profile>
  在配置过程中遇到问题总结:
       1.    不使用proguard插件的压缩(shrink)、代码优化(optimize)、改变包目录结构、Class类内容以及其它修改包内容的命令。因为这些命令虽然能够优化代码,但是优化的过程是不可控的,会产生很多问题,例如:某个方法被删除、class类字节编码验证失败等。因此只加入如下两条不压缩和不优化的两条命令。
-dontshrink
-dontoptimize
         2.    为了能够保存所有Spring的注解,并且能够使Spring能够扫描到jar中的class类,分别加入以下两条命令。
-keepattributes **
-keepdirectories
        3.    混淆后的Class类虽然位于不同的包下,但是可能会出现重名的情况,而使用Spring注解,id默认和类名相同,就可能会出现id冲突,因此默认id改为类的完整路径,通过在spring扫描时设置name-generator属性实现。
<context:component-scan base-package="com" name-generator="com.projects.synergy.common.generator.AnnotationFullBeanNameGenerator" > 
</context:component-scan>
      AnnotationFullBeanNameGenerator继承默认的注解id生成类AnnotationBeanNameGenerator,并覆盖了默认id生成方法buildDefaultBeanName,内容如下:
package com.projects.synergy.common.generator;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;

public class AnnotationFullBeanNameGenerator extends AnnotationBeanNameGenerator {
	@Override  
    protected String buildDefaultBeanName(BeanDefinition definition) {  
        return definition.getBeanClassName();  
    } 
}
       4.    com.github.wvengen.proguard-maven-plugin.2.06.jar在maven中央库中不存在,需要从库:http://repository.pentaho.org/artifactory/repo/中下载,因此需要设置插件库地址。
       5.    在proguard插件配置中,将依赖的包进行了混淆,被混淆的jar包也被放入obfuscate-classes中。在使用assembly打war包时,首先将相应的原有maven依赖包移除,然后分别将obfuscate-classes中内容放到WEB-INF\classes和WEB-INF\lib下。
        6.    yuicompressor-maven-plugin插件的aggregation任务中,如果output设置的输出文件名和项目以后的文件重名,会首先删除项目中文件,然后再重新建一个新文件。因此,需要给output文件名加后缀“-obfuscate”,以做区分。在压缩完成后在使用ant命令重命名加后缀“-obfuscate”的文件。
       7.    yuicompressor-maven-plugin插件的aggregation任务中,设置<removeIncluded>true</removeIncluded>会将include中包含的文件删掉,因此聚合的inputDir不要设置源文件目录。
       8.    yuicompressor使用的sourceDirectory为target\ ${project.build.finalName},直接对文件夹中的js文件进行压缩,exclude的文件在压缩完成之后也包括在当前文件夹下,不受yuicompressor压缩的影响。因此assembly打包时,可以直接拷贝当前文件夹下的内容。
       9.    yuicompressor-maven-plugin插件的aggregation任务中,会根据include中文件匹配顺序合并js文件,由于js存在命名空间的问题,因此根命名空间要合并到文件前面。
       10.    maven-antrun-plugin能够在maven中运行ant的任务,包括文件目录操作、文件内容替换、文件删除和重命名等,但是所有的任务必须放到<target>标签中。在执行时maven会根据target中任务新建一个ant xml文件。
       11.    在使用yuicompressor-maven-plugin进行js文件合并时,在eclipse中执行maven控制台命令,合并后的js文件没有乱码,在Windows控制台中执行maven控制台命令会出现乱码。通过设置maven资源编码方式、JDK编码方式和yuicompressor-maven-plugins的编码方式为utf-8都不起作用。所以直接抛弃使用yui进行js压缩的方式,改用antrun进行js文件合并。
       12.   在maven-antrun-plugin的一个execution阶段输入的properties属性,在其它的execution阶段不能被访问到。解决方式为:将需要被访问的properties属性放到一个antrun创建的一个共享文件中。如下:
<!--创建properties文件-->
<propertyfile file="${project.build.directory}\version.properties">
											<entry key="new.verion" value="${new.verion}"/>
										</propertyfile>
<!--读取properties文件-->
<property file="${project.build.directory}\version.properties"></property>
      13.   现在已经将产品配置文件和混淆的过程分开,分别对应不同的profile,既可以在产品中进行混淆也可以在产品中不混淆。但是在pom.xml中必须将产品的profile放到混淆的profile之后,执行命令时也必须首先声明混淆再声明产品,即clean package -Dmaven.test.skip=true -Pobfuscate -Pproduction。
      14.   在windows系统下可以直接使用控制台的命令进行maven打包。可以将所有的maven打包命令放到一个bin文件夹下,然后将bin文件夹放到项目目录下。运行bin文件夹下的相应命令执行打包。bin文件夹见附件。

你可能感兴趣的:(maven)