12.2.2 account-service的主代码
account-service的目的是封装下层细节,对外暴露尽可能简单的接口。先看一下这个接口是怎样的,见代码清单12-3。
代码清单12-3 AccountService.java
package com.juvenxu.mvnbook.account.service;
public interface AccountService
{
String generateCaptchaKey()
throws AccountServiceException;
byte[]generateCaptchaImage(String captchaKey)
throws AccountServiceException;
void signUp(SignUpRequest signUpRequest)
throws AccountServiceException;
void activate(String activationNumber)
throws AccountServiceException;
void login(String id,String password)
throws AccountServiceException;
}
正如4.3.1节介绍的那样,该接口提供5个方法。generateCaptchaKey()用来生成一个验证码的唯一标识符。generateCaptchaImage()根据这个标识符生成验证码图片,图片以字节流的方式返回。用户需要使用signUp()方法进行注册,注册信息使用SignUpRequest进行封装,这个SignUpRequest类是一个简单的POJO,它包含了注册ID、email、用户名、密码、验证码标识、验证码值等信息[1]。注册成功之后,用户会得到一个激活链接,该链接包含了一个激活码,这个时候用户需要使用activate()方法并传入激活码以激活账户。最后,login()方法用来登录。
下面来看一下该接口的实现类AccountServiceImpl.java。首先它需要使用3个底层模块的服务,如代码清单12-4所示。
代码清单12-4 AccountServiceImpl.java第1部分
public class AccountServiceImpl
implements AccountService
{
private AccountPersistService accountPersistService;
private AccountEmailService accountEmailService;
private AccountCaptchaService accountCaptchaService;
public AccountPersistService getAccountPersistService()
{
return accountPersistService;
}
public void setAccountPersistService(AccountPersistService accountPersist-
Service)
{
this.accountPersistService=accountPersistService;
}
……
}
三个私有变量来自account-persist、account-email和account-captcha模块,它们都有各自的get()和set()方法,并且通过Spring注入。
AccountServiceImpl.java借助accountCaptchaService实现验证码的标识符生成及验证码图片生成,如代码清单12-5所示。
代码清单12-5 AccountServiceImpl.java第2部分
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);
}
}
稍微复杂一点的是signUp()方法的实现,见代码清单12-6。
代码清单12-6 AccountServiceImpl.java第3部分
private Map<String,String>activationMap=new HashMap<String,String>();
public void signUp(SignUpRequest signUpRequest)
throws AccountServiceException
{
try
{
if(!signUpRequest.getPassword().equals(signUpRequest.getConfirmPassword()))
{
throw new AccountServiceException("2 passwords do not match.");
}
if(!accountCaptchaService
.validateCaptcha(signUpRequest.getCaptchaKey(),signUpRequest.
getCaptchaValue()))
{
throw new AccountServiceException("Incorrect Captcha.");
}
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);
String activationId=RandomGenerator.getRandomString();
activationMap.put(activationId,account.getId());
String link=signUpRequest.getActivateServiceUrl().endsWith("/")?sign-
UpRequest.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);
}
}
signUp()方法首先检查请求中的两个密码是否一致,接着使用accountCaptchaService检查验证码,下一步使用请求中的用户信息实例化一个Account对象,并使用accountPersist-Service将用户信息保存。下一步是生成一个随机的激活码并保存在临时的activateMap中,然后基于该激活码和请求中的服务器URL创建一个激活链接,并使用accountEmailService将该链接发送给用户。如果其中任何一步发生异常,signUp()方法会创建一个一致的AccountServiceExcpetion对象,提供并抛出对应的异常提示信息。
最后再看一下相对简单的activate()和login()方法,见代码清单12-7。
代码清单12-7 AccountServiceImpl.java第4部分
public void activate(String activationId)
throws AccountServiceException
{
String accountId=activationMap.get(activationId);
if(accountId==null)
{
throw new AccountServiceException("Invalid account activation 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
{
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);
}
}
activate()方法仅仅是简单根据激活码从临时的activationMap中寻找对应的用户ID,如果找到就更新账户状态为激活。login()方法则是根据ID读取用户信息,检查其是否为激活,并比对密码,如果有任何错误则抛出异常。
除了上述代码之外,account-service还包括一些Spring配置文件和单元测试代码,这里就不再详细介绍。有兴趣的读者可以自行下载阅读。
[1]由于篇幅的原因,这里不再给出源代码,有兴趣的读者可以自行下载并查看本书源码。