使用Maven构建Web应用(上)
2015-07-28 16:23
387 查看
在Java的世界中,Web应用占有很大的地位,而它的标准打包方式是WAR。接下来通过Maven构建一个WAR应用,不过由于篇幅,这一篇先介绍Maven中的Web项目结构和服务模块的构建,WAR应用放在下篇具体介绍。
Web项目的目录结构
Web应用的打包方式WAR与JAR类似,但它包含了更多内容,如JSP文件、Servlet、web.xml配置文件、静态web资源(如html,css,js)等。
一个WAR包下至少包含两个子目录:META-INF和WEB-INF。前者包含了一些打包元数据信息,后者是WAR包的核心,WEB-INF下必须包含一个Web资源表述文件web.xml,它的子目录classes包含所有该Web项目的类,而另一个子目录lib则包含了有所有该Web项目的依赖Jar包,classes和lib目录都会在运行时被加入到Classpath中。除了META-INF和WEB-INF外,一般的WAR包都会包含很多Web资源,比如html和jsp文件,有时还有些文件夹如img,css和js,它们包含对应文件供页面使用。
同其它Maven项目一样,Maven对Web项目的布局结构也有通用的约定。首先,用户必须为Web项目显式指定打包方式为war。如果不显式指定packaging类型,Maven会使用默认打包方式jar,从而无法正确打包Web项目。
Web项目的类及资源文件同一般Jar项目一样,默认位置相同。Web项目比较特殊的方式在于:它还有一个Web资源目录,默认位置为src/main/webapp/。在该目录下,必须包含一个子目录WEB-INF,该子目录必须包含web.xml文件。src/main/webapp/目录下的其他文件与WAR包中的资源完全一致。
在使用Maven构建Web项目之前,必须理解这种Maven项目结构和WAR包结构的构建关系。有一点要注意,WAR包中必须有一个lib目录包含所有依赖JAR包,但Maven项目结构中没有该目录,因为所有依赖都配置在POM中,Maven在用WAR方式打包时会根据POM配置从本地仓库复制相应的包。
以下是一个account-service模块,用来封装account-email,account-persist和account-captcha三个模块。
POM部分
//account-service的pom.xml
主代码部分
account-service的目的是封装下层细节,对外暴露尽可能简单的接口。先写一个简单的服务接口:
//AccountService.java
//AccountServiceException.java
//SignUpRequest.java
接下来是服务接口的实现类:
//AccountServiceImpl.java
Web项目的目录结构
Web应用的打包方式WAR与JAR类似,但它包含了更多内容,如JSP文件、Servlet、web.xml配置文件、静态web资源(如html,css,js)等。
一个WAR包下至少包含两个子目录:META-INF和WEB-INF。前者包含了一些打包元数据信息,后者是WAR包的核心,WEB-INF下必须包含一个Web资源表述文件web.xml,它的子目录classes包含所有该Web项目的类,而另一个子目录lib则包含了有所有该Web项目的依赖Jar包,classes和lib目录都会在运行时被加入到Classpath中。除了META-INF和WEB-INF外,一般的WAR包都会包含很多Web资源,比如html和jsp文件,有时还有些文件夹如img,css和js,它们包含对应文件供页面使用。
同其它Maven项目一样,Maven对Web项目的布局结构也有通用的约定。首先,用户必须为Web项目显式指定打包方式为war。如果不显式指定packaging类型,Maven会使用默认打包方式jar,从而无法正确打包Web项目。
Web项目的类及资源文件同一般Jar项目一样,默认位置相同。Web项目比较特殊的方式在于:它还有一个Web资源目录,默认位置为src/main/webapp/。在该目录下,必须包含一个子目录WEB-INF,该子目录必须包含web.xml文件。src/main/webapp/目录下的其他文件与WAR包中的资源完全一致。
在使用Maven构建Web项目之前,必须理解这种Maven项目结构和WAR包结构的构建关系。有一点要注意,WAR包中必须有一个lib目录包含所有依赖JAR包,但Maven项目结构中没有该目录,因为所有依赖都配置在POM中,Maven在用WAR方式打包时会根据POM配置从本地仓库复制相应的包。
以下是一个account-service模块,用来封装account-email,account-persist和account-captcha三个模块。
POM部分
//account-service的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> <parent> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>account-service</artifactId> <name>Account Service</name> <properties> <greenmail.version>1.4.1</greenmail.version> </properties> <dependencies> <dependency> <groupId>${project.groupId}</groupId> <artifactId>account-email</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>account-persist</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>account-captcha</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>com.icegreen</groupId> <artifactId>greenmail</artifactId> <version>${greenmail.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <testResources> <testResource> <directory>src/test/resources</directory> <filtering>true</filtering> </testResource> </testResources> </build> </project>与其他模块一样,account-service模块继承自account-parent,它依赖于account-email,account-persist和account-captcha三个模块。由于是同一项目的其他模块,groupId和version都完全一致,因此可以使用Maven属性${project.groupId}和${project.version}进行替换,减少升级项目版本时更改。项目中的其他配置如junit和greenmail依赖是为了方便单元测试。
主代码部分
account-service的目的是封装下层细节,对外暴露尽可能简单的接口。先写一个简单的服务接口:
//AccountService.java
package com.juvenxu.mvnbook.account.service; public interface AccountService { /** * 生成一个验证码的唯一标识符并返回 * * @return 验证码的标识符 * @throws AccountServiceException */ String generateCaptchaKey() throws AccountServiceException; /** * 根据标识符生成验证码图片,以字节流方式返回 * * @param captchaKey * 验证码标识符 * @return 图片字节流 * @throws AccountServiceException */ byte[] generateCaptchaImage(String captchaKey) throws AccountServiceException; /** * 根据注册信息注册 * * @param signUpRequest * 封装的注册信息 * @throws AccountServiceException */ void signUp(SignUpRequest signUpRequest) throws AccountServiceException; /** * 根据激活码激活账户 * * @param activationNumber * 激活码 * @throws AccountServiceException */ void activate(String activationNumber) throws AccountServiceException; /** * 根据用户id和密码进行登录 * * @param id * 用户注册id * @param password * 用户密码 * @throws AccountServiceException */ void login(String id, String password) throws AccountServiceException; }其中的异常类定义如下:
//AccountServiceException.java
package com.juvenxu.mvnbook.account.service; @SuppressWarnings("serial") public class AccountServiceException extends Exception { public AccountServiceException(String message) { super(message); } public AccountServiceException(String message, Throwable throwable) { super(message, throwable); } }其中的SignUpRequest是一个POJO,即简单的Java对象,只包含属性和getter,setter:
//SignUpRequest.java
package com.juvenxu.mvnbook.account.service; public class SignUpRequest { private String id; // 用户id private String email; // 用户邮箱 private String name; // 用户名 private String password; // 账户密码 private String confirmPassword; // 确认密码 private String captchaKey; // 验证码标识符 private String captchaValue; // 验证码值 private String activateServiceUrl; // 激活链接 public String getId() { return id; } public void setId(String id) { this.id = id; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getConfirmPassword() { return confirmPassword; } public void setConfirmPassword(String confirmPassword) { this.confirmPassword = confirmPassword; } public String getCaptchaKey() { return captchaKey; } public void setCaptchaKey(String captchaKey) { this.captchaKey = captchaKey; } public String getCaptchaValue() { return captchaValue; } public void setCaptchaValue(String captchaValue) { this.captchaValue = captchaValue; } public String getActivateServiceUrl() { return activateServiceUrl; } public void setActivateServiceUrl(String activateServiceUrl) { this.activateServiceUrl = activateServiceUrl; } }在AccountService类中,用户需要使用signUp()方法进行注册,注册信息由SignUpRequest进行封装。注册成功后,用户会得到一个激活链接,该连接包含一个激活码,这个时候用户需要使用activate()方法传入激活码以激活账户。最后,login()方法用来登录 。
接下来是服务接口的实现类:
//AccountServiceImpl.java
package com.juvenxu.mvnbook.account.service; import java.util.HashMap; import java.util.Map; import com.juvenxu.mvnbook.account.captcha.AccountCaptchaException; import com.juvenxu.mvnbook.account.captcha.AccountCaptchaService; import com.juvenxu.mvnbook.account.captcha.RandomGenerator; import com.juvenxu.mvnbook.account. b6ea email.AccountEmailException; import com.juvenxu.mvnbook.account.email.AccountEmailService; import com.juvenxu.mvnbook.account.persist.Account; import com.juvenxu.mvnbook.account.persist.AccountPersistException; import com.juvenxu.mvnbook.account.persist.AccountPersistService; public class AccountServiceImpl implements AccountService { private Map<String, String> activationMap = new HashMap<String, String>(); private AccountPersistService accountPersistService; // 账户持久层服务 private AccountEmailService accountEmailService; // 账户邮件服务 private AccountCaptchaService accountCaptchaService; // 账户验证码服务 public AccountPersistService getAccountPersistService() { return accountPersistService; } public void setAccountPersistService( AccountPersistService accountPersistService) { this.accountPersistService = accountPersistService; } public AccountEmailService getAccountEmailService() { return accountEmailService; } public void setAccountEmailService(AccountEmailService accountEmailService) { this.accountEmailService = accountEmailService; } public AccountCaptchaService getAccountCaptchaService() { return accountCaptchaService; } public void setAccountCaptchaService( AccountCaptchaService accountCaptchaService) { this.accountCaptchaService = accountCaptchaService; } public byte[] generateCaptchaImage(String captchaKey) throws AccountServiceException { try { return accountCaptchaService.generateCaptchaImage(captchaKey); } catch (AccountCaptchaException e) { throw new AccountServiceException( "Unable to generate Captcha Image.", e); } } public String generateCaptchaKey() throws AccountServiceException { try { return accountCaptchaService.generateCaptchaKey(); } catch (AccountCaptchaException e) { throw new AccountServiceException( "Unable to generate Captcha key.", e); } } public void signUp(SignUpRequest signUpRequest) throws AccountServiceException { try { // 检查密码是否一致 if (!signUpRequest.getPassword().equals( signUpRequest.getConfirmPassword())) { throw new AccountServiceException("2 passwords do not match."); } // 使用accountCaptchaService检查验证码 if (!accountCaptchaService.validateCaptcha( signUpRequest.getCaptchaKey(), signUpRequest.getCaptchaValue())) { throw new AccountServiceException("Incorrect Captcha."); } // 根据请求中用户信息实例化一个Account对象 Account account = new Account(); account.setId(signUpRequest.getId()); account.setEmail(signUpRequest.getEmail()); account.setName(signUpRequest.getName()); account.setPassword(signUpRequest.getPassword()); account.setActivated(false); // 保存用户信息 accountPersistService.createAccount(account); // 生成随机的激活码并保存在临时的activateMap中 String activationId = RandomGenerator.getRandomString(); activationMap.put(activationId, account.getId()); // 基于该激活码和请求中的服务器URL创建一个激活链接 String link = signUpRequest.getActivateServiceUrl().endsWith("/") ? signUpRequest .getActivateServiceUrl() + activationId : signUpRequest.getActivateServiceUrl() + "?key=" + activationId; // 使用邮件服务将链接发送给用户 accountEmailService.sendMail(account.getEmail(), "Please Activate Your Account", link); } catch (AccountCaptchaException e) { throw new AccountServiceException("Unable to validate captcha.", e); } catch (AccountPersistException e) { throw new AccountServiceException("Unable to create account.", e); } catch (AccountEmailException e) { throw new AccountServiceException( "Unable to send actiavtion mail.", e); } } public void activate(String activationId) throws AccountServiceException { // 根据激活码从临时的activationMap中寻找相应的用户id String accountId = activationMap.get(activationId); // 找不到用户id则抛出异常 if (accountId == null) { throw new AccountServiceException("Invalid account activation ID."); } // 找到用户id则更新账户状态为激活 try { Account account = accountPersistService.readAccount(accountId); account.setActivated(true); accountPersistService.updateAccount(account); } catch (AccountPersistException e) { throw new AccountServiceException("Unable to activate account."); } } public void login(String id, String password) throws AccountServiceException { try { // 根据id读取用户信息 Account account = accountPersistService.readAccount(id); if (account == null) { throw new AccountServiceException("Account does not exist."); } if (!account.isActivated()) { throw new AccountServiceException("Account is disabled."); } if (!account.getPassword().equals(password)) { throw new AccountServiceException("Incorrect password."); } } catch (AccountPersistException e) { throw new AccountServiceException("Unable to log in.", e); } } }
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- GUI - Web前端开发框架
- 介绍一款信息管理系统的开源框架---jeecg
- Extjs4.0 最新最全视频教程
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序