Spring+Mybatis+Maven非web项目打包成可执行jar

时间:2021-09-24 09:40:15

花了一整天的时间,终于将一个Spring+Mybatis+Maven的非web项目打包成了可执行jar,真心觉得不容易,现在将我

使用的方法记录下来,希望能给有需要的同行们一些帮助。项目中Spring版本为3.2.2Mybatis版本为3.2.0

Maven版本为3.0.2

最终使用的是Maven插件maven-shade-plugin进行打包,方便而且得到的jar体积小。pom.xml内容为:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.xxxx.sqlXXX</groupId>
	<artifactId>sqlXXX</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>sqlXXX</name>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<org.springframework.version>3.2.2.RELEASE</org.springframework.version>
	</properties>
	<dependencies>

		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework.version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<!--余下dependency省略 ...... -->
		

	</dependencies>


	<build>
		<!-- 处理资源文件 -->
		<resources>
			<!-- 控制资源文件的复制。遍历directory下的包(**/表示深度遍历),将找到的指定格式的文件复制到jar中对应位置 -->
			<resource><!-- 复制src/main/resources下的配置文件 -->
				<directory>src/main/resources</directory>
				<includes>
					<include>**/*.properties</include>
					<include>**/*.xml</include>
				</includes>
				<filtering>false</filtering>
			</resource>
			<resource><!-- 复制与*Mapper.java文件同目录的mybatis的*Mapper.xml资源文件 -->
				<directory>src/main/java</directory>
				<includes>
					<include>**/*.xml</include>
				</includes>
				<filtering>false</filtering>
			</resource>
		</resources>
		<!-- 处理资源文件 结束-->
		
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
		  <plugin>
			    <groupId>org.apache.maven.plugins</groupId>
			    <artifactId>maven-shade-plugin</artifactId>
			    <version>2.3</version>
			    <executions>
			       <execution>
				        <phase>package</phase>
				        <goals>
				        	<goal>shade</goal>
				        </goals>
				        <configuration>
				        	<transformers>
					          	<!-- 打包jar文件时,配置manifest文件,加入程序入口mainClass -->
					            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
					            	<mainClass>com.xxxx.manager.Run</mainClass>
					            </transformer>

					            <!-- AppendingTransformer 指定当文件冲突时需要合并的文件 -->
					            <transformer  
		                            implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">  
		                        	<resource>META-INF/cxf/bus-extensions.txt</resource>  
			                 	</transformer>  
			                	<transformer  
			                    	 implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">  
			                    	<resource>META-INF/spring.handlers</resource>  
			                	</transformer>  
			                	<transformer  
			                    	 implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">  
			                    	<resource>META-INF/spring.tooling</resource>  
			                	</transformer>  
				                <transformer  
				                 	 implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">  
				                	<resource>META-INF/spring.schemas</resource>  
				                </transformer> 
				        	</transformers>
				        </configuration>
			       </execution>
			    </executions>
		   </plugin>
		</plugins>
	</build>
</project>

其中三个AppendingTransformer是关键,因为shade会将所依赖的jar文件解压并添加到我们的项目中一起打包成jar,这个过程中,具有相同包名(路径)的类或文件会被合并到一个文件夹下,而spring的spring-context-3.2.2.RELEASE.jar、spring-beans-3.2.2.RELEASE.jar、spring-aop-3.2.2.RELEASE.jar等包下面都相同名称的文件:

Spring+Mybatis+Maven非web项目打包成可执行jarSpring+Mybatis+Maven非web项目打包成可执行jarSpring+Mybatis+Maven非web项目打包成可执行jar

当合并到同一文件夹时会产生冲突。通过AppendingTransformer设置需要合并的文件,当遇到名称相同的文件时便将它们合并成一个文件。

如果没有这些设置会出现如下错误:

Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration p
roblem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframewor
k.org/schema/aop]
Offending resource: class path resource [applicationContext-common.xml]


        at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemRe
porter.java:68)
        at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:85)
        at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:80)
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.error(BeanDefinitionPa
rserDelegate.java:318)
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(Bea
nDefinitionParserDelegate.java:1435)
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(Bea
nDefinitionParserDelegate.java:1428)
        at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefini
tions(DefaultBeanDefinitionDocumentReader.java:185)
        at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanD
efinitions(DefaultBeanDefinitionDocumentReader.java:139)
        at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDef
initions(DefaultBeanDefinitionDocumentReader.java:108)
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(Xml
BeanDefinitionReader.java:493)
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBe
anDefinitionReader.java:390)
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBean
DefinitionReader.java:334)

............


打开aop中的spring.handlers,内容如下:

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

applicationContext-common.xml文件内容为:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
	<span style="color:#cc0000;">http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd</span>">

	<!-- 设置属性占位符的配置文件 -->
	<!-- <context:property-placeholder location="classpath:/config.properties" 
		/> -->
	<bean
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
		<property name="ignoreResourceNotFound" value="true" />
		<property name="ignoreUnresolvablePlaceholders" value="true" />
		<property name="locations"><span style="font-family: Arial, Helvetica, sans-serif;">........</span>

网上有很多对

Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframewor
k.org/schema/aop]
Offending resource: class path resource [applicationContext-common.xml]..................

这种错误的解决方法都是引入相应的包,比如http://www.tuicool.com/articles/6bqQ3y中说的,但都不是在生成jar时的错误。


还有一种打包方法,是利用MyEclipse自带的导出可执行jar文件功能,但是需要修改程序以适应打包后的配置文件资源路径,导致打包后程序与不打包程序不一致,而且打包后依赖的jar包不包含在生成jar文件内部,不够方便(如果把依赖包直接导入生成jar内部会导致Spring容器无法扫描到所要加载的Service等组件,产生bean没有定义的错误,用Eclipse打包好像有一种方法避免这种情况,没有试过)。