###5.1 何爲Maven座標### Maven的世界中擁有數量龐大的構件,也就是日常用的jar、war包,就像在三維座標系中一個座標值(x,y,z)能夠惟一的肯定一個點的位置信息,Maven中的構件也須要一個座標來標識它們。在咱們開發Maven項目的時候,須要爲其定義適當的座標,這是Maven強制要求的。在這個基礎上,其餘Maven項目才能應用該項目生成的構件。 ###5.2 座標詳解### Maven座標爲各類構件引入了秩序,任何一個構件都必須明肯定義本身的座標,而一組Maven座標是經過一些元素定義的,它們是groupId,artifactId,version,packaging,class-sifer。下面是一組座標定義:html
<groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <packaging>jar</packaging> <version>0.0.1-SNAPSHOT</version>
下面講解一下各個座標元素:java
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>3.1.2.RELEASE</version> </dependency>
可是有些架包仍是比較特殊的:
好比 JSON-lib ,我利用 sonatype的 jar 搜素引擎搜索發現,JSON-lib的jar提供了兩個版本的SDK,如圖:
這個時候就必須使用classifier屬性指定了:mysql
<dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.4</version> <classifier>jdk15</classifier> </dependency>
若是不定義classifier的話就maven就會報錯說找不到 jar 文件。 ###5.3 account-email### 工程總佈局如圖:
####5.3.1 account-email的POM####web
<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.my.account</groupId> <artifactId>account-email</artifactId> <version>1.0.0-SNAPSHOT</version> <properties> <springversion>4.2.1.RELEASE</springversion> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${springversion}</version> <!--classifier>RELEASE</classifier--> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${springversion}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${springversion}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${springversion}</version> </dependency> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.7</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <dependency> <groupId>com.icegreen</groupId> <artifactId>greenmail</artifactId> <version>1.4.1</version> <scope>test</scope> </dependency> </dependencies> </project>
####5.3.2 account-email的主代碼####spring
package com.learn.mvn.account.email; public class AccountEmailException extends Exception { private static final long serialVersionUID = -4817386460334501672L; public AccountEmailException( String message ) { super( message ); } public AccountEmailException( String message, Throwable throwable ) { super( message, throwable ); } }
package com.learn.mvn.account.email; public interface AccountEmailService { void sendMail(String to, String subject, String htmlText ) throws AccountEmailException; }
package com.learn.mvn.account.email; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; public class AccountEmailServiceImpl implements AccountEmailService { private JavaMailSender javaMailSender; private String systemEmail; public JavaMailSender getJavaMailSender() { return javaMailSender; } public void setJavaMailSender(JavaMailSender javaMailSender) { this.javaMailSender = javaMailSender; } public String getSystemEmail() { return systemEmail; } public void setSystemEmail(String systemEmail) { this.systemEmail = systemEmail; } public void sendMail(String to, String subject, String htmlText) throws AccountEmailException { try { MimeMessage msg = javaMailSender.createMimeMessage(); MimeMessageHelper msgHelper = new MimeMessageHelper(msg); msgHelper.setFrom(systemEmail); msgHelper.setTo(to); msgHelper.setSubject(subject); msgHelper.setText(htmlText, true); javaMailSender.send(msg); } catch (MessagingException e) { throw new AccountEmailException("Failed to send email.", e); } } }
Spring的配置文件account-email.xml:sql
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:email.properties" /> </bean> <bean id="javaMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host" > <value>${email.host}</value> </property> <property name="port" > <value>${email.port}</value> </property> <property name="protocol"> <value>${email.protocol}</value> </property> <property name="username"> <value>${email.username}</value> </property> <property name="password"> <value>${email.password}</value> </property> <!-- SMTP服務器驗證 --> <property name="javaMailProperties"> <props> <!-- 驗證身份 --> <prop key="mail.${email.protocol}.auth">${email.auth}</prop> </props> </property> </bean> <bean id="accountEmailService" class="com.learn.mvn.account.email.AccountEmailServiceImpl"> <property name="javaMailSender" ref="javaMailSender" /> <property name="systemEmail" value="${email.systemEmail}" /> </bean> </beans>
####5.3.3 account-email的測試代碼####數據庫
package com.learn.mvn.account.email; import static org.junit.Assert.*; import javax.mail.Message; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.icegreen.greenmail.util.GreenMail; import com.icegreen.greenmail.util.GreenMailUtil; import com.icegreen.greenmail.util.ServerSetup; public class AccountEmailServiceTest { private GreenMail greenMail; private ApplicationContext applicationContext; /** * 啓動郵件服務器 * * @throws Exception * @return void */ @Before public void setUp() throws Exception { greenMail = new GreenMail(ServerSetup.SMTP); greenMail.setUser("test1234@163.com", "test1234"); greenMail.start(); } /** * Test method for */ @Test public void testSendEmail() throws Exception { applicationContext = new ClassPathXmlApplicationContext("account-email.xml"); AccountEmailService accountEmailService = (AccountEmailService) applicationContext.getBean("accountEmailService"); String subject = "Test Subject"; String htmlText = "<h3> Test </h3>"; accountEmailService.sendMail("test1234@163.com", subject, htmlText); greenMail.waitForIncomingEmail(2000, 1); Message[] msgs = greenMail.getReceivedMessages(); assertEquals(1, msgs.length); assertEquals(subject, msgs[0].getSubject()); assertEquals(htmlText, GreenMailUtil.getBody(msgs[0]).trim()); } /** * 關閉郵件服務器 * * @throws Exception * @return void */ @After public void tearDown() throws Exception { greenMail.stop(); } }
email.properties文件:apache
email.protocol=smtp email.host=localhost email.port=25 email.username=test1234@163.com email.password=test1234 email.auth=true email.systemEmail=test1234@163.com
運行mvn clean test執行測試:json
------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.learn.mvn.account.email.AccountEmailServiceTest SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further detail s. 九月 18, 2015 12:20:11 上午 org.springframework.context.support.ClassPathXmlAppl icationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationCont ext@220711: startup date [Fri Sep 18 00:20:11 CST 2015]; root of context hierarc hy 九月 18, 2015 12:20:11 上午 org.springframework.beans.factory.xml.XmlBeanDefinit ionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [account-email.xml] 九月 18, 2015 12:20:11 上午 org.springframework.beans.factory.config.PropertyPla ceholderConfigurer loadProperties 信息: Loading properties file from class path resource [email.properties] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.991 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.984 s [INFO] Finished at: 2015-09-18T00:20:11+08:00 [INFO] Final Memory: 12M/29M [INFO] ------------------------------------------------------------------------
###5.4 依賴的配置### 依賴能夠聲明以下:api
<project> ... <dependencies> <dependency> <groupId>group-a</groupId> <artifactId>artifact-a</artifactId> <version>1.0</version> <exclusions> <exclusion> <groupId>group-c</groupId> <artifactId>excluded-artifact</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>group-a</groupId> <artifactId>artifact-b</artifactId> <version>1.0</version> <type>bar</type> <scope>runtime</scope> </dependency> </dependencies> </project>
依賴會包含基本的groupId, artifactId,version等元素,根元素project下的dependencies能夠包含一個或者多個dependency元素,以聲明一個或者多個依賴。
下面詳細講解每一個依賴能夠包含的元素:
groupId,artifactId和version:依賴的基本座標,對於任何一個依賴來講,基本座標是最重要的,Maven根據座標才能找到須要的依賴。
type: 依賴的類型,對應於項目座標定義的packaging。大部分狀況下,該元素沒必要聲明,其默認值是jar。
scope: 依賴的範圍,下面會進行詳解。
optional: 標記依賴是否可選。
exclusions: 用來排除傳遞性依賴,下面會進行詳解。
大部分依賴聲明只包含基本座標。 ###5.5 依賴範圍### Maven在編譯主代碼的時候須要使用一套classpath,在編譯和執行測試的時候會使用另外一套classpath,實際運行項目的時候,又會使用一套classpath。
依賴範圍就是用來控制依賴與這三種classpath(編譯classpath、測試classpath、運行classpath)的關係,Maven有如下幾種依賴範圍:
<dependency> <groupId>javax.sql</groupId> <artifactId>jdbc-stdext</artifactId> <version>2.0</version> <scope></scope> <systemPath>${java.home}/lib/rt.jar</systemPath> </dependency>
<project> <modelVerion>4.0</modelVersion> <groupId>com.learn.mvn</groupId> <artifactId>project-B</artifactId> <version>1.0</version> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.10</version> <optional>true</optional> </dependency> <dependency> <groupId>postgresql</groupId> <artifactId>postagresql</artifactId> <version>8.4-701.jdbc3</version> <optional>true</optional> </dependency> </dependencies> </project>
所以當項目A依賴項目B的時候,若是實際使用基於Mysql數據庫,那麼在項目A中須要顯示的聲明mysql-connection-java依賴。
最後,關於可選依賴須要說明的一點是,在理想的狀況下,是不該該使用可選依賴的。前面咱們能夠看到,使用可選依賴的緣由是某一個項目實現了多個特性,在面向對象設計中,有個單一職責性原則,意指一個類應該只有一項職責,而不是糅合太多的功能 。這個原則在規劃maven項目的時候也一樣適用。在上面的例子中,更好的作法是爲mysql和postgresql分別建立一個maven項目,基於一樣的groupId分配不一樣的artifactId。 ###5.9 最佳實踐### ####5.9.1 排除依賴#### 傳遞性依賴給項目隱式地引入了不少依賴,這極大地簡化了項目的依賴管理,可是有時候這種特性也會帶來問題。好比,當前項目有一個第三方依賴,而這個第三方依賴因爲某些緣由依賴了另一個類庫的SNAPSHOT的版本,那麼這個SNAPSHOT就會成爲當前項目的傳遞性依賴,而SNAPSHOT的不穩定性會影響到當前項目。這時須要排除該SNAPSHOT,而且在當前項目中聲明該類庫某個正式發佈版本。
<project> <modelVerion>4.0</modelVersion> <groupId>com.learn.mvn</groupId> <artifactId>project-a</artifactId> <version>1.0.0</version> <dependencies> <dependency> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-b</artifactId> <version>1.0.0</version> <exclusions> <exclusion> <groupId>com.juvencu.mvnbook</groupId> <artifactId>project-c</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.juvencu.mvnbook</groupId> <artifactId>project-c</artifactId> <version>1.1.0</version> </dependency> </dependencies> </project>
代碼中,項目A依賴於項目B,可是因爲一些緣由,不想引入傳遞性依賴C,而是本身顯示地聲明對於項目C1.1.0版本的依賴。代碼中使用exclusions元素聲明排除依賴,exclusions能夠包含一個或者多個exclusion子元素。
須要注意的是,聲明exclusion的時候只須要groupId,artifactId就能惟必定義某個依賴。 ####5.9.2 歸類依賴#### 經過<properties>元素來定義。經過${變量名}來引用聲明一個常量信息,全部用到的地方都用這個常量
<properties> <springversion>2.5.6</springversion> <junitversion>2.5.6</junitversion> </properties>
在依賴使用的時候只須要:<version>${springversion}</version> ####5.9.3 優化依賴#### maven會自動解析全部項目的直接依賴和傳遞性依賴,而且根據規則正確判斷每一個依賴的範圍。對於一些依賴衝突,也能進行調節,以確保任何一個構件只有惟一的版本在依賴中存在。在這些工做以後,最後獲得的那些依賴被稱爲解析依賴。運行下面兩條命令分別能夠查看當前項目的已解析依賴。 mvn dependency:list mvn dependency:tree 使用mvn dependency:list和mvn dependency:tree能夠幫助咱們詳細瞭解項目中全部依賴的具體信息。在此基礎上,還有dependency:analyze工具能夠幫助分析當前項目的依賴,可是該工具只會分析編譯主代碼和測試代碼所須要用到的依賴,一些執行測試和運行時須要的依賴它就發現不了。