项目架构优化之路-------持续更新

简介: 前言:对于一个项目的优化,主要分为两种,一种是设计优化,一种是性能优化性能优化主要是值对代码执行效率的优化,比如缓存优化,算法优化等都属于性能优化,而设计优化则是值对代码逻辑以及代码结构的精简和代码可读性的优化。

案例:(设计优化,项目基于SSH框架,并且使用spring-data-jpa)

这里以一个基于ssh框架的项目为例,相信大家都知道,我们在使用ssh框架完成一个项目的时候,通常会为每一

个实体类的对象创建一个对应的action,创建项目目录如下:

微信图片_20221110171000.png

我们现在要做的就是对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,这样让代码做到最大程度

的精简,提高代码的复用性,也能提高我们开发的效率,今天先写到这里,有时间继续更新,有什么不对的希望


相关文章
|
5天前
|
设计模式 前端开发 测试技术
Flutter 项目架构技术指南
探讨Flutter项目代码组织架构的关键方面和建议。了解设计原则SOLID、Clean Architecture,以及架构模式MVC、MVP、MVVM,如何有机结合使用,打造优秀的应用架构。
Flutter 项目架构技术指南
|
5天前
|
机器学习/深度学习 搜索推荐 算法
深度学习推荐系统架构、Sparrow RecSys项目及深度学习基础知识
深度学习推荐系统架构、Sparrow RecSys项目及深度学习基础知识
|
5天前
|
设计模式 前端开发 Java
KnowStreaming系列教程第二篇——项目整体架构分析
KnowStreaming系列教程第二篇——项目整体架构分析
49 0
|
5天前
|
前端开发 JavaScript Java
电商4.0项目【二】: 架构搭建
电商4.0项目【二】: 架构搭建
43 0
|
5天前
|
SpringCloudAlibaba Java 持续交付
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
204 0
|
2天前
|
前端开发 JavaScript Java
《浅谈架构之路:前后端分离模式》 - 山人行 - 博客园,前端开发新手项目
《浅谈架构之路:前后端分离模式》 - 山人行 - 博客园,前端开发新手项目
|
5天前
|
Java Maven Nacos
项目架构设计
项目架构设计
15 1
|
5天前
|
前端开发 JavaScript 数据库
Flask狼书笔记 | 09_图片社交网站 - 大型项目的架构与需求(2)
9.8 收藏图片 前面已经学习过如何使用关联表来表示多对多关系,缺点是只能表示关系,不能存储数据(如我还想记录下收藏图片的时间戳)。这种情况下,我们可以使用关联模型来表示多对多关系。 在关联模型中,我们将Photo模型与User模型的多对多关系,分离成了User模型和Collect模型的一对多关系,和Photo模型与Collect模型的一对多关系。
91 0
|
5天前
|
消息中间件 并行计算 网络协议
探秘高效Linux C/C++项目架构:让进程、线程和通信方式助力你的代码飞跃
探秘高效Linux C/C++项目架构:让进程、线程和通信方式助力你的代码飞跃
48 0
|
5天前
|
缓存 监控 安全
如何设计大型项目技术运营服务架构
【2月更文挑战第3天】如何设计大型项目技术运营服务架构
349 1