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属性的值配置的。