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]由于篇幅的原因,这里不再给出源代码,有兴趣的读者可以自行下载并查看本书源码。