8.1.2 account-persist的主代码

account-persist的Java主代码位于默认的src/main/java目录,包含Account.java、AccountPersistService.java、AccountPersistServiceImpl.java和AccountPersistException.java四个文件,它们的包名都是com.juvenxu.mvnbook.account.persist,该包名与account-persist的groupId com.juvenxu.mvnbook.account及artifactId account-persist对应。

Account类定义了账户的简单模型,它包含id、name等字段,并为每个字段提供了一组getter和setter方法,见代码清单8-2。

代码清单8-2 Account.java


package com.juvenxu.mvnbook.account.persist;

public class Account

{

private String id;

private String name;

private String email;

private String password;

private boolean activated;

public String getId()

{

return id;

}

public void setId(String id)

{

this.id=id;

}

public String getName()

{

return name;

}

public void setName(String name)

{

this.name=name;

}

//getter and setter methods for email,password and activated

}


account-persist对外提供的服务在接口AccountPersistService中定义,其方法对应了账户的增、删、改、查,见代码清单8-3。

代码清单8-3 AccountPersistService.java


package com.juvenxu.mvnbook.account.persist;

public interface AccountPersistService

{

Account createAccount(Account account)throws AccountPersistException;

Account readAccount(String id)throws AccountPersistException;

Account updateAccount(Account account)throws AccountPersistException;

void deleteAccount(String id)throws AccountPersistException;

}


当增、删、改、查操作发生异常的时候,该服务则抛出AccountPersistException。

AccountPersistService对应的实现为AccountPersistServiceImpl,它通过操作XML文件实现账户数据的持久化。首先看一下该类的两个私有方法:readDocument()和writeDocument(),见代码清单8-4。

代码清单8-4 AccountPersistServiceImpl.java第1部分


private String file;

private SAXReader reader=new SAXReader();

private Document readDocument()throws AccountPersistException

{

File dataFile=new File(file);

if(!dataFile.exists())

{

dataFile.getParentFile().mkdirs();

Document doc=DocumentFactory.getInstance().createDocument();

Element rootEle=doc.addElement(ELEMENT_ROOT);

rootEle.addElement(ELEMENT_ACCOUNTS);

writeDocument(doc);

}

try

{

return reader.read(new File(file));

}

catch(DocumentException e)

{

throw new AccountPersistException("Unable to read persist data xml",e);

}

}

private void writeDocument(Document doc)throws AccountPersistException

{

Writer out=null;

try

{

out=new OutputStreamWriter(new FileOutputStream(file),"utf-8");

XMLWriter writer=newk XMLWriter(out,OutputFormat.createPrettyPrint());

writer.write(doc);

}

catch(IOException e)

{

throw new AccountPersistException("Unable to write persist data xml",e);

}

finally

{

try

{

if(out!=null)

{

out.close();

}

}

catch(IOException e)

{

throw new AccountPersistException("Unable to close persist data xml

writer",e);

}

}

}


先看writeDocument()方法。该方法首先使用变量file构建一个文件输出流,file是AccountPersistServiceImpl的一个私有变量,它的值通过SpringFramework注入。得到输出流后,该方法再使用DOM4J创建一个XMLWriter,这里的OutputFormat.createPrettyPrint()用来创建一个带缩进及换行的友好格式。得到XMLWriter后,就调用其write()方法,将Document写入到文件中。该方法的其他代码用做处理流的关闭及异常处理。

readDocument()方法与writeDocument()对应,它负责从文件中读取XML数据,也就是Document对象。不过,在这之前,该方法首先会检查文件是否存在,如果不存在,则需要初始化一个XML文档,于是借助DocumentFactory创建一个Document对象,接着添加XML元素,再把这个不包含任何账户数据的XML文档写入到文件中。如果文件已经被初始化了,则该方法使用SAXReader读取文件至Document对象。

用来存储账户数据的XML文件结构十分简单,如下是一个包含一个账户数据的文件,见代码清单8-5。

代码清单8-5 账户数据的XML文件


<?xml version="1.0"encoding="UTF-8"?>

<account-persist>

<accounts>

<account>

<id>juven</id>

<name>Juven Xu</name>

<email>juven@changeme.com</email>

<password>this_should_be_encrypted</password>

<activated>false</activated>

</account>

</accounts>

</account-persist>


这个XML文件的根元素是account-persist,其下是accounts元素,accounts可以包含零个或者多个account元素,每个account元素代表一个账户,其子元素表示该账户的id、姓名、电子邮件、密码以及是否被激活等信息。

现在看一下readAccount()方法是如何从XML文档读取并构建Account对象的,见代码清单8-6。

代码清单8-6 AccountPersistServiceImpl.java第2部分


public Account readAccount(String id)throws AccountPersistException

{

Document doc=readDocument();

Element accountsEle=doc.getRootElement().element(ELEMENT_ACCOUNTS);

for(Element accountEle:(List<Element>)accountsEle.elements())

{

if(accountEle.elementText(ELEMENT_ACCOUNT_ID).equals(id))

{

return buildAccount(accountEle);

}

}

return null;

}

private Account buildAccount(Element element)

{

Account account=new Account();

account.setId(element.elementText(ELEMENT_ACCOUNT_ID));

account.setName(element.elementText(ELEMENT_ACCOUNT_NAME));

account.setEmail(element.elementText(ELEMENT_ACCOUNT_EMAIL));

account.setPassword(element.elementText(ELEMENT_ACCOUNT_PASSWORD));

account.setActivated(("true".equals(element.elementText(ELEMENTACCOUNT

ACTIVATED))?true:false));

return account;

}


readAccount()方法首先获取XML文档的Document对象,接着获取根元素的accounts子元素,这里的ELEMENT_ACCOUNTS是一个静态常量,其值就是accounts。接着遍历accounts的子元素,如果当前子元素的id与要读取的账户的id一致,并且基于该子元素构建Account对象,这也就是buildAccount()方法。

在buildAccount()方法中,先创建一个Account对象,然后当前XML元素的子元素的值设置该对象。Element的elementText()方法能够根据子元素名称返回子元素的值,与ELEMENT_ACCOUNTS类似,这里使用了一些静态常量表示id、name、email等XML中的元素名称。Account对象设置完后就直接返回,如果XML文档中没有匹配的id,则返回null。

为了使本章内容不致过于冗长,这里就不再介绍createAccount()、updateAccount()和deleteAccount()几个方法的实现。感兴趣的读者可以参照DOM4J的文档实现这几个方法,过程应该非常简单。

除了Java代码,account-persist模块还需要一个SpringFramework的配置文件,它位于src/main/resources目录,其内容见代码清单8-7。

代码清单8-7 account-persist的Spring配置文件


<?xml version="1.0"encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="propertyConfigurer"

class="org.springframework.beans.factory.config.PropertyPlaceholderCo-

nfigurer">

<property name="location"value="classpath:account-service.proper-

ties"/>

</bean>

<bean id="accountPersistService"

class="com.juvenxu.mvnbook.account.persist.AccountPersistServiceI-

mpl">

<property name="file"value="${persist.file}"/>

</bean>

</beans>


该配置文件首先配置了一个id为propertyConfigurer的bean,其实现为PropertyPlace-holderConfigurer,作用是从项目classpath载入名为account-service.properties的配置文件。随后的bean是accountPersistService,实现为AccountPersistServiceImpl,同时这里使用属性persist.file配置其file字段的值。也就是说,XML数据文档的位置是由项目classpath下account-serv-ice.properties文件中persist.file属性的值配置的。