微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

【SSH网上商城项目实战02】基本增删查改、Service和Action的抽取以及使用注解替换xml

上一节我们搭建好了Struts2、hibernatespring的开发环境,并成功将它们整合在一起。这节主要完成一些基本的增删改查以及Service、Dao和Action的抽取。

1. Service层的抽取

上一节中,我们在service层简单写了save和update方法,这里我们开始完善该部分的代码,然后对service层的代码进行抽取。

1.1完善CategoryService层

数据库的操作无非是增删改查,首先我们来完善CategoryService层的接口和实现:

  1. //CategoryService接口
  2. publicinterfaceCategoryServiceextendsBaseService<Category>{
  3. publicvoidsave(Categorycategory);//插入
  4. publicvoidupdate(Categorycategory);//更新
  5. publicvoiddelete(intid);//删除
  6. publicCategoryget(intid);//获取一个Category
  7. publicList<Category>query();//获取全部Category
  8. }
//CategoryService接口
public interface CategoryService extends BaseService<Category> {

    public void save(Category category); //插入

    public void update(Category category);//更新

    public void delete(int id); //删除

    public Category get(int id); //获取一个Category

    public List<Category> query(); //获取全部Category

}
对CategoryService接口的具体实现:
  1. publicclassCategoryServiceImplextendsBaseServiceImpl<Category>implementsCategoryService{
  2. privateSessionFactorysessionFactory;
  3. //Spring会注进来
  4. publicvoidsetSessionFactory(SessionFactorysessionFactory){
  5. this.sessionFactory=sessionFactory;
  6. }
  7. protectedSessiongetSession(){
  8. //从当前线程获取session,如果没有则创建一个新的session
  9. returnsessionFactory.getCurrentSession();
  10. }
  11. @Override
  12. publicvoidsave(Categorycategory){
  13. getSession().save(category);
  14. }
  15. @Override
  16. publicvoidupdate(Categorycategory){
  17. getSession().update(category);
  18. }
  19. @Override
  20. publicvoiddelete(intid){
  21. /*第一种方法有个弊端,就是没删除一次得先查询一次
  22. Objectobj=getSession().get(Category.class,id);
  23. if(obj!=null){
  24. getSession().delete(obj);
  25. }*/
  26. Stringhql=”deleteCategorywhileid=:id”;
  27. getSession().createquery(hql)//
  28. .setInteger(”id”,id)//
  29. .executeUpdate();
  30. }
  31. @Override
  32. publicCategoryget(intid){
  33. return(Category)getSession().get(Category.class,id);
  34. }
  35. @Override
  36. publicList<Category>query(){
  37. Stringhql=”fromCategory”;
  38. returngetSession().createquery(hql).list();
  39. }
  40. }
public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {

    private SessionFactory sessionFactory;

    //Spring会注进来
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    protected Session getSession() {
        //从当前线程获取session,如果没有则创建一个新的session
        return sessionFactory.getCurrentSession();
    }

    @Override 
    public void save(Category category) {
        getSession().save(category);
    }

    @Override 
    public void update(Category category) {
        getSession().update(category);  
    }

    @Override
    public void delete(int id) {
        /*第一种方法有个弊端,就是没删除一次得先查询一次
        Object obj = getSession().get(Category.class,id);
        if(obj != null) {
            getSession().delete(obj);
        }*/
        String hql = "delete Category while id=:id";
        getSession().createquery(hql) //
                .setInteger("id",id) //
                .executeUpdate();
    }

    @Override
    public Category get(int id) {
        return (Category) getSession().get(Category.class,id);
    }

    @Override
    public List<Category> query() {
        String hql = "from Category";
        return getSession().createquery(hql).list();
    }
}

1.2 Service层抽取实现

完成了CategoryService后,我们来抽取Service层的基础实现。思路是这样的:我们抽取一个基础接口BaseService以及基础接口的实现BaseServiceImpl,后面开发的时候,如果需要新的Service,只需要做两步即可:首先定义一个新的接口xxxService继承BaseService接口,这个接口可以增加新的抽象方法;然后定义一个新的实现类xxxServiceImpl继承BaseServiceImpl并实现xxxService接口即可。这样更加便于项目的维护。

我们先根据上面的CategoryService接口来创建BaseService接口:

  1. //基础接口BaseService,使用泛型
  2. publicinterfaceBaseService<T>{
  3. publicvoidsave(Tt);
  4. publicvoidupdate(Tt);
  5. publicvoiddelete(intid);
  6. publicTget(intid);
  7. publicList<T>query();
  8. }
//基础接口BaseService,使用泛型
public interface BaseService<T> {
    public void save(T t);

    public void update(T t);

    public void delete(int id);

    public T get(int id);

    public List<T> query();
}
然后再根据CategoryServiceImpl实现类创建BaseService接口的实现类BaseServiceImpl:
  1. /**
  2. *@DescriptionTodo(公共模块的抽取)
  3. *@authoreson_15
  4. *
  5. */
  6. @SuppressWarnings(“unchecked”)
  7. publicclassBaseServiceImpl<T>implementsBaseService<T>{
  8. privateClassclazz;//clazz中存储了当前操作的类型,即泛型T
  9. privateSessionFactorysessionFactory;
  10. publicBaseServiceImpl(){
  11. //下面三个打印信息可以去掉,这里是给自己看的
  12. System.out.println(”this代表的是当前调用构造方法的对象”+this);
  13. System.out.println(获取当前this对象的父类信息”+this.getClass().getSuperclass());
  14. System.out.println(获取当前this对象的父类信息(包括泛型信息)”+this.getClass().getGenericSuperclass());
  15. //拿到泛型的参数类型
  16. ParameterizedTypetype=(ParameterizedType)this.getClass().getGenericSuperclass();
  17. clazz=(Class)type.getActualTypeArguments()[0];
  18. }
  19. publicvoidsetSessionFactory(SessionFactorysessionFactory){
  20. this.sessionFactory=sessionFactory;
  21. }
  22. protectedSessiongetSession(){
  23. //从当前线程获取session,如果没有则创建一个新的session
  24. returnsessionFactory.getCurrentSession();
  25. }
  26. @Override
  27. publicvoidsave(Tt){
  28. getSession().save(t);
  29. }
  30. @Override
  31. publicvoidupdate(Tt){
  32. getSession().update(t);
  33. }
  34. @Override
  35. publicvoiddelete(intid){
  36. System.out.println(clazz.getSimpleName());
  37. Stringhql=”delete”+clazz.getSimpleName()+“ascwherec.id=:id”;
  38. getSession().createquery(hql)//
  39. .setInteger(”id”,id)//
  40. .executeUpdate();
  41. }
  42. @Override
  43. publicTget(intid){
  44. return(T)getSession().get(clazz,id);
  45. }
  46. @Override
  47. publicList<T>query(){
  48. Stringhql=”from”+clazz.getSimpleName();
  49. returngetSession().createquery(hql).list();
  50. }
  51. }
/**
 * @Description Todo(公共模块的抽取)
 * @author eson_15
 *
 */
@SuppressWarnings("unchecked")
public class BaseServiceImpl<T> implements BaseService<T> {

    private Class clazz; //clazz中存储了当前操作的类型,即泛型T
    private SessionFactory sessionFactory;

    public BaseServiceImpl() {
                //下面三个打印信息可以去掉,这里是给自己看的
               System.out.println("this代表的是当前调用构造方法的对象" + this);
        System.out.println("获取当前this对象的父类信息" + this.getClass().getSuperclass());
        System.out.println("获取当前this对象的父类信息(包括泛型信息)" + this.getClass().getGenericSuperclass());
        //拿到泛型的参数类型
        ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
        clazz = (Class)type.getActualTypeArguments()[0];
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    protected Session getSession() {
        //从当前线程获取session,如果没有则创建一个新的session
        return sessionFactory.getCurrentSession();
    }

    @Override
    public void save(T t) {
        getSession().save(t);
    }

    @Override
    public void update(T t) {
        getSession().update(t); 
    }

    @Override
    public void delete(int id) {
        System.out.println(clazz.getSimpleName());
        String hql = "delete " + clazz.getSimpleName() + " as c where c.id=:id";
        getSession().createquery(hql) //
                  .setInteger("id",id) //
                  .executeUpdate();
    }

    @Override
    public T get(int id) {
        return (T) getSession().get(clazz,id);
    }

    @Override
    public List<T> query() {
        String hql = "from " + clazz.getSimpleName();
        return getSession().createquery(hql).list();
    }

}
抽取完了后,我们就可以改写CategoryService接口和CategoryServiceImpl实现类了。如下:
  1. //CategoryService接口继承BaseService接口
  2. publicinterfaceCategoryServiceextendsBaseService<Category>{
  3. /*
  4. *只要添加CategoryService本身需要的新的方法即可,公共方法已经在BaseService中了
  5. */
  6. }
  7. /**
  8. *@DescriptionTodo(模块自身的业务逻辑)
  9. *@authoreson_15
  10. *
  11. */
  12. publicclassCategoryServiceImplextendsBaseServiceImpl<Category>implementsCategoryService{
  13. /*
  14. *只需实现CategoryService接口中新增的方法即可,公共方法已经在BaseServiceImpl中实现了
  15. */
  16. }
//CategoryService接口继承BaseService接口
public interface CategoryService extends BaseService<Category> {
    /*
     * 只要添加CategoryService本身需要的新的方法即可,公共方法已经在BaseService中了
     */
}

/**
* @Description Todo(模块自身的业务逻辑)
* @author eson_15
*
*/
public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {

 /*
  * 只需实现CategoryService接口中新增的方法即可,公共方法已经在BaseServiceImpl中实现了
  */
}
代码中可以看出,新增的Service只需要继承BaseService接口,然后在接口中新增本Service所需要的业务逻辑即可。新增的ServiceImpl只需要继承BaseServiceImpl并实现新增的业务逻辑即可。
但是别忘了很重要的一点:就是修改Spring的配置文件beans.xml中的bean
  1. <!–泛型类是不能实例化的,所以要加lazy-init属性–>
  2. <beanid=“baseService”class=“cn.it.shop.service.impl.BaseServiceImpl”lazy-init=“true”>
  3. <propertyname=“sessionFactory”ref=“sessionFactory”/>
  4. </bean>
  5. <beanid=“categoryService”class=“cn.it.shop.service.impl.CategoryServiceImpl”parent=“baseService”/>
<!-- 泛型类是不能实例化的,所以要加lazy-init属性 -->
<bean id="baseService" class="cn.it.shop.service.impl.BaseServiceImpl" lazy-init="true">
     <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="categoryService" class="cn.it.shop.service.impl.CategoryServiceImpl" parent="baseService"/>
将原来categoryService中的property干掉,然后增加parent属性,指明继承baseService;然后配置一下baseService,将sessionFactory配到baseService中去,另外要注意一点:设置lazy-init属性为true,因为baseService是泛型类,泛型类是不能实例化的。至此,Service层的抽取就搞定了。

2. Service层添加一个Account

刚刚抽取好了Service层,那么现在我们想写一个Account(管理员)的service就很简单了:

首先写一个AccountService接口继承BaseService:

  1. publicinterfaceAccountServiceextendsBaseService<Account>{//注意BaseService里的泛型现在是Account
  2. /*
  3. *只要添加AccountService本身需要的新的方法即可,公共方法已经在BaseService中了
  4. */
  5. }
public interface AccountService extends BaseService<Account> { //注意BaseService里的泛型现在是Account
    /*
     * 只要添加AccountService本身需要的新的方法即可,公共方法已经在BaseService中了
     */
}
然后写一个AccountServiceImpl实现类继承BaseServiceImpl实现类,并实现AccountService接口即可:
  1. publicclassAccountServiceImplextendsBaseServiceImpl<Account>implementsAccountService{
  2. /*
  3. *只需实现AccountService接口中新增的方法即可,公共方法已经在BaseServiceImpl中实现了
  4. */
  5. //管理登陆功能,后期再完善
  6. }
public class AccountServiceImpl extends BaseServiceImpl<Account> implements AccountService {

    /*
     * 只需实现AccountService接口中新增的方法即可,公共方法已经在BaseServiceImpl中实现了
     */

    //管理登陆功能,后期再完善
}
最后在beans.xml文件加上如下配置:
  1. <beanid=“accountService”class=“cn.it.shop.service.impl.AccountServiceImpl”parent=“baseService”/>
<bean id="accountService" class="cn.it.shop.service.impl.AccountServiceImpl" parent="baseService" />
这样就写好了一个新的service了,以后需要添加service就遵循这个流程,非常方便。

3. Action的抽取

3.1 Action中往域(request,session,application等)中存数据

我们知道,在Action中可以直接通过ActionContext.getContext()去获取一个ActionContext对象,然后通过该对象再去获得相应的域对象;也可以通过实现xxxAware接口来注入相应的域对象。我们先来看一下这两种方法

  1. publicclassCategoryActionextendsActionSupportimplementsRequestAware,SessionAware,ApplicationAware{
  2. privateCategorycategory;
  3. privateCategoryServicecategoryService;
  4. publicvoidsetCategoryService(CategoryServicecategoryService){
  5. this.categoryService=categoryService;
  6. }
  7. publicStringupdate(){
  8. System.out.println(”—-update—-“);
  9. categoryService.update(category);
  10. return“index”;
  11. }
  12. publicStringsave(){
  13. System.out.println(”—-save—-“);
  14. return“index”;
  15. }
  16. publicStringquery(){
  17. //解决方案一,采用相应的map取代原来的内置对象,这样与jsp没有依赖,但是代码量比较大
  18. //ActionContext.getContext().put(“categoryList”,categoryService.query());//放到request域中
  19. //ActionContext.getContext().getSession().put(“categoryList”,categoryService.query());//放到session域中
  20. //ActionContext.getContext().getApplication().put(“categoryList”,categoryService.query());//放到application域中
  21. //解决方案二,实现相应的接口(RequestAware,ApplicationAware),让相应的map注入
  22. request.put(”categoryList”,categoryService.query());
  23. session.put(”categoryList”,categoryService.query());
  24. application.put(”categoryList”,categoryService.query());
  25. return“index”;
  26. }
  27. publicCategorygetCategory(){
  28. returncategory;
  29. }
  30. publicvoidsetCategory(Categorycategory){
  31. this.category=category;
  32. }
  33. privateMap<String,Object>request;
  34. privateMap<String,Object>session;
  35. privateMap<String,Object>application;
  36. @Override
  37. publicvoidsetApplication(Map<String,Object>application){
  38. this.application=application;
  39. }
  40. @Override
  41. publicvoidsetSession(Map<String,Object>session){
  42. this.session=session;
  43. }
  44. @Override
  45. publicvoidsetRequest(Map<String,Object>request){
  46. this.request=request;
  47. }
  48. }
public class CategoryAction extends ActionSupport implements RequestAware,ApplicationAware{

    private Category category;

     private CategoryService categoryService;
 
     public void setCategoryService(CategoryService categoryService) {
      this.categoryService = categoryService;
     }

    public String update() {
        System.out.println("----update----");
        categoryService.update(category);
        return "index";
    }

    public String save() {
        System.out.println("----save----");
        return "index";
    }

    public String query() {
         //解决方案一,采用相应的map取代原来的内置对象,这样与jsp没有依赖,但是代码量比较大
 //     ActionContext.getContext().put("categoryList",categoryService.query()); //放到request域中
 //     ActionContext.getContext().getSession().put("categoryList",categoryService.query()); //放到session域中
 //     ActionContext.getContext().getApplication().put("categoryList",categoryService.query()); //放到application域中

        //解决方案二,实现相应的接口(RequestAware,ApplicationAware),让相应的map注入
        request.put("categoryList",categoryService.query()); 
        session.put("categoryList",categoryService.query()); 
        application.put("categoryList",categoryService.query()); 
        return "index";
    }

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    private Map<String,Object> request;
    private Map<String,Object> session;
    private Map<String,Object> application;

    @Override
    public void setApplication(Map<String,Object> application) {
        this.application = application;
    }

    @Override
    public void setSession(Map<String,Object> session) {
        this.session = session;
    }

    @Override
    public void setRequest(Map<String,Object> request) {
        this.request = request;
    }
}
还是上一节整合三大框架时的CategoryAction类,我们在里面加了一个query方法,在该方法中,我们通过向request域、session域和application域中存入查询的结果。第一种方法是直接使用ActionContext来实现,不需要实现任何接口,但是代码量较大;第二种方法通过实现RequestAware、SessionAware和ApplicationAware接口,实现该接口的三个抽象方法把request、session和application注入进来,然后赋给相应的成员变量中,这样就可以在query方法中向域中存放查询结果了。这代码量貌似比第一种方法更大……但是我们可以抽取,先往下看。
我们在index.jsp中新加一个查询连接来测试能否将查询结果显示出来:
  1. <%@pagelanguage=“java”import=“java.util.*”pageEncoding=“UTF-8”%>
  2. <%@tagliburi=“http://java.sun.com/jsp/jstl/core”prefix=“c”%>
  3. <!DOCTYPEHTMLPUBLIC”-//W3C//DTDHTML4.01Transitional//EN”>
  4. <html>
  5. <head>
  6. <title>MyJSP‘index.jsp’startingpage</title>
  7. </head>
  8. <body>
  9. <ahref={pageContext.request.contextpath&nbsp;}/category_update.action?category.id=2&amp;category.type=gga&amp;category.hot=false"</span><spanclass="tag">&gt;</span><span>访问update</span><spanclass="tag">&lt;/</span><spanclass="tag-name">a</span><spanclass="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">a</span><span>&nbsp;</span><spanclass="attribute">href</span><span>=</span><spanclass="attribute-value">"category_save.action"</span><spanclass="tag">&gt;</span><span>访问save</span><spanclass="tag">&lt;/</span><spanclass="tag-name">a</span><spanclass="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">a</span><span>&nbsp;</span><spanclass="attribute">href</span><span>=</span><spanclass="attribute-value">"category_query.action"</span><spanclass="tag">&gt;</span><span>查询所有类别</span><spanclass="tag">&lt;/</span><spanclass="tag-name">a</span><spanclass="tag">&gt;</span><spanclass="tag">&lt;</span><spanclass="tag-name">br</span><spanclass="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">c:forEach</span><span>&nbsp;</span><spanclass="attribute">items</span><span>=</span><spanclass="attribute-value">"{requestScope.categoryList}”var=“category”>
  10. {category.id&nbsp;}&nbsp;|&nbsp;{category.type}|{category.hot&nbsp;}&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">br</span><spanclass="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;/</span><spanclass="tag-name">c:forEach</span><spanclass="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></li><liclass="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">c:forEach</span><span>&nbsp;</span><spanclass="attribute">items</span><span>=</span><spanclass="attribute-value">"{sessionScope.categoryList}”var=“category”>
  11. {category.id&nbsp;}&nbsp;|&nbsp;{category.type}|{category.hot&nbsp;}&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">br</span><spanclass="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;/</span><spanclass="tag-name">c:forEach</span><spanclass="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></li><liclass="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">c:forEach</span><span>&nbsp;</span><spanclass="attribute">items</span><span>=</span><spanclass="attribute-value">"{applicationScope.categoryList}”var=“category”>
  12. {category.id&nbsp;}&nbsp;|&nbsp;{category.type}|${category.hot}<br/>
  13. </c:forEach>
  14. </body>
  15. </html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
  </head>

  <body>
    <a href="${pageContext.request.contextpath }/category_update.action?category.id=2&category.type=gga&category.hot=false">访问update</a>
    <a href="category_save.action">访问save</a>
    <a href="category_query.action">查询所有类别</a><br/>
    <c:forEach items="${requestScope.categoryList }" var="category">
        ${category.id } | ${category.type } | ${category.hot } <br/>
    </c:forEach>

    <c:forEach items="${sessionScope.categoryList }" var="category">
        ${category.id } | ${category.type } | ${category.hot } <br/>
    </c:forEach>

    <c:forEach items="${applicationScope.categoryList }" var="category">
        ${category.id } | ${category.type } | ${category.hot } <br/>
    </c:forEach>
  </body>
</html>

3.2抽取BaseAction

刚刚提到了,第二种方法代码量更大,但是我们可以抽取一个BaseAction,专门处理这些域相关的操作。

  1. publicclassBaseActionextendsActionSupportimplementsRequestAware,ApplicationAware{
  2. protectedMap<String,Object>request;
  3. protectedMap<String,Object>session;
  4. protectedMap<String,Object>request){
  5. this.request=request;
  6. }
  7. }
public class BaseAction extends ActionSupport implements RequestAware,ApplicationAware {

    protected Map<String,Object> request;
    protected Map<String,Object> session;
    protected Map<String,Object> request) {
        this.request = request;
    }
}
然后我们自己的Action如果需要用到这些域对象来存储数据时,直接继承BaseAction即可,就能直接使用request、session和application对象了。所以修改后的CategoryAction如下:
  1. publicclassCategoryActionextendsBaseAction{
  2. privateCategorycategory;
  3. <prename=”code”class=“java”>
  4. privateCategoryServicecategoryService;
  5. publicvoidsetCategoryService(CategoryServicecategoryService){
  6. this.categoryService=categoryService;
  7. }
public class CategoryAction extends BaseAction {

    private Category category;
<pre name="code" class="java">
     private CategoryService categoryService;
 
     public void setCategoryService(CategoryService categoryService) {
      this.categoryService = categoryService;
     }
public String update() {System.out.println(“—-update—-“);categoryService.update(category); return “index”; }public String save() {System.out.println(“—-save—-“);return “index”; } public String query() {request.put(“categoryList”,categoryService.query()); session.put(“categoryList”,categoryService.query()); application.put(“categoryList”,categoryService.query()); return “index”; } public Category getCategory() { return category; } public void setCategory(Category category) {this.category = category; }}
 
  后面所有要使用request、session和application域的Action,只要直接继承BaseAction即可,非常方便。 
  
 

3.3获取参数(ModelDriven)

我们继续看上面的CategoryAction类,里面有个成员变量category,这是个POJO,定义这个变量并写好set和get方法是为了JSP页面可以通过url后面附带参数传进来,参数是category对象中的属性,比如id,type等,但是url中的参数必须写成category.id、category.type等。这样struts会自动将这写参数注入到category对象中,然后我们就可以直接使用这个category对象了,但是这样有点繁琐。我们可以使用ModelDriven来更方便的解决

  1. publicclassCategoryActionextendsBaseActionimplementsModelDriven<Category>{
  2. privateCategorycategory;
  3. //使用ModelDriven接口必须要实现getModel()方法,此方法会把返回的项压到栈顶
  4. @Override
  5. publicCategorygetModel(){
  6. category=newCategory();
  7. returncategory;
  8. }
  9. <prename=”code”class=“java”>privateCategoryServicecategoryService;
  10. publicvoidsetCategoryService(CategoryServicecategoryService){
  11. this.categoryService=categoryService;
  12. }
  13. publicStringupdate(){
  14. System.out.println(”—-update—-“);
  15. categoryService.update(category);
  16. return“index”;
  17. }
  18. publicStringsave(){
  19. System.out.println(”—-save—-“);
  20. return“index”;
  21. }
  22. publicStringquery(){
  23. request.put(”categoryList”,categoryService.query());
  24. return“index”;
  25. }
  26. }
public class CategoryAction extends BaseAction implements ModelDriven<Category>{

    private Category category;

    //使用ModelDriven接口必须要实现getModel()方法,此方法会把返回的项压到栈顶
    @Override
    public Category getModel() {
        category = new Category();
        return category;
    }
<pre name="code" class="java">     private CategoryService categoryService;
 
     public void setCategoryService(CategoryService categoryService) {
      this.categoryService = categoryService;
     }

    public String update() {
        System.out.println("----update----");
        categoryService.update(category);
        return "index";
    }

    public String save() {
        System.out.println("----save----");
        return "index";
    }

    public String query() {
        request.put("categoryList",categoryService.query()); 
        return "index";
    }

}
 
  
 

这样我们在前台JSP页面就不用带category.id这种繁琐的参数了,看JSP页面中的ModelDriven部分:

  1. <%@pagelanguage=“java”import=“java.util.*”pageEncoding=“UTF-8”%>
  2. <%@tagliburi=“http://java.sun.com/jsp/jstl/core”prefix=“c”%>
  3. <!DOCTYPEHTMLPUBLIC”-//W3C//DTDHTML4.01Transitional//EN”>
  4. <html>
  5. <head>
  6. <title>MyJSP‘index.jsp’startingpage</title>
  7. </head>
  8. <body>
  9. <ahref={pageContext.request.contextpath&nbsp;}/category_update.action?category.id=2&amp;category.type=gga&amp;category.hot=false"</span><spanclass="tag">&gt;</span><span>访问update</span><spanclass="tag">&lt;/</span><spanclass="tag-name">a</span><spanclass="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">a</span><span>&nbsp;</span><spanclass="attribute">href</span><span>=</span><spanclass="attribute-value">"category_save.action?id=1&amp;type=haha&amp;hot=true"</span><spanclass="tag">&gt;</span><span>测试ModelDriven</span><spanclass="tag">&lt;/</span><spanclass="tag-name">a</span><spanclass="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">a</span><span>&nbsp;</span><spanclass="attribute">href</span><span>=</span><spanclass="attribute-value">"category_query.action"</span><spanclass="tag">&gt;</span><span>查询所有类别</span><spanclass="tag">&lt;/</span><spanclass="tag-name">a</span><spanclass="tag">&gt;</span><spanclass="tag">&lt;</span><spanclass="tag-name">br</span><spanclass="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">c:forEach</span><span>&nbsp;</span><spanclass="attribute">items</span><span>=</span><spanclass="attribute-value">"{requestScope.categoryList}”var=“category”>
  10. {category.id&nbsp;}&nbsp;|&nbsp;{category.type}|{category.hot&nbsp;}&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">br</span><spanclass="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;/</span><spanclass="tag-name">c:forEach</span><spanclass="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></li><liclass="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">c:forEach</span><span>&nbsp;</span><spanclass="attribute">items</span><span>=</span><spanclass="attribute-value">"{sessionScope.categoryList}”var=“category”>
  11. {category.id&nbsp;}&nbsp;|&nbsp;{category.type}|{category.hot&nbsp;}&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">br</span><spanclass="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;/</span><spanclass="tag-name">c:forEach</span><spanclass="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li><liclass=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></li><liclass="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<spanclass="tag">&lt;</span><spanclass="tag-name">c:forEach</span><span>&nbsp;</span><spanclass="attribute">items</span><span>=</span><spanclass="attribute-value">"{applicationScope.categoryList}”var=“category”>
  12. {category.id&nbsp;}&nbsp;|&nbsp;{category.type}|${category.hot}<br/>
  13. </c:forEach>
  14. </body>
  15. </html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
  </head>

  <body>
    <a href="${pageContext.request.contextpath }/category_update.action?category.id=2&category.type=gga&category.hot=false">访问update</a>
    <a href="category_save.action?id=1&type=haha&hot=true">测试ModelDriven</a>
    <a href="category_query.action">查询所有类别</a><br/>
    <c:forEach items="${requestScope.categoryList }" var="category">
        ${category.id } | ${category.type } | ${category.hot } <br/>
    </c:forEach>

    <c:forEach items="${sessionScope.categoryList }" var="category">
        ${category.id } | ${category.type } | ${category.hot } <br/>
    </c:forEach>

    <c:forEach items="${applicationScope.categoryList }" var="category">
        ${category.id } | ${category.type } | ${category.hot } <br/>
    </c:forEach>
  </body>
</html>

测试结果是可以获得catgory,并且将id,type和hot属性全部赋值好。我们可以看出,通过实现ModelDriven接口,我们可以很方便的在url中携带参数,Action中只需要实现getModel方法,new一个要使用的对象返回即可。到这里我们很容易想到,struts中肯定会有很多这种model需要获取,所以这一块我们也要抽取到BaseAction中去。

3.4抽取ModelDriven到BaseAction

首先我们在BaseAction中添加ModelDriven部分的代码,如下:

  1. //因为有很多不同的model都需要使用ModelDriven,所以这里使用泛型
  2. publicclassBaseAction<T>extendsActionSupportimplementsRequestAware,ApplicationAware,ModelDriven<T>{
  3. protectedMap<String,Object>request;
  4. protectedMap<String,Object>session;
  5. protectedMap<String,Object>application;
  6. protectedTmodel;
  7. @Override
  8. publicvoidsetApplication(Map<String,Object>application){
  9. this.application=application;
  10. }
  11. @Override
  12. publicvoidsetSession(Map<String,Object>session){
  13. this.session=session;
  14. }
  15. @Override
  16. publicvoidsetRequest(Map<String,Object>request){
  17. this.request=request;
  18. }
  19. @Override
  20. publicTgetModel(){//这里通过解析传进来的T来new一个对应的instance
  21. ParameterizedTypetype=(ParameterizedType)this.getClass().getGenericSuperclass();
  22. Classclazz=(Class)type.getActualTypeArguments()[0];
  23. try{
  24. model=(T)clazz.newInstance();
  25. }catch(Exceptione){
  26. thrownewRuntimeException(e);
  27. }
  28. returnmodel;
  29. }
  30. }
//因为有很多不同的model都需要使用ModelDriven,所以这里使用泛型
public class BaseAction<T> extends ActionSupport implements RequestAware,ModelDriven<T> {

    protected Map<String,Object> application;

    protected T model;

    @Override
    public void setApplication(Map<String,Object> request) {
        this.request = request;
    }

    @Override
    public T getModel() { //这里通过解析传进来的T来new一个对应的instance
        ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass();
        Class clazz = (Class)type.getActualTypeArguments()[0];
        try {
            model = (T)clazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }   
        return model;
    }
}
抽取完了后,CategoryAction中的代码会越来越少:
  1. //继承BaseAction,并且加上泛型
  2. publicclassCategoryActionextendsBaseAction<Category>{
  3. privateCategoryServicecategoryService;
  4. publicvoidsetCategoryService(CategoryServicecategoryService){
  5. this.categoryService=categoryService;
  6. }
  7. publicStringupdate(){
  8. System.out.println(”—-update—-“);
  9. categoryService.update(model);//直接使用model
  10. return“index”;
  11. }
  12. publicStringsave(){
  13. System.out.println(”—-save—-“);
  14. System.out.println(model);//直接使用model
  15. return“index”;
  16. }
  17. publicStringquery(){
  18. request.put(”categoryList”,categoryService.query());
  19. return“index”;
  20. }
  21. }
//继承BaseAction,并且加上泛型
public class CategoryAction extends BaseAction<Category> {

    private CategoryService categoryService;

    public void setCategoryService(CategoryService categoryService) {
        this.categoryService = categoryService;
    }

    public String update() {
        System.out.println("----update----");
        categoryService.update(model);//直接使用model
        return "index";
    }

    public String save() {
        System.out.println("----save----");
        System.out.println(model); //直接使用model
        return "index";
    }

    public String query() { 
        request.put("categoryList",categoryService.query()); 
        return "index";
    }

}
到这里,还有一个看着不爽的地方,就是categoryService这个成员变量,它一直存在在CategoryAction里,因为CategoryAction中有用到categoryService对象中的方法,所以必须得创建这个对象,并且有set方法才能注入进来。这就导致一个弊端:如果很多Action都需要使用categoryService的话,那就必须在它们的Action里创建这个对象和set方法,而且,如果一个Action中要使用好几个不同的service对象,那就得全部创建,这样就变得很冗杂。

3.5抽取service到BaseAction

针对上面的问题,我们将工程中所有的service对象都抽取到BaseAction中创建,这样其他Action继承BaseAction后,想用什么service就直接拿来用即可:

  1. //我将BaseAction中的内容归归类了
  2. publicclassBaseAction<T>extendsActionSupportimplementsRequestAware,ModelDriven<T>{
  3. //service对象
  4. protectedCategoryServicecategoryService;
  5. protectedAccountServiceaccountService;
  6. publicvoidsetCategoryService(CategoryServicecategoryService){
  7. this.categoryService=categoryService;
  8. }
  9. publicvoidsetAccountService(AccountServiceaccountService){
  10. this.accountService=accountService;
  11. }
  12. //域对象
  13. protectedMap<String,Object>application;
  14. @Override
  15. publicvoidsetApplication(Map<String,Object>application){
  16. this.application=application;
  17. }
  18. @Override
  19. publicvoidsetSession(Map<String,Object>session){
  20. this.session=session;
  21. }
  22. @Override
  23. publicvoidsetRequest(Map<String,Object>request){
  24. this.request=request;
  25. }
  26. //ModelDriven
  27. protectedTmodel;
  28. @Override
  29. publicTgetModel(){
  30. ParameterizedTypetype=(ParameterizedType)this.getClass().getGenericSuperclass();
  31. Classclazz=(Class)type.getActualTypeArguments()[0];
  32. try{
  33. model=(T)clazz.newInstance();
  34. }catch(Exceptione){
  35. thrownewRuntimeException(e);
  36. }
  37. returnmodel;
  38. }
  39. }
//我将BaseAction中的内容归归类了
public class BaseAction<T> extends ActionSupport implements RequestAware,ModelDriven<T> {

    //service对象
    protected CategoryService categoryService;
    protected AccountService accountService;

    public void setCategoryService(CategoryService categoryService) {
        this.categoryService = categoryService;
    }
    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }

    //域对象
    protected Map<String,Object> application) {
        this.application = application;
    }
    @Override
    public void setSession(Map<String,Object> session) {
        this.session = session;
    }
    @Override
    public void setRequest(Map<String,Object> request) {
        this.request = request;
    }

    //ModelDriven
    protected T model;
    @Override
    public T getModel() {
        ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass();
        Class clazz = (Class)type.getActualTypeArguments()[0];
        try {
            model = (T)clazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }   
        return model;
    }
}
这样CategoryAction中就更加清爽了:
  1. publicclassCategoryActionextendsBaseAction<Category>{
  2. publicStringupdate(){
  3. System.out.println(”—-update—-“);
  4. categoryService.update(model);
  5. return“index”;
  6. }
  7. publicStringsave(){
  8. System.out.println(”—-save—-“);
  9. System.out.println(model);
  10. return“index”;
  11. }
  12. publicStringquery(){
  13. request.put(”categoryList”,categoryService.query());
  14. session.put(”categoryList”,categoryService.query());
  15. application.put(”categoryList”,categoryService.query());
  16. return“index”;
  17. }
  18. }
public class CategoryAction extends BaseAction<Category> {

    public String update() {
        System.out.println("----update----");
        categoryService.update(model);
        return "index";
    }

    public String save() {
        System.out.println("----save----");
        System.out.println(model);
        return "index";
    }

    public String query() {
        request.put("categoryList",categoryService.query()); 
        return "index";
    }

}
有人可能会问,BaseAction中注入了那么多service对象的话不会冗余么?这是不会的,因为就算不写在BaseAction中,spring容器也是会创建这个对象的,这点没有关系,相反,service对象全放在BaseAction中更加便于其他Action的开发,而且BaseAction不需要配到struts.xml文件中,因为根本就没有哪个JSP会请求BaseAction,它只是让其他Action来继承用的。

还有一点别忘了:那就是修改在beans.xml中的配置:

  1. <!–如果是prototype类型,认是使用时创建,不是启动时自动创建–>
  2. <beanid=“baseAction”class=“cn.it.shop.action.BaseAction”scope=“prototype”>
  3. <propertyname=“categoryService”ref=“categoryService”></property>
  4. <propertyname=“accountService”ref=“accountService”></property>
  5. </bean>
  6. <beanid=“categoryAction”class=“cn.it.shop.action.CategoryAction”scope=“prototype”parent=“baseAction”/>
<!-- 如果是prototype类型,认是使用时创建,不是启动时自动创建 -->
<bean id="baseAction" class="cn.it.shop.action.BaseAction" scope="prototype">
     <property name="categoryService" ref="categoryService"></property>
     <property name="accountService" ref="accountService"></property>
</bean>

<bean id="categoryAction" class="cn.it.shop.action.CategoryAction" scope="prototype" parent="baseAction"/>
新加一个baseAction的bean,将工程中所有service对象作为property配好,将原来的categoryAction中的property干掉。

以后我们如果要写新的xxxAction,直接继承BaseAction即可,如果xxxAction中有用到某个service,直接拿来用即可,只需要在beans.xml文件中加一个xxxAction对应的bean,在struts.xml文件中配置好跳转即可。

4.将xml改成注解

我们可以看到,随着项目越写越大,beans.xml中的配置会越来越多,而且很多配置有冗余,为了更加便于开发,我们现在将xml的配置改成注解的形式,我们先看一下beans.xml中的配置:


这些是我们之前搭建环境以及抽取的时候写的bean,这些都需要转换成注解的形式,下面我们一块一块的换掉:首先替换service部分,这部分有三个:baseService、categoryService和accountService。替换如下:


然后将beans.xml中的相应部分干掉即可。接下来修改ActIon部分,主要有baseAction、categoryAction和accountAction三个,替换如下:


然后再干掉beans.xml中的Action部分的配置即可,最后在beans.xml文件添加一个如下配置,就可以使用注解了。

  1. <context:component-scanbase-package=“cn.it.shop..”/>
<context:component-scan base-package="cn.it.shop.."/>
有人可能会问,为什么service和action两个使用注解的时候不一样呢?service中使用的是@Service而action中使用的是@Controller呢?其实是一样的,只是为了区分它们是不同层的bean而已,便于阅读。



相关阅读:http://blog.csdn.net/column/details/str2hiberspring.html

整个项目的源码下载地址:http://blog.csdn.NET/eson_15/article/details/51479994

_____________________________________________________________________________________________________________________________________________________

—–乐于分享,共同进步!

—–更多文章请看:http://blog.csdn.net/eson_15
document.getElementById("bdshell_js").src = "http://bdimg.share.baidu.com/static/js/shell_v2.js?cdnversion=" + Math.ceil(new Date()/3600000)
<div id="digg" articleid="51297698">
        <dl id="btnDigg" class="digg digg_enable" onclick="btndigga();">

             <dt>顶</dt>
            <dd>22</dd>
        </dl>


        <dl id="btnBury" class="digg digg_enable" onclick="btnburya();">

              <dt>踩</dt>
            <dd>1</dd>               
        </dl>

    </div>
 <div class="tracking-ad" data-mod="popu_222"><a href="javascript:void(0);" target="_blank">&nbsp;</a>   </div>
<div class="tracking-ad" data-mod="popu_223"> <a href="javascript:void(0);" target="_blank">&nbsp;</a></div>
<script type="text/javascript">
            function btndigga() {
                $(".tracking-ad[data-mod='popu_222'] a").click();
            }
            function btnburya() {
                $(".tracking-ad[data-mod='popu_223'] a").click();
            }
        </script>
<div style="clear:both; height:10px;"></div>


    <div class="similar_article" style="">
            <h4>我的同类文章</h4>
            <div class="similar_c" style="margin:20px 0px 0px 0px">
                <div class="similar_c_t">
                            <label class="similar_cur">
                                <span style="cursor:pointer" onclick="GetCategoryArticles('6228419','eson_15','foot','51297698');">●  项目实战<em>(29)</em></span>
                            </label>
                            <label class="">
                                <span style="cursor:pointer" onclick="GetCategoryArticles('6214186','51297698');">------【SSH网上商城】<em>(29)</em></span>
                            </label>
                </div>

                <div class="similar_wrap tracking-ad" data-mod="popu_141" style="max-height:195px;">
                    <a href="http://blog.csdn.net" style="display:none" target="_blank">http://blog.csdn.net</a>
                    <ul class="similar_list fl"><li><em>•</em><a href="http://blog.csdn.net/eson_15/article/details/51506334" id="foot_aritcle_51506334undefined8369459379660253" target="_blank" title="【SSH网上商城项目实战29】使用JsChart技术在后台显示商品销售报表">【SSH网上商城项目实战29】使用JsChart技术在后台显示商品销售报表</a><span>2016-05-26</span><label><i>阅读</i><b>6059</b></label></li> <li><em>•</em><a href="http://blog.csdn.net/eson_15/article/details/51484247" id="foot_aritcle_51484247undefined48390500531566816" target="_blank" title="【SSH网上商城项目实战27】域名空间的申请和项目的部署及发布">【SSH网上商城项目实战27】域名空间的申请和项目的部署及发布</a><span>2016-05-23</span><label><i>阅读</i><b>13823</b></label></li> <li><em>•</em><a href="http://blog.csdn.net/eson_15/article/details/51475431" id="foot_aritcle_51475431undefined581392143237967" target="_blank" title="【SSH网上商城项目实战26】完成订单支付后的短信发送功能">【SSH网上商城项目实战26】完成订单支付后的短信发送功能</a><span>2016-05-22</span><label><i>阅读</i><b>4704</b></label></li> <li><em>•</em><a href="http://blog.csdn.net/eson_15/article/details/51465067" id="foot_aritcle_51465067undefined29928502161607917" target="_blank" title="【SSH网上商城项目实战24】Struts2中如何处理多个Model请求">【SSH网上商城项目实战24】Struts2中如何处理多个Model请求</a><span>2016-05-21</span><label><i>阅读</i><b>4245</b></label></li> <li><em>•</em><a href="http://blog.csdn.net/eson_15/article/details/51452243" id="foot_aritcle_51452243undefined8578162251584256" target="_blank" title="【SSH网上商城项目实战22】获取银行图标以及支付页面显示">【SSH网上商城项目实战22】获取银行图标以及支付页面显示</a><span>2016-05-19</span><label><i>阅读</i><b>3742</b></label></li> <li><em>•</em><a href="http://blog.csdn.net/eson_15/article/details/51441431" id="foot_aritcle_51441431undefined23568729536020583" target="_blank" title="【SSH网上商城项目实战20】在线支付平台的介绍">【SSH网上商城项目实战20】在线支付平台的介绍</a><span>2016-05-18</span><label><i>阅读</i><b>4107</b></label></li> </ul>

                    <ul class="similar_list fr"><li><em>•</em><a href="http://blog.csdn.net/eson_15/article/details/51487323" id="foot_aritcle_51487323undefined23490824805608823" target="_blank" title="【SSH网上商城项目实战28】使用Ajax技术局部更新商品数量和总价">【SSH网上商城项目实战28】使用Ajax技术局部更新商品数量和总价</a><span>2016-05-24</span><label><i>阅读</i><b>5337</b></label></li> <li><em>•</em><a href="http://blog.csdn.net/eson_15/article/details/51479994" id="foot_aritcle_51479994undefined11433626551361509" target="_blank" title="【SSH网上商城项目实战30】项目总结(附源码下载地址)">【SSH网上商城项目实战30】项目总结(附源码下载地址)</a><span>2016-05-27</span><label><i>阅读</i><b>21890</b></label></li> <li><em>•</em><a href="http://blog.csdn.net/eson_15/article/details/51475046" id="foot_aritcle_51475046undefined609370430212923" target="_blank" title="【SSH网上商城项目实战25】使用java email给用户发送邮件">【SSH网上商城项目实战25】使用java email给用户发送邮件</a><span>2016-05-22</span><label><i>阅读</i><b>4007</b></label></li> <li><em>•</em><a href="http://blog.csdn.net/eson_15/article/details/51464415" id="foot_aritcle_51464415undefined2724736389502649" target="_blank" title="【SSH网上商城项目实战23】完成在线支付功能">【SSH网上商城项目实战23】完成在线支付功能</a><span>2016-05-20</span><label><i>阅读</i><b>8822</b></label></li> <li><em>•</em><a href="http://blog.csdn.net/eson_15/article/details/51447492" id="foot_aritcle_51447492undefined269467390791984" target="_blank" title="【SSH网上商城项目实战21】从Demo中看易宝支付的流程">【SSH网上商城项目实战21】从Demo中看易宝支付的流程</a><span>2016-05-18</span><label><i>阅读</i><b>9653</b></label></li> </ul>
                <a href="http://blog.csdn.net/eson_15/article/category/6228419" class="MoreArticle">更多文章</a></div>
            </div>
        </div>    
<script type="text/javascript">
    $(function () {
        GetCategoryArticles('6228419','51297698');
    });
</script>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


php输出xml格式字符串
J2ME Mobile 3D入门教程系列文章之一
XML轻松学习手册
XML入门的常见问题(一)
XML入门的常见问题(三)
XML轻松学习手册(2)XML概念
xml文件介绍及使用
xml编程(一)-xml语法
XML文件结构和基本语法
第2章 包装类