上篇account-service中已经封装了实现细节,所以接下来只要在此次基础上提供Web页面,并使用简单servlet,jsp与后台实现交互控制。以下是account-web模块的构成:
- POM部分
<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-web</artifactId> <packaging>war</packaging> <name>Account Web</name> <dependencies> <dependency> <groupId>${project.groupId}</groupId> <artifactId>account-service</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.1.7.RELEASE</version> </dependency> </dependencies> </project>
上述代码中,account-web的packaging元素值为war,表示这是一个Web项目,需要以war方式打包。account-web依赖于servlet-api和jsp-api这两个几乎所有Web项目都要依赖的包,它们为servlet和jsp的编写提供支持。这里依赖范围是provided,表示它们不会被打包到war中,这是因为几乎所有Web容器都会提供这两个类库,如果war包中重复出现,就会导致依赖冲突等问题。account-web还依赖于account-service和spring-web,前者为Web应用提供底层支持,后者为Web应用提供Spring的集成支持。
在一些Web项目中,可能会有finalName的配置。该元素用来标识项目生成的主构件名称,该元素默认值已在超级POM中设定,值为${project.artifactId}-${project.version}。我们可以通过<finalName>account</finalName>的配置修改主构件名称为account,之后项目生成的war包名称就会成为account.war,更方便部署。
- 主代码部分
主代码包含了2个JSP页面和4个Servlet,分别是:
- signup.jsp:账户注册页面
- login.jsp:账户登陆页面
- CaptchaImageServlet:生成验证码图片的Servlet
- LoginServlet:处理账户注册请求
- ActivateServlet:处理账户激活
- LoginServlet
不过首先要在web.xml中配置Servlet,该文件位于src/main/webapp/WEB-INF/目录下。配置如下:
//web.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>Account Service</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:/account-persist.xml classpath:/account-captcha.xml classpath:/account-email.xml classpath:/account-service.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>CaptchaImageServlet</servlet-name> <servlet-class>com.juvenxu.mvnbook.account.web.CaptchaImageServlet</servlet-class> </servlet> <servlet> <servlet-name>ActivateServlet</servlet-name> <servlet-class>com.juvenxu.mvnbook.account.web.ActivateServlet</servlet-class> </servlet> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.juvenxu.mvnbook.account.web.LoginServlet</servlet-class> </servlet> <servlet> <servlet-name>SignUpServlet</servlet-name> <servlet-class>com.juvenxu.mvnbook.account.web.SignUpServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CaptchaImageServlet</servlet-name> <url-pattern>/captcha_image</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>SignUpServlet</servlet-name> <url-pattern>/signup</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ActivateServlet</servlet-name> <url-pattern>/activate</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> </web-app>
该web.xml首先配置了Web项目的显示名称,接着是一个名为ContextLoaderListener的ServletListener。该Listener来自于spring-web,用来为Web项目启动Spring的IoC容器,从而实现Bean的注入。名为contextConfigLocation的caontext-param则用来为指定的Spring配置文件的位置。这里的值是四个模块的Spring配置XML文件,例如classpath:/account-persist.xml表示从classpath的根路径获取名为account-persist.xml的文件。我们知道account-persist.xml文件在account-persist模块打包后的根路径下,这一JAR文件通过依赖的方式被引入到account-web的classpath下。web.xml中的其余部分是Servlet,包括各个Servlet的名称,类名以及相应的URL模式。
下面再构建视图文件signup.jsp文件,用来显示账户注册页面,存放在src/main/webapp/目录下。
<%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%> <%@ page import="org.springframework.web.context.support.WebApplicationContextUtils"%> <%@ page import="org.springframework.web.context.WebApplicationContext"%> <%@ page import="org.springframework.context.ApplicationContext"%> <%@ page import="com.juvenxu.mvnbook.account.service.*"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html> <html> <head> <base href="<%=basePath%>"> <title>SignUp Page</title> <style type="text/css"> .text-field { position: absolute; left: 40%; background-color: rgb(255, 230, 220); } label { display: inline-table; width: 90px; margin: 0px 0px 10px 20px; } input { display: inline-table; width: 150px; margin: 0px 20px 10px 0px; } img { width: 150px; margin: 0px 20px 10px 110px; } h2 { margin: 20px 20px 20px 40px; } button { margin: 20px 20px 10px 110px } </style> </head> <body> <% //引入Spring的ApplicationContext类 ApplicationContext context = WebApplicationContextUtils .getWebApplicationContext(getServletContext()); //加载后台的AccountService对象 AccountService accountService = (AccountService) context .getBean("accountService"); //使用该对象生成一个验证码的key String captchaKey = accountService.generateCaptchaKey(); %> <div class="text-field"> <h2>注册新账户</h2> <form name="signup" action="signup" method="post"> <label>账户ID:</label> <input type="text" name="id"/><br/> <label>Email:</label> <input type="text" name="email"/><br/> <label>显示名称:</label> <input type="text" name="name"/><br/> <label>密码:</label> <input type="password" name="password"/><br/> <label>确认密码:</label> <input type="password" name="confirm_password"/><br/> <label>验证码:</label> <input type="text" name="captcha_value"/><br/> <input type="hidden" name="captcha_key" value="<%=captchaKey%>"/> <img src="<%=request.getContextPath()%>/captcha_image?key=<%=captchaKey%>"/><br/> <button>确认并提交</button> </form> </div> </body> </html>
上述JSP中使用/captcha_image这一资源获取验证码图片。根据web.xml,该资源对应于CaptchaImageServlet,所有Servlet都放在src/main/java/目录下,代码如下:
//CaptchaImageServlet.java
package com.juvenxu.mvnbook.account.web; import java.io.IOException; import java.io.OutputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import com.juvenxu.mvnbook.account.service.AccountService; import com.juvenxu.mvnbook.account.service.AccountServiceException; @SuppressWarnings("serial") public class CaptchaImageServlet extends HttpServlet { private ApplicationContext context; // Spring的ApplicationContext @Override /** * 首先初始化ApplicationContext */ public void init() throws ServletException { super.init(); context = WebApplicationContextUtils .getWebApplicationContext(getServletContext()); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取key参数 String key = request.getParameter("key"); // 检查key是否为空 if (key == null || key.length() == 0) { // 返回HTTP400错误,请求不合法 response.sendError(400, "No Captcha Key Found"); } else { // 获取Spring Bean并强制类型转换 AccountService service = (AccountService) context .getBean("accountService"); try { // 设置response格式为image/jpeg response.setContentType("image/jpeg"); // 将产生的字节流写入到Servlet的输出流中 OutputStream out = response.getOutputStream(); out.write(service.generateCaptchaImage(key)); out.close(); } catch (AccountServiceException e) { response.sendError(400, e.getMessage()); } } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
而signup.jsp中form表单的action是signup,根据web.xml对应于SignUpServlet,代码如下:
//SignUpServlet.java
package com.juvenxu.mvnbook.account.web; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import com.juvenxu.mvnbook.account.service.AccountService; import com.juvenxu.mvnbook.account.service.AccountServiceException; import com.juvenxu.mvnbook.account.service.SignUpRequest; @SuppressWarnings("serial") public class SignUpServlet extends HttpServlet { private ApplicationContext context; // 用于获取Spring Bean @Override /** * 首先初始化ApplicationContext */ public void init() throws ServletException { super.init(); context = WebApplicationContextUtils .getWebApplicationContext(getServletContext()); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 获取HTTP POST请求,读取表单中的id,邮箱,用户名,密码,确认密码,验证主键,验证值参数 String id = req.getParameter("id"); String email = req.getParameter("email"); String name = req.getParameter("name"); String password = req.getParameter("password"); String confirmPassword = req.getParameter("confirm_password"); String captchaKey = req.getParameter("captcha_key"); String captchaValue = req.getParameter("captcha_value"); // 判断各参数是否为空,存在为空值则返回报错 if (id == null || id.length() == 0 || email == null || email.length() == 0 || name == null || name.length() == 0 || password == null || password.length() == 0 || confirmPassword == null || confirmPassword.length() == 0 || captchaKey == null || captchaKey.length() == 0 || captchaValue == null || captchaValue.length() == 0) { resp.sendError(400, "Parameter Incomplete."); return; } // 获取名为accountService的bean AccountService service = (AccountService) context .getBean("accountService"); // 初始化一个SignUpRequest实例并设置其属性 SignUpRequest request = new SignUpRequest(); request.setId(id); request.setEmail(email); request.setName(name); request.setPassword(password); request.setConfirmPassword(confirmPassword); request.setCaptchaKey(captchaKey); request.setCaptchaValue(captchaValue); request.setActivateServiceUrl(getServletContext().getRealPath("/") + "activate"); // 发送账户激活邮件的地址,这里是ActivateServlet的地址 // 使用AccountService注册用户 try { service.signUp(request); resp.getWriter() .print("Account is created, please check your mail box for activation link."); } catch (AccountServiceException e) { resp.sendError(400, e.getMessage()); return; } } }
在这里又用到了ActivateServlet,代码如下:
//ActivateServlet
package com.juvenxu.mvnbook.account.web; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import com.juvenxu.mvnbook.account.service.AccountService; import com.juvenxu.mvnbook.account.service.AccountServiceException; @SuppressWarnings("serial") public class ActivateServlet extends HttpServlet { private ApplicationContext context; @Override public void init() throws ServletException { super.init(); context = WebApplicationContextUtils .getWebApplicationContext(getServletContext()); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String key = req.getParameter("key"); // 判断激活码是否为空 if (key == null || key.length() == 0) { resp.sendError(400, "No activation key provided."); return; } AccountService service = (AccountService) context .getBean("accountService"); // 激活账户 try { service.activate(key); resp.getWriter().write("Account is activated, now you can login."); } catch (AccountServiceException e) { resp.sendError(400, "Unable to activate account"); return; } } }
以上是注册页面及相关逻辑处理的实现。另外一个页面是login.jsp,负责处理登录。
<%@ page contentType="text/html; charset=UTF-8" language="java"%> <html> <head> <style type="text/css"> .text-field { position: absolute; left: 40%; background-color: rgb(255, 230, 220); } label { display: inline-table; width: 90px; margin: 0px 0px 10px 20px; } input { display: inline-table; width: 150px; margin: 0px 20px 10px 0px; } h2 { margin: 20px 20px 20px 40px; } button { margin: 20px 20px 10px 110px } </style> </head> <body> <div class="text-field"> <h2>账户登录</h2> <form name="login" action="login" method="post"> <label>账户ID:</label> <input type="text" name="id"/><br/> <label>密码:</label> <input type="password" name="password"/><br/> <button>确认并提交</button> </form> </div> </body> </html>
表单的action是login,LoginServlet代码如下:
//LoginServlet.java
package com.juvenxu.mvnbook.account.web; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import com.juvenxu.mvnbook.account.service.AccountService; import com.juvenxu.mvnbook.account.service.AccountServiceException; @SuppressWarnings("serial") public class LoginServlet extends HttpServlet { private ApplicationContext context; @Override public void init() throws ServletException { super.init(); context = WebApplicationContextUtils .getWebApplicationContext(getServletContext()); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String id = req.getParameter("id"); String password = req.getParameter("password"); if (id == null || id.length() == 0 || password == null || password.length() == 0) { resp.sendError(400, "incomplete parameter"); return; } AccountService service = (AccountService) context .getBean("accountService"); try { service.login(id, password); resp.getWriter().print("Login Successful!"); } catch (AccountServiceException e) { resp.sendError(400, e.getMessage()); } } }
由于代码格式及内容和上面的非常相似,所以这里省略了注释,大部分代码可以参考SignUpServlet。
最后在在src/main/resources/目录下建立一个配置文件,account-service.properties,具体配置参见前面邮箱的配置,示例如下
//account.service.properties
email.protocol=smtp email.host=localhost email.port=25 [email protected] email.password=123456 email.auth=true [email protected] persist.file=C\:/persist-data.xml
这样一个maven的Web项目就构建完了。在account-web的目录上执行mvn clean install,该项目的WAR包就被打包到了本地仓库上。
参考书籍:《Maven实战》第12章——许晓斌著
版权声明:本文为博主原创文章,未经博主允许不得转载。