springboot 通用功能的开发

18 篇文章 0 订阅
6 篇文章 0 订阅

在开发过程中,我们常常会想,也许每天都是重复的CRUD类的工作,而基于面向对象又有很丰富的特性,例如:封装,继承,多态等特性。那么完全可以将通用的部分的代码抽离出来。极大的简化web开发。

  什么样的功能可以被抽离?其实对于大家平常开发都会用的功能都可以抽离出来,一张表的增删改查。分页查询,批量操作等。对于缓存的操作,也可以抽离出来。尽可能的解除开发的耦合性,

  对于通用功能的实现,也许对于不同的公司,不同的业务都有不同的实现,那么就简单的介绍一下我的实现吧。

项目环境:springboot2.14

                  tk.mybatis  通用mapper

                  lombok

如何去配置通用的mapper 以及部署环境就不在这里过多的阐述了

下面来看一下具体的实现。

通用的  api- controller   接口的实现 。(在这个层级里主要是对crud操作的实现)

/**
 * 通用的rest接口 用于拓展 api controller  在继承层级中 第二层 业务解耦合性 使简单业务逻辑开发更容易
 * 懒人必备 ....!!!!
 *
 * @author 徐塬峰
 * @email 986771570@qq.com
 * @date 2019-06-30
 * @description
 **/
@RestController
public abstract class BaseApiController<T, S extends CrudService<T, ID>, ID extends Serializable> extends SimpleController {
    /**
     * 注入service 层 实现通用的crud方法
     */
    @Autowired
    protected S service;
    /**
     * 插入
     * entity
     * @return
     */
    @RequestMapping("/save")
    @ApiOperation(value = "通用的api插入接口", notes = "插入一条数据")
    public JsonResult save(T entity) {
        service.save(entity);
        return JsonResult.ok();
    }
    /**
     * 更新  不为null的字段
     *
     * @param entity
     * @return
     */
    @RequestMapping("/update")
    @ApiOperation(value = "通用的更新接口", notes = "更新一条数据")
    public JsonResult update(T entity) {
        service.updateByEntity(entity);
        return JsonResult.ok();
    }
    /**
     * 根据id删除一条数据
     *
     * @param id
     * @return
     */
    @RequestMapping("/deleteById/{id}")
    @ApiOperation(value = "通用的删除接口", notes = "根据id来删除一条数据")
    public JsonResult deleteById(@PathVariable("id") ID id) {
        service.deleteById(id);
        return JsonResult.ok();
    }
    /**
     * 删除一条数据
     * @param entity
     * @return
     */
    @RequestMapping("/deleteEntity")
    public JsonResult deleteEntity(T entity) {
        return JsonResult.ok(service.deleteByEntity(entity));
    }

    /**
     * 查询出所有的数据
     *
     * @return
     */
    @RequestMapping("/selectAll")
    @ApiOperation(value = "通用的查询接口", notes = "查询出所有的表数据")
    public JsonResult selectAll() {
        return JsonResult.ok(service.selectAll());
    }

    /**
     * 根据id查询出一条数据
     *
     * @return
     */
    @RequestMapping("/selectOne/{id}")
    @ApiOperation(value = "通用的查询接口", notes = "查询出所有的表数据")
    public JsonResult selectOne(@PathVariable("id") ID id) {
        return JsonResult.ok(service.selectById(id));
    }
    /**
     * 分页 查询出所有数据 与 bootstrap-table整合 !!!
     */
    @ResponseBody
    @RequestMapping("/pageList")
    public JsonResult queryAll(Integer page,Integer pageSize)
    {
        return JsonResult.ok(service.queryAll(page,pageSize));
    }

}

通用的controller接口的实现(在这一层级里主要是对要跳转的页面进行封装)

/**
 * @author 徐塬峰
 * @email 986771570@qq.com
 * @date 2019-06-30
 * @description 用于拓展controller 第二层
 * 懒人必备 ....!!!!
 **/
@Controller
@Api(hidden = true)
public abstract class BaseController<T, S extends BaseService<T>> extends SimpleController {
    /**
     * 基础路径
     */
    protected String basePathPrefix;
    /**
     * 添加
     */
    protected String PAGE_ADD = "/add";
    /**
     * 列表
     */
    protected String PAGE_LIST = "/list";
    /**
     * 显示详情
     */
    protected String PAGE_DETAILS = "/details";
    /**
     * 编辑
     */
    protected String PAGE_EDIT = "/edit";
    /**
     * 错误页面
     */
    protected String PAGE_ERROR = "error/500";

    /**
     * 跳转到添加页面
     *
     * @return
     */
    @SysLog
    @RequestMapping("/add")
    public String add() {
        return basePathPrefix + PAGE_ADD;
    }

    /**
     * 跳转到编辑页面
     *
     * @return
     */
    @SysLog
    @RequestMapping("/edit")
    public String edit() {
        return basePathPrefix + PAGE_EDIT;
    }

    /**
     * 跳转到列表
     *
     * @return
     */
    @SysLog
    @RequestMapping("/list")
    public String list() {
        return basePathPrefix + PAGE_LIST;
    }

    /**
     * 跳转到列表
     *
     * @return
     */
    @SysLog
    @RequestMapping("/details")
    public String details() {
        return basePathPrefix + PAGE_DETAILS;
    }


//    protected SpringDataWebProperties.Pageable getPageRequest() {
//        String offsetStr = request.getParameter("offset");
//        String limitStr = request.getParameter("limit");
//        if (StringKit.isBlank(offsetStr) && StringKit.isBlank(limitStr)) {
//            // 表示不分页
//            return null;
//        } else {
//            int offset = 0;
//            int limit = 5;// 相当于pageSize
//            int pageNumber = 0;// 如果是pageRequest对象,页码起始值为0
//            if (StringKit.isNotBlank(limitStr)) {
//                limit = Integer.valueOf(limitStr);
//            }
//            if (StringKit.isNotBlank(offsetStr)) {
//                // 转为页码
				offset = Integer.valueOf(offsetStr);
				pageNumber = offset / limit;
//                  offset = Integer.valueOf(offsetStr);
//                pageNumber = offset - 1;
//            }
//            return PageRequest.of(pageNumber, limit, getSort());
//        }
//    }

}

       继承层级最高的SimpleController (对项目中controller通用的资源进行注入,例如request session 日志等都可以放在这一层级里)

/**
 * @author 徐塬峰
 * @email 986771570@qq.com
 * @date 2019-07-07
 * @description 用于拓展 controller 第一层
 **/
public class SimpleController {


    /**
     * 统一日期转换处理
     *
     * @param binder
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat,
                true));// CustomDateEditor为自定义日期编辑器
    }
    /**
     * 日志对象
     *
     * @since 1.0.0
     */
    protected static Logger log = LoggerFactory.getLogger(SimpleController.class);

    @Autowired
    protected HttpServletRequest request;

    @Autowired
    protected HttpSession session;


    /**
     * HttpServletResponse
     *
     * @since 1.0.0
     */
    @Autowired
    protected HttpServletResponse response;

    /**
     * Spring applicationContext
     *
     * @since 1.0.0
     */
    protected ApplicationContext applicationContext;


    /**
     * 获取webBasePath【如 http://127.0.0.1:8080/projectname】
     *
     * @return webBasePath
     * @since 1.0.2
     */
    protected String getWebBasePath() {
        return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
    }


    /**
     * 获取来访的ip地址
     *
     * @param request
     * @return ip地址
     * @since 1.0.6
     */
    protected String getRemortIP(HttpServletRequest request) {
        if (request.getHeader("x-forwarded-for") == null) {
            return request.getRemoteAddr();
        }
        return request.getHeader("x-forwarded-for");
    }

    /**
     * 获取经过nginx后的ip地址
     *
     * @param request
     * @return ip地址
     * @since 4.0.1
     */
    protected String getNginxForwardIp(HttpServletRequest request) {

        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (!CommonUtils.isEmpty(ip)) {
            ip = ip.split(",")[0];
        }
        return ip;

    }

}

通用的Service层 crudService 主要是定义通用的接口

/**
 * @author 徐塬峰
 * @email 986771570@qq.com
 * @date 2019-07-24
 * @description
 **/
public interface CrudService<T,ID> {

     T selectOne(T entity);
     void deleteById(ID id);
     int deleteByEntity(T entity);
     int updateByEntity(T entity);
     void deleteBatch(ID[] ids);
     void save(T Entity);
     List<T> selectAll();
     T selectById(ID id);
     PageResult queryAll(Integer page, Integer pageSize);
}

通用的BaseService

/**
 * @author 徐塬峰
 * @email 986771570@qq.com
 * @date 2019-06-30
 * @description
 **/
public interface BaseService<T> {

  

}

对于通用的service的具体实现 serviceImpl ,同样在这一层级里注入的通用的mapper,crud的操作也封装在这里

/**
 * @author 徐塬峰
 * @email 986771570@qq.com
 * @date 2019-07-23
 * @description 通用的crud service 用于拓展 service  第一层 实现简单的crud方法 需要继承 MyMapper
 * 注意 有些service 可能用不到 crud  那么请选择 BaseServiceImpl 通用的业务抽离 解耦合
 **/
@Slf4j
public abstract class CrudServiceImpl<T, S extends MyMapper<T>, ID extends Serializable> implements CrudService<T, ID> {

    @Autowired
    protected S mapper;

    protected Class<T> tClass;

    /**
     * 查询出一条数据
     *
     * @param entity
     * @return
     */
    @Override
    public T selectOne(T entity) {
        return mapper.selectOne(entity);
    }

    /**
     * 删除一条数据 根据id
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteById(ID id) {
        try {
            val entity = tClass.newInstance();
            BeanUtils.setProperty(entity, "id", id);
            mapper.deleteByPrimaryKey(entity);
        } catch (InstantiationException e) {
            throw new RuntimeException("删除一条数据 创建对象" + tClass + "发生了错误");
        } catch (IllegalAccessException e) {
            throw new RuntimeException("删除一条数据 创建对象" + tClass + "发生了错误");
        } catch (InvocationTargetException e) {
            throw new RuntimeException("BeanUtils设置对象的id值发生了错误");
        }

    }
    /**
     * 删除一条数据
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteByEntity(T entity) {
        return mapper.deleteByPrimaryKey(entity);
    }

    /**
     * 更新不为null的字段
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateByEntity(T entity) {
        return mapper.updateByPrimaryKeySelective(entity);
    }

    /**
     * 批量删除
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(ID[] ids) {
        if (ArrayUtils.isEmpty(ids)) {
            return;
        }
        try {
            for (ID id : ids) {
                val entity = tClass.newInstance();
                //再通过BeanUtils来给对象设置id值
                BeanUtils.setProperty(entity, "id", id);
                mapper.deleteByPrimaryKey(entity);
            }
        } catch (InstantiationException e) {
            throw new RuntimeException("批量删除 创建对象" + tClass + "发生了错误");
        } catch (IllegalAccessException e) {
            throw new RuntimeException("批量删除 创建对象" + tClass + "发生了错误");
        } catch (InvocationTargetException e) {
            throw new RuntimeException("BeanUtils设置对象的id值发生了错误");
        }
    }

    /**
     * 插入一条数据到数据库
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(T entity) {
        mapper.insertSelective(entity);
    }

    /**
     * 查询出所有的数据
     *
     * @return
     */
    @Override
    public List<T> selectAll() {
        return mapper.selectAll();
    }

    /***
     * 根据id查询出一条数据
     * @param id
     * @return
     */
    @Override
    public T selectById(ID id) {
        try {
            val entity = tClass.newInstance();
            BeanUtils.setProperty(entity, "id", id);
            return mapper.selectOne(entity);
        } catch (InstantiationException e) {
            throw new RuntimeException(" 创建对象" + tClass + "发生了错误");
        } catch (IllegalAccessException e) {
            throw new RuntimeException(" 创建对象" + tClass + "发生了错误");
        } catch (InvocationTargetException e) {
            throw new RuntimeException("BeanUtils设置对象的id值发生了错误");
        }
    }
    /**
     * 分页查询所有数据
     * @param page
     * @param pageSize
     */
    @Override
    public PageResult queryAll(Integer page, Integer pageSize) {
        PageHelper.startPage(page,pageSize);
        List<T> list= mapper.selectAll();
        PageInfo<T> pageList = new PageInfo<T>(list);
        PageResult pageResult = new PageResult();
        pageResult.setPage(page);
        pageResult.setRecords(pageList.getPages());
        pageResult.setRows(list);
        pageResult.setRecords(pageList.getTotal());
        return pageResult;
    }
}

通用的Mapper,可以在此基础上进行你的拓展

/**
 * 通用的mapper工具类 用于拓展mapper  第一层
 * @param <T>
 */
public interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {

}

 

在完成上述的封装框架之后,web开发就变得更加简单了。

Controller层只需要继承BaseController,并重写basePathfix 路径地址 即可 获得crud template 前端页面地址。

@Controller
@RequestMapping("/drawCrash")
@Api(hidden = true)
public class DrawCrashController extends BaseController {

    public DrawCrashController()
    {
        basePathPrefix="xuanwang/wallet/drawCrash";
    }

}

Controller-api层则需要传入service ,model 类,主键类型

@RestController
@RequestMapping("/user")
@Api(value = "用户接口", tags = {"用户接口"})
public class UserApiController extends BaseApiController<User, UserService, Long> {
    /**
     * 封号
     */
    @RequestMapping("/ban/{userId}")
    public JsonResult banUser(@PathVariable("userId") Long userId) {
        return JsonResult.ok(service.banUser(userId));
    }


}

简单的介绍就到这里吧,如需要更多的案列欢迎  项目代码: https://github.com/RAOE/next1-boot  

群号:673926093,看到这里你是不是迫不及待的想要去封装自己的springboot拉。如果有更好的框架实现思路也欢迎交流沟通哦

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值