配色: 字号:
SSH框架网上商城项目第2战之基本增删查改、Service和Action的抽取
2016-12-24 | 阅:  转:  |  分享 
  
SSH框架网上商城项目第2战之基本增删查改、Service和Action的抽取

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

1.Service层的抽取

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

1.1完善CategoryService层

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

//CategoryService接口

publicinterfaceCategoryServiceextendsBaseService{

publicvoidsave(Categorycategory);//插入

publicvoidupdate(Categorycategory);//更新

publicvoiddelete(intid);//删除

publicCategoryget(intid);//获取一个Category

publicListquery();//获取全部Category

}

对CategoryService接口的具体实现:

publicclassCategoryServiceImplextendsBaseServiceImplimplementsCategoryService{

privateSessionFactorysessionFactory;

//Spring会注进来

publicvoidsetSessionFactory(SessionFactorysessionFactory){

this.sessionFactory=sessionFactory;

}

protectedSessiongetSession(){

//从当前线程获取session,如果没有则创建一个新的session

returnsessionFactory.getCurrentSession();

}

@Override

publicvoidsave(Categorycategory){

getSession().save(category);

}

@Override

publicvoidupdate(Categorycategory){

getSession().update(category);

}

@Override

publicvoiddelete(intid){

/第一种方法有个弊端,就是没删除一次得先查询一次

Objectobj=getSession().get(Category.class,id);

if(obj!=null){

getSession().delete(obj);

}/

Stringhql="deleteCategorywhileid=:id";

getSession().createQuery(hql)//

.setInteger("id",id)//

.executeUpdate();

}

@Override

publicCategoryget(intid){

return(Category)getSession().get(Category.class,id);

}

@Override

publicListquery(){

Stringhql="fromCategory";

returngetSession().createQuery(hql).list();

}

}

1.2Service层抽取实现

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

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

//基础接口BaseService,使用泛型

publicinterfaceBaseService{

publicvoidsave(Tt);

publicvoidupdate(Tt);

publicvoiddelete(intid);

publicTget(intid);

publicListquery();

}

然后再根据CategoryServiceImpl实现类创建BaseService接口的实现类BaseServiceImpl:

/

@DescriptionTODO(公共模块的抽取)

@authoreson_15



/

@SuppressWarnings("unchecked")

publicclassBaseServiceImplimplementsBaseService{

privateClassclazz;//clazz中存储了当前操作的类型,即泛型T

privateSessionFactorysessionFactory;

publicBaseServiceImpl(){

//下面三个打印信息可以去掉,这里是给自己看的

System.out.println("this代表的是当前调用构造方法的对象"+this);

System.out.println("获取当前this对象的父类信息"+this.getClass().getSuperclass());

System.out.println("获取当前this对象的父类信息(包括泛型信息)"+this.getClass().getGenericSuperclass());

//拿到泛型的参数类型

ParameterizedTypetype=(ParameterizedType)this.getClass().getGenericSuperclass();

clazz=(Class)type.getActualTypeArguments()[0];

}

publicvoidsetSessionFactory(SessionFactorysessionFactory){

this.sessionFactory=sessionFactory;

}

protectedSessiongetSession(){

//从当前线程获取session,如果没有则创建一个新的session

returnsessionFactory.getCurrentSession();

}

@Override

publicvoidsave(Tt){

getSession().save(t);

}

@Override

publicvoidupdate(Tt){

getSession().update(t);

}

@Override

publicvoiddelete(intid){

System.out.println(clazz.getSimpleName());

Stringhql="delete"+clazz.getSimpleName()+"ascwherec.id=:id";

getSession().createQuery(hql)//

.setInteger("id",id)//

.executeUpdate();

}

@Override

publicTget(intid){

return(T)getSession().get(clazz,id);

}

@Override

publicListquery(){

Stringhql="from"+clazz.getSimpleName();

returngetSession().createQuery(hql).list();

}

}

抽取完了后,我们就可以改写CategoryService接口和CategoryServiceImpl实现类了。如下:

//CategoryService接口继承BaseService接口

publicinterfaceCategoryServiceextendsBaseService{

/

只要添加CategoryService本身需要的新的方法即可,公共方法已经在BaseService中了

/

}

/

@DescriptionTODO(模块自身的业务逻辑)

@authoreson_15



/

publicclassCategoryServiceImplextendsBaseServiceImplimplementsCategoryService{

/

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

/

}

从代码中可以看出,新增的Service只需要继承BaseService接口,然后在接口中新增本Service所需要的业务逻辑即可。新增的ServiceImpl只需要继承BaseServiceImpl并实现新增的业务逻辑即可。

但是别忘了很重要的一点:就是修改Spring的配置文件beans.xml中的bean。











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

2.Service层添加一个Account

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

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

publicinterfaceAccountServiceextendsBaseService{//注意BaseService里的泛型现在是Account

/

只要添加AccountService本身需要的新的方法即可,公共方法已经在BaseService中了

/

}

然后写一个AccountServiceImpl实现类继承BaseServiceImpl实现类,并实现AccountService接口即可:

publicclassAccountServiceImplextendsBaseServiceImplimplementsAccountService{

/

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

/

//管理登陆功能,后期再完善

}

最后在beans.xml文件里加上如下配置:



这样就写好了一个新的service了,以后需要添加service就遵循这个流程,非常方便。

3.Action的抽取

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

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

publicclassCategoryActionextendsActionSupportimplementsRequestAware,SessionAware,ApplicationAware{

privateCategorycategory;

privateCategoryServicecategoryService;

publicvoidsetCategoryService(CategoryServicecategoryService){

this.categoryService=categoryService;

}

publicStringupdate(){

System.out.println("----update----");

categoryService.update(category);

return"index";

}

publicStringsave(){

System.out.println("----save----");

return"index";

}

publicStringquery(){

//解决方案一,采用相应的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,SessionAware,ApplicationAware),让相应的map注入

request.put("categoryList",categoryService.query());

session.put("categoryList",categoryService.query());

application.put("categoryList",categoryService.query());

return"index";

}

publicCategorygetCategory(){

returncategory;

}

publicvoidsetCategory(Categorycategory){

this.category=category;

}

privateMaprequest;

privateMapsession;

privateMapapplication;

@Override

publicvoidsetApplication(Mapapplication){

this.application=application;

}

@Override

publicvoidsetSession(Mapsession){

this.session=session;

}

@Override

publicvoidsetRequest(Maprequest){

this.request=request;

}

}

还是上一节整合三大框架时的CategoryAction类,我们在里面加了一个query方法,在该方法中,我们通过向request域、session域和application域中存入查询的结果。第一种方法是直接使用ActionContext来实现,不需要实现任何接口,但是代码量较大;第二种方法通过实现RequestAware、SessionAware和ApplicationAware接口,实现该接口的三个抽象方法把request、session和application注入进来,然后赋给相应的成员变量中,这样就可以在query方法中向域中存放查询结果了。这代码量貌似比第一种方法更大……但是我们可以抽取,先往下看。

我们在index.jsp中新加一个查询连接来测试能否将查询结果显示出来:

<%@pagelanguage="java"import="java.util."pageEncoding="UTF-8"%>

<%@tagliburi="java.sun/jsp/jstl/core"prefix="c"%>







MyJSP''index.jsp''startingpage





访问update

访问save

查询所有类别




${category.id}|${category.type}|${category.hot}






${category.id}|${category.type}|${category.hot}






${category.id}|${category.type}|${category.hot}








3.2抽取BaseAction

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

publicclassBaseActionextendsActionSupportimplementsRequestAware,SessionAware,ApplicationAware{

protectedMaprequest;

protectedMapsession;

protectedMapapplication;

@Override

publicvoidsetApplication(Mapapplication){

this.application=application;

}

@Override

publicvoidsetSession(Mapsession){

this.session=session;

}

@Override

publicvoidsetRequest(Maprequest){

this.request=request;

}

}

然后我们自己的Action如果需要用到这些域对象来存储数据时,直接继承BaseAction即可,就能直接使用request、session和application对象了。所以修改后的CategoryAction如下:

publicclassCategoryActionextendsBaseAction{

privateCategorycategory;



privateCategoryServicecategoryService;

publicvoidwww.visa158.comsetCategoryService(CategoryServicecategoryService){

this.categoryService=categoryService;

}

publicStringupdate(){System.out.println("----update----");categoryService.update(category);return"index";}publicStringsave(){System.out.println("----save----");return"index";}publicStringquery(){request.put("categoryList",categoryService.query());session.put("categoryList",categoryService.query());application.put("categoryList",categoryService.query());return"index";}publicCategorygetCategory(){returncategory;}publicvoidsetCategory(Categorycategory){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来更方便的解决。

publicclassCategoryActionextendsBaseActionimplementsModelDriven{

privateCategorycategory;

//使用ModelDriven接口必须要实现getModel()方法,此方法会把返回的项压到栈顶

@Override

publicCategorygetModel(){

category=newCategory();

returncategory;

}

privateCategoryServicecategoryService;

publicvoidsetCategoryService(CategoryServicecategoryService){

this.categoryService=categoryService;

}

publicStringupdate(){

System.out.println("----update----");

categoryService.update(category);

return"index";

}

publicStringsave(){

System.out.println("----save----");

return"index";

}

publicStringquery(){

request.put("categoryList",categoryService.query());

session.put("categoryList",categoryService.query());

application.put("categoryList",categoryService.query());

return"index";

}

}

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

?

<%@pagelanguage="java"import="java.util."pageEncoding="UTF-8"%>

<%@tagliburi="java.sun/jsp/jstl/core"prefix="c"%>







MyJSP''index.jsp''startingpage





访问update

测试ModelDriven

查询所有类别




${category.id}|${category.type}|${category.hot}






${category.id}|${category.type}|${category.hot}






${category.id}|${category.type}|${category.hot}








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

3.4抽取ModelDriven到BaseAction

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

//因为有很多不同的model都需要使用ModelDriven,所以这里使用泛型

publicclassBaseActionextendsActionSupportimplementsRequestAware,SessionAware,ApplicationAware,ModelDriven{

protectedMaprequest;

protectedMapsession;

protectedMapapplication;

protectedTmodel;

@Override

publicvoidsetApplication(Mapapplication){

this.application=application;

}

@Override

publicvoidsetSession(Mapsession){

this.session=session;

}

@Override

publicvoidsetRequest(Maprequest){

this.request=request;

}

@Override

publicTgetModel(){//这里通过解析传进来的T来new一个对应的instance

ParameterizedTypetype=(ParameterizedType)this.getClass().getGenericSuperclass();

Classclazz=(Class)type.getActualTypeArguments()[0];

try{

model=(T)clazz.newInstance();

}catch(Exceptione){

thrownewRuntimeException(e);

}

returnmodel;

}

}

抽取完了后,CategoryAction中的代码会越来越少:

//继承BaseAction,并且加上泛型

publicclassCategoryActionextendsBaseAction{

privateCategoryServicecategoryService;

publicvoidsetCategoryService(CategoryServicecategoryService){

this.categoryService=categoryService;

}

publicStringupdate(){

System.out.println("----update----");

categoryService.update(model);//直接使用model

return"index";

}

publicStringsave(){

System.out.println("----save----");

System.out.println(model);//直接使用model

return"index";

}

publicStringquery(){

request.put("categoryList",categoryService.query());

session.put("categoryList",categoryService.query());

application.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就直接拿来用即可:

//我将BaseAction中的内容归归类了

publicclassBaseActionextendsActionSupportimplementsRequestAware,SessionAware,ApplicationAware,ModelDriven{

//service对象

protectedCategoryServicecategoryService;

protectedAccountServiceaccountService;

publicvoidsetCategoryService(CategoryServicecategoryService){

this.categoryService=categoryService;

}

publicvoidsetAccountService(AccountServiceaccountService){

this.accountService=accountService;

}

//域对象

protectedMaprequest;

protectedMapsession;

protectedMapapplication;

@Override

publicvoidsetApplication(Mapapplication){

this.application=application;

}

@Override

publicvoidsetSession(Mapsession){

this.session=session;

}

@Override

publicvoidsetRequest(Maprequest){

this.request=request;

}

//ModelDriven

protectedTmodel;

@Override

publicTgetModel(){

ParameterizedTypetype=(ParameterizedType)this.getClass().getGenericSuperclass();

Classclazz=(Class)type.getActualTypeArguments()[0];

try{

model=(T)clazz.newInstance();

}catch(Exceptione){

thrownewRuntimeException(e);

}

returnmodel;

}

}

这样CategoryAction中就更加清爽了:

publicclassCategoryActionextendsBaseAction{

publicStringupdate(){

System.out.println("----update----");

categoryService.update(model);

return"index";

}

publicStringsave(){

System.out.println("----save----");

System.out.println(model);

return"index";

}

publicStringquery(){

request.put("categoryList",categoryService.query());

session.put("categoryList",categoryService.query());

application.put("categoryList",categoryService.query());

return"index";

}

}

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

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













新加一个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文件中添加一个如下配置,就可以使用注解了。有人可能会问,为什么service和action两个使用注解的时候不一样呢?service中使用的是@Service而action中使用的是@Controller呢?其实是一样的,只是为了区分它们是不同层的bean而已,便于阅读。





















献花(0)
+1
(本文系白狐一梦首藏)