###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工具能够帮助分析当前项目的依赖,可是该工具只会分析编译主代码和测试代码所须要用到的依赖,一些执行测试和运行时须要的依赖它就发现不了。