截止到现在,我们已经写了不少代码了,回顾我们的代码,可以发现有很多可以优化的地方,最明显的就是很多重复代码,现在我们是时候重构了。重构这个过程是在我们开发中经常发生的事情,一旦我们发现有可以优化的地方,尽可能的早点重构,这样会减轻我们后面的工作量。因为我们教程的目的主要是后台开发,所有重构的目标也主要放在我们后台。
1、实体
首先我们重构我们的实体,仔细观察可以发现,所有的实体都实现了Serializable接口,所有的实体都有id主键,虽然主键不一定是id,我们可以约定所有主键就使用id,还有主键的类型,一般主键只要是实现Serializable接口的类型都可以,常见的有Long和String,如果要兼容多种类型的话,我们就可以给实体定义一个泛型,表示用什么类型做主键,其实我们大部分情况下,都是用Long做主键就ok了。
前面讲菜单的时候说过,我们实体一般分为普通实体和树形实体,我们再看树形实体一般有什么属性,有parent属性,children属性,addChild方法。
分析完,我们现在可以动手了,我们新建一个BaseEntity实体,作为所有实体的基类
这里使用了@MappedSuperclass注解,该注解表示注解的类将不是一个完整的实体类,他将不会映射到数据库表,但是他的属性都将映射到其子类的数据库字段中。具体可以了解下JPA或者hibernate实体继承的实现方式。
我们在新建一个BaseTreeEntity实体,他将继承BaseEntity,所有的树形对象将继承他。
然后我们将之前的实体分别继承这两个实体,只有Menu是基础BaseTreeEntity,其余的都是继承BaseEntity,
处理完所有实体,我们重启应用,测试之前的功能是不是都能正常运行。
2、Repository
同样,我们将Repository也分为BaseRepository和BaseTreeRepository
暂时这两个基类里面没有定义方法。
同样的,我们将之前的Repository继承相应的基类。
处理完,我们再重启测试下功能,每重构一部分,我们尽量早点测试,避免后面出现问题不好处理。
3、Service
之前我们的业务都比较简单,就省去了Service这一层,其实一般属性java web开发的同学知道我们还需要有Service的,为了规范架构,我们现在加上Service,加Service的同时,我们先设计Service的基类,我们的基类要包括常用的增删改查方法。
IBaseService:
这里首先我们定义了一个getRepository方法,用来获取对应的Repository,然后定义了单个保存和批量保存的方法,更新和保存方法是一致的,如果实体里面主键有值就是更新,没值就是新增。删除同样也提供了常用的几个方法。
查询方法有定义了很多,满足我们大多数情况下的要求,根据id查询,QBE查询,分页查询,然后也允许自定义hql查询。
BaseService:
这里,我们注入了entityManager,jdbcTemplate,namedParameterJdbcTemplate,entityManager是这里用到了的,jdbcTemplate和namedParameterJdbcTemplate是为了方便以后具体的实现类在遇到复杂场景时,可以自己写sql。
在默认的构造方法里面,我们通过反射获取当前类具体的泛型类型,因为后面的查询我们会用到。
因为getRepository我们没有实现,这个将在具体的Service里面实现,所以BaseService是一个抽象类。
这里我们使用了事务注解,对于需要事务的方法,我们最好显示的声明事务,特别是一个service方法里面进行多次修改操作的。
IBaseTreeService
BaseTreeService
现在我们添加对应业务的service
其余几个service就不列了。将service都写好后,我们需要修改Controller,原则上Controller不能直接使用Repostiory来操作数据库,全部得通过Service来进行数据持久化。
我们先修改PostController,去掉以前多余的测试方法,由Repostiory改成Service
MenuController
其他Controller类似修改,然后进行测试。
4、Controller
最后我们对Controller来进行重构。
BaseEntityController
BaseEntityController比较复杂,分为几个点,首先,我们像Service注入Repository一样,在Controller里面提供抽象方法注入Service,然后提供三个抽象方法来获取列表页面,新增页面,编辑页面对应的模板位置,再然后是通用的增删改查方法实现,这里注意下updateOld方法,默认updateOld方法什么也不做,即编辑不会做任何修改,如果子类需要编辑生效,必须覆盖该方法,最后,因为权限字符串是变化的,我们不能再在方法注解上进行权限控制,必须改成方法调用的方式,默认不进行权限判断。
BaseTreeEntityController
这里我们看两个具体的实现类
PermController
MenuController
通过这两个实现类,可以看到,代码还是有些冗余,其实我们还可以进一步优化,这里好几个方法都是提供一个字符串,这个操作其实我们可以自定义类级别的注解,类似下面这样
我们还可以在基类里面定义很多类似updateOld一样的钩子函数,例如在查询前先执行什么操作,或者在查询后做什么操作,这样可以大大增强基类方法的重复利用,减少子类的冗余代码,像在修改UserController的时候发现,保存时,我们先必须对密码进行加密,我们可以在UserController里面重写save方法,但是最好是在基类里面留一个钩子。
我们重新在扩充一下BaseEntityController,加上几个钩子。
BaseTreeEntityController也同样
这样,我们在UserController里面就不需要覆盖save方法,只需要实现beforeSave方法就行了
5、总结
这节内容看起来不起眼,但是东西很重要,这里面很多都是底层的设计,一般都是由架构师来完成的,可以看到,我们大量使用了继承和泛型的特性,现在我们的代码结构很清晰了,Controller-Service-Repository-Entity,我们所有的业务功能无非就是增删改查,底层架构都是为这些功能服务。
重构是一项长期的工作,一旦你发现自己代码写得不好,有可以优化的地方,恭喜你,你就可以重构了,架构师的进步就是在一次次的重构过程中,重构永没有止境,因为没有最好,只有更好,我们后面还会根据需要进行重构。
代码:
https://github.com/www15119258/springboot-study/tree/branch27
网友评论