案例:(设计优化,项目基于SSH框架,并且使用spring-data-jpa)
这里以一个基于ssh框架的项目为例,相信大家都知道,我们在使用ssh框架完成一个项目的时候,通常会为每一
个实体类的对象创建一个对应的action,创建项目目录如下:
我们现在要做的就是对action中公共的部分进行抽取,对代码结构进行精简,首先我们看其中两个未抽取action
的代码结构:
@Controller @Scope("prototype") @ParentPackage("json-default") @Namespace("/") public class CourierAction extends ActionSupport implements ModelDriven<Courier> { // 模型驱动接收收派员信息 private Courier courier = new Courier(); @Override public Courier getModel() { return courier; } // 属性驱动接收分页信息 private int page; // 要显示的页码数 private int rows; // 每个页面上要显示的数据的条数 public void setPage(int page) { this.page = page; } public void setRows(int rows) { this.rows = rows; } // 接收作废送派员或者还原收派员时用户选中的送派员的id private String ids; // 接收状态标记 private String status; public void setStatus(String status) { this.status = status; } public void setIds(String ids) { this.ids = ids; } @Autowired private ICourierService CourierServiceImpl; @Action(value = "courier_save", results = { @Result(name = "success", type = "redirect", location = "/pages/base/courier.html") }) public String save() { CourierServiceImpl.save(courier); return SUCCESS; } @Action(value = "courier_pageQuery", results = { @Result(name = "success", type = "json") }) public String pageQuery() { // 封装数据 Pageable pageable = new PageRequest(page - 1, rows, Direction.ASC, "courierNum"); // 调用方法查询到封装好的页面数据 Page<Courier> page = CourierServiceImpl.pageQuery(new Specification<Courier>() { @Override public Predicate toPredicate(Root<Courier> root, CriteriaQuery<?> query, CriteriaBuilder cb) { List<Predicate> predicates = new ArrayList<>(); // 对接收到的数据进行判断,只要不为空的时候才是查询条件 if (!StringUtils.isBlank(courier.getCourierNum())) { // 对工号进行判断 Predicate p1 = cb.equal(root.get("courierNum").as(String.class), courier.getCourierNum()); predicates.add(p1); } if (StringUtils.isNotBlank(courier.getCompany())) { // 对所属的单位进行判断 Predicate p2 = cb.like(root.get("company").as(String.class), "%" + courier.getCompany() + "%"); predicates.add(p2); } if (!StringUtils.isBlank(courier.getType())) { // 对类型进行判断 Predicate p3 = cb.equal(root.get("type").as(String.class), courier.getType()); predicates.add(p3); } // 对收派标准进行判断,涉及到了多表查询 Join<Object, Object> join = root.join("standard", JoinType.INNER); if (courier.getStandard() != null && StringUtils.isNotBlank(courier.getStandard().getName())) { Predicate p4 = cb.equal(join.get("name").as(String.class), courier.getStandard().getName()); predicates.add(p4); } return cb.and(predicates.toArray(new Predicate[0])); } }, pageable); // 获取数据 long total = page.getTotalElements(); // 信息总条数 List<Courier> rows = page.getContent(); // 所有的收派员对象 // 将数据封装到一个map中 Map<String, Object> result = new HashMap<String, Object>(); result.put("total", total); result.put("rows", rows); // 将map集合压入到值栈中 ActionContext.getContext().getValueStack().push(result); return SUCCESS; } /** * 一个作废或者还原派送员的方法 * * @return */ @Action(value = "courier_updateBatch", results = { @Result(name = "success", location = "pages/base/courier.html", type = "redirect") }) public String updateBatch() { Character deltag = status.charAt(0); String[] strIds = ids.split(","); // 调用业务层,实现批量作废或者还原 CourierServiceImpl.delBatch(strIds, deltag); return SUCCESS; } }
@Controller @Scope("prototype") @ParentPackage("json-default") @Namespace("/") public class StandardAction extends ActionSupport implements ModelDriven<Standard> { private int page; // 显示的页码 private int rows; // 每页显示的条数 public void setPage(int page) { this.page = page; } public void setRows(int rows) { this.rows = rows; } private Standard standard = new Standard(); @Override public Standard getModel() { return standard; } @Autowired private IStandardService standardServiceImpl; /** * 添加收派标准的方法 * * @return */ @Action(value = "standard_save", results = { @Result(name = "success", location = "/pages/base/standard.html", type = "redirect") }) public String save() { standardServiceImpl.addStandard(standard); return SUCCESS; } /** * 一个分页查询的方法 * * @return */ @Action(value = "standard_pageQuery", results = { @Result(name = "success", type = "json") }) public String pageQuery() { // 封装分页查询数据到Pageable对象中 Pageable pageRequest = new PageRequest(page - 1, rows, Direction.ASC, "minWeight"); // 调用方法查询到封装好的页面 Page<Standard> page = standardServiceImpl.pageQuery(pageRequest); int total = (int) page.getTotalElements(); // 数据总条数 List<Standard> standards = page.getContent(); // 查询到的页面的所有信息 // 将数据封装到map集合并压入值栈顶部,利用框架特性将其转换成json数据返回到服务器 Map<String, Object> result = new HashMap<>(); result.put("total", total); result.put("rows", standards); ActionContext.getContext().getValueStack().push(result); return SUCCESS; } @Action(value = "standard_findAll", results = { @Result(name = "success", type = "json") }) public String findAll() { // 调用方法查询到所有数据 List<Standard> standards = standardServiceImpl.findAll(); // 将数据封装到map集合并压入值栈顶部,利用框架特性将其转换成json数据返回到服务器 ActionContext.getContext().getValueStack().push(standards); return SUCCESS; } }
对于以上代码我们发现,首先,每个action都继承了ActionSupport类,都采用了模型驱动去接收封装好的数
据,其次每个action都采用了属性驱动去接收分页的数据,最后每个action都继承了json-default包,这就导致
了在给服务器返回数据时,都有将数据压入到值栈顶部的操作。
分析后,我们都这部分代码进行优化,抽取baseAction,代码如下:
public abstract class BaseAction<T> extends ActionSupport implements ModelDriven<T> { // 模型驱动 protected T model; @Override public T getModel() { return model; } // 构造器 完成model实例化 public BaseAction() { /** * 当子类实例化的时候,一定会先执行父类的构造函数, * 父类构造函数执行的时候,通过this调用子类对象,获取子类的字节码文件对象, * 再通过这个字节码对象获取继承父类型的泛型字节码对象, * 通过这个字节码对象获取泛型参数,获取泛型参数的对应字节码对象 * 通过这个字节码对象创建泛型对应对象 */ ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass(); Class<T> clazz = (Class<T>) type.getActualTypeArguments()[0]; try { model = clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); System.out.println("模型对象创建失败!"); } } // 接收到分页的参数 protected int page; protected int rows; public void setPage(int page) { this.page = page; } public void setRows(int rows) { this.rows = rows; } /** * 一个将分页数据压入到值栈顶部的方法 * @param pagedata 页面数据 */ protected void pushPageDataToValueStack(Page<T> pagedata){ long total = pagedata.getTotalElements(); // 数据条数 List<T> content = pagedata.getContent(); // 页面数据 // 创建集合存储数据 Map<String, Object> result = new HashMap<>(); result.put("total",total); result.put("rows", content); // 数据存入值栈 ActionContext.getContext().getValueStack().push(result); } }
这部分代码相对而言比较复杂的就是就是怎么实例化泛型对象那一段了,我已经写了注释,这里不再多说
对于这种抽取base的思想其实很常见,除了baseAction,我们还可以抽取baseDao,这样让代码做到最大程度
的精简,提高代码的复用性,也能提高我们开发的效率,今天先写到这里,有时间继续更新,有什么不对的希望