ssm项目开发, 必须要关注MVC结构的每一个环节. 该文章要说的是不同层级之间的抽取,使得项目有较好的基础。以下编码的思想来自Java设计模式中的"策略模式"
"策略模式"面向对象原则: (1)封装变化:即把变化与不会变化的分开(2)多用组合(在这里没有体现) (3)针对接口编程,不针对实现编程
准备二个bean, 分别是Supplier ,Account
Supplier.java
package cn.itcast.scm.entity;
import java.io.Serializable;
import java.math.BigDecimal;
public class Supplier implements Serializable {
/**
*
*/
private static final long serialVersionUID = -3051147093149131879L;
private Integer supId;//id
private String supName;// 供应商名称
private String supLinkman;//联系人
private String supPhone; // 联系方式
private String supAddress; //地址
private String supRemark; //备注
private BigDecimal supPay; //期初应付
private String supType; //供应商类型
public Integer getSupId() {
return supId;
}
public void setSupId(Integer supId) {
this.supId = supId;
}
public String getSupName() {
return supName;
}
public void setSupName(String supName) {
this.supName = supName;
}
public String getSupLinkman() {
return supLinkman;
}
public void setSupLinkman(String supLinkman) {
this.supLinkman = supLinkman;
}
public String getSupPhone() {
return supPhone;
}
public void setSupPhone(String supPhone) {
this.supPhone = supPhone;
}
public String getSupAddress() {
return supAddress;
}
public void setSupAddress(String supAddress) {
this.supAddress = supAddress;
}
public String getSupRemark() {
return supRemark;
}
public void setSupRemark(String supRemark) {
this.supRemark = supRemark;
}
public BigDecimal getSupPay() {
return supPay;
}
public void setSupPay(BigDecimal supPay) {
this.supPay = supPay;
}
public String getSupType() {
return supType;
}
public void setSupType(String supType) {
this.supType = supType;
}
@Override
public String toString() {
return "Supplier [supId=" + supId + ", supName=" + supName
+ ", supLinkman=" + supLinkman + ", supPhone=" + supPhone
+ ", supAddress=" + supAddress + ", supRemark=" + supRemark
+ ", supPay=" + supPay + ", supType=" + supType + "]";
}
}
Account.java
package cn.itcast.scm.entity;
import java.io.Serializable;
public class Account implements Serializable {
private static final long serialVersionUID = 8971557569763969226L;
private Integer accId;
private String accLogin;
private String accName;
private String accPass;
public Integer getAccId() {
return accId;
}
public void setAccId(Integer accId) {
this.accId = accId;
}
public String getAccLogin() {
return accLogin;
}
public void setAccLogin(String accLogin) {
this.accLogin = accLogin;
}
public String getAccName() {
return accName;
}
public void setAccName(String accName) {
this.accName = accName;
}
public String getAccPass() {
return accPass;
}
public void setAccPass(String accPass) {
this.accPass = accPass;
}
@Override
public String toString() {
return "Account [accId=" + accId + ", accLogin=" + accLogin
+ ", accName=" + accName + ", accPass=" + accPass + "]";
}
}
1 Dao层的抽取
从dao开始, 我们把共同的功能放在一起baseMapper 如CRUD, 把各自的数据库操作放在supplierMapper , accountMapper. 在application.xml中应该配置转换器, 这样我们应该通过注解方式自动生成mapper代理, , 我们将baseMapper抽取出来, 这样子在supplierMapper 中写自己的方法就行了!
baseMapper 代码如下:
package cn.itcast.scm.dao;
import java.util.List;
import cn.itcast.scm.entity.Page;
public interface BaseMapper<T> {
/**添加单个对象
* @param entity 需要插入的对象
* */
public int insert(T entity);
/**修改单个对象
* */
public int update(T entity);
/**删除单个对象
* */
public int delete(T entity);
/**查询单个对象
* */
public T select(T entity);
/** 分页查询
* */
public List<T> selectKeyWordPageList(Page<T> page);
/** 返回总记录数
* */
public Integer selectKeyWordTotalPage(Page<T> page);
/** 按照Id批量删除
* */
public Integer deleteList(String [] arrays);
}
package cn.itcast.scm.dao;
import cn.itcast.scm.entity.Supplier;
public interface SupplierMapper extends BaseMapper<Supplier> {
//写自己的业务
}
以上是Dao的抽取, 挺简单
2 Service 抽取
由于service层有service的实现类, 抽取比较麻烦, 但是非常实用,
接口的处理和Dao是一样的! 只展示baseService
package cn.itcast.scm.service;
import cn.itcast.scm.entity.Page;
public interface BaseService<T> {
/**添加单个对象
* */
public int insert (T entity) throws Exception;
/**修改单个对象
* */
public int update(T entity) throws Exception;
/**删除单个对象
* */
public int delete(T entity) throws Exception;
/**查询单个对象
* */
public T select(T entity);
/** 分页查询(page,rows)查询
* */
public void selectKeyWordPageList(Page<T> page);
/** 返回总记录数
* */
public Integer selectKeyWordTotalPage(Page<T> page);
/**按照id批量删除
* */
public Integer deleteById(String[] arrays);
}
接下来就是对service实现的抽取, 为什么需要抽取呢? 我们的supplierServiceImpl实现类是需要继承baseService 和supplierService的.那么baseService中的方法都需要实现, 编码角度上看不清晰, oo原则上看, 不符合"封装变化", 于是我们需要写一个baseServiceImpl来处理.
认真看注释, 就知道什么意思!
package cn.itcast.scm.service.impl;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import cn.itcast.scm.dao.AccountMapper;
import cn.itcast.scm.dao.BaseMapper;
import cn.itcast.scm.dao.SupplierMapper;
import cn.itcast.scm.entity.Page;
import cn.itcast.scm.service.BaseService;
/**
*
* */
public class BaseServiceImpl<T> implements BaseService<T> {
protected BaseMapper<T> baseMapper;
@Autowired
protected SupplierMapper supplierMapper;
@Autowired
protected AccountMapper accountMapper;
/** 该初始化函数作用是给baseMapper动态确认是哪个mapper;
* baseMapper=Supplier;
* */
@PostConstruct//在构造方法后,初化前执行
private void initBaseMapper() throws Exception{
//完成以下逻辑,需要对研发本身进行命名与使用规范
//this关键字指对象本身,这里指的是调用此方法的实现类(子类)
//this 具体来说是指SupplierServiceImpl / AccountserviceImpl/...
System.out.println("=======this :"+this);
System.out.println("=======父类基本信息:"+this.getClass().getSuperclass());
System.out.println("=======父类和泛型的信息:"+this.getClass().getGenericSuperclass());
//BaseServiceImpl<Supplier>
ParameterizedType type =(ParameterizedType)this.getClass().getGenericSuperclass();
//获取第一个参数的class---Supplier/Account
Class clazz = (Class)type.getActualTypeArguments()[0];
System.out.println("=======class:"+clazz);
//转化为属性名(相关的Mapper子类的引用名)Supplier
//localField=supplierMapper/...
String localField = clazz.getSimpleName().substring(0,1).toLowerCase()+clazz.getSimpleName().substring(1)+"Mapper";
System.out.println("=======localField:"+localField);
//getDeclaredField:可以使用于包括私有、默认、受保护、公共字段,但不包括继承的字段
//就是该类的属性 supplierMapper或者accountMapper
Field field=this.getClass().getSuperclass().getDeclaredField(localField);
System.out.println("=======field:"+field);
System.out.println("=======field对应的对象:"+field.get(this));
//baseMaper属性
Field baseField = this.getClass().getSuperclass().getDeclaredField("baseMapper");
System.out.println("=======baseField:"+baseField);
System.out.println("=======baseField对应的对象:"+baseField.get(this));
//field.get(this)获取当前this的field字段的值。例如:Supplier对象中,baseMapper所指向的对象为其子类型SupplierMapper对象,子类型对象已被spring实例化于容器中
//baseMapper= supplierMapper(AccountMapper);
baseField.set(this, field.get(this));
System.out.println("========baseField对应的对象:"+baseMapper);
}
@Override
public int insert(T entity) throws Exception {
return baseMapper.insert(entity);
}
@Override
public int update(T entity) throws Exception {
return baseMapper.update(entity);
}
@Override
public int delete(T entity) throws Exception {
return baseMapper.delete(entity);
}
@Override
public T select(T entity) {
return baseMapper.select(entity);
}
@Override
public void selectKeyWordPageList(Page<T> page) {
page.setList(baseMapper.selectKeyWordPageList(page));
page.setTotalRecord(baseMapper.selectKeyWordTotalPage(page));
}
@Override
public Integer selectKeyWordTotalPage(Page<T> page) {
return baseMapper.selectKeyWordTotalPage(page);
}
@Override
public Integer deleteById(String[] arrays) {
return baseMapper.deleteList(arrays);
}
}
有了baseServiceImpl ,使用起来, SupplierServiceImpl ,AccountServiceImpl继承他, 那么里面就减少了很多"不必要的代码了", 只需要实现对应service接口中的方法!
如下SupplierServcieImpl.java就可以如下这样写了. 清晰美观,感觉好多了!
package cn.itcast.scm.service.impl;
import org.springframework.stereotype.Service;
import cn.itcast.scm.entity.Supplier;
import cn.itcast.scm.service.SupplierService;
@Service("supplierService")
public class SupplierServiceImpl extends BaseServiceImpl<Supplier>
implements SupplierService {
//写SupplierService实现,
}
最后看看项目的结构: 发现BaseAction,BaseDao,BaseService,BaseServiecImpl 里面的都是共有的功能, 自己的业务都写在自己的Service中, 这样我们就可以专注业务了,
不是很舒服?