JDK8以上函数式接口与Lambda方法引用实战

目录

一、业务场景demo

数据库

SpringBoot持久层代码

SpringBoot业务层代码

SpringBoot后端控制器代码

通用结果封装类Result

前端Vue代码

最终效果:

二、代码分析

1、业务层代码冗余

2、解决方式

3、代码优化

三、测试


一、业务场景demo

数据库

三个数据库,分别是employee、animal、shop

SpringBoot持久层代码

ShopMapper

EmployeeMapper

/**
 * @author LiChao
 * @version v1.0.0.20240522_base
 * @date 2024/05/22 21:46
 * @description ...
 */
@Repository
public interface EmployeeMapper {

    @Select("SELECT * FROM employee WHERE e_id = #{id}")
    Employee getEmployeeById(Integer id);

    @Select("SELECT * FROM employee")
    List<Employee> getAllEmployee();

}

AnimalMapper

/**
 * @author LiChao
 * @version v1.0.0.20240522_base
 * @date 2024/05/22 21:46
 * @description ...
 */
@Repository
public interface AnimalMapper {

    @Select("SELECT * FROM animal WHERE a_id = #{id}")
    Animal getAnimalById(Integer id);

    @Select("SELECT * FROM animal")
    List<Animal> getAllAnimal();

}

SpringBoot业务层代码

ShopServiceImpl

/**
 * @author LiChao
 * @version v1.0.0.20240523_base
 * @date 2024/05/23 9:57
 * @description ...
 */
@SuppressWarnings({"all"})
@Service
public class ShopServiceImpl implements ShopService {

    @Autowired
    private ShopMapper shopMapper;

    @Override
    public Result<Shop> getShopById(Integer id) {
        Shop shopById = shopMapper.getShopById(id);
        if (shopById != null) {
            return Result.success(shopById);
        }
        return Result.fail("查询的店铺不存在");
    }
}

EmployeeServiceImpl

/**
 * @author LiChao
 * @version v1.0.0.20240522_base
 * @date 2024/05/22 21:53
 * @description ...
 */
@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Resource
    private EmployeeMapper employeeMapper;

    @Override
    public Result<Employee> getEmployeeById(Integer id) {
        Employee employeeById = employeeMapper.getEmployeeById(id);
        if (employeeById != null) {
            return Result.success(employeeById);
        }
        return Result.fail("查询的员工不存在");
    }

    @Override
    public Result<List<Employee>> getAllEmployee() {
        return Result.success(employeeMapper.getAllEmployee());
    }
}

AnimalServiceImpl

/**
 * @author LiChao
 * @version v1.0.0.20240522_base
 * @date 2024/05/22 21:52
 * @description ...
 */
@Service
public class AnimalServiceImpl implements AnimalService {

    @Resource
    private AnimalMapper animalMapper;

    @Override
    public Result<Animal> getAnimalById(Integer id) {
        Animal animalById = animalMapper.getAnimalById(id);
        if (animalById != null) {
            return Result.success(animalById);
        }
        return Result.fail("查询的动物不存在");
    }

    @Override
    public Result<List<Animal>> getAllAnimal() {
        return Result.success(animalMapper.getAllAnimal());
    }
}

SpringBoot后端控制器代码

这里为了测试方便,就将所有业务都写在一个控制器里了

/**
 * @author LiChao
 * @version v1.0.0.20240522_base
 * @date 2024/05/22 21:33
 * @description ...
 */
@RestController
@RequestMapping("/api")
public class SingleParameterController {

    @Resource
    private EmployeeService employeeService;
    @Resource
    private AnimalService animalService;
    @Resource
    private ShopService shopService;

    @GetMapping("animal/{id}")
    public Result<Animal> getAnimalById(@PathVariable("id") int id) {
        return animalService.getAnimalById(id);
    }

    @GetMapping("employee/{id}")
    public Result<Employee> getEmployeeById(@PathVariable("id") int id) {
        return employeeService.getEmployeeById(id);
    }

    @GetMapping("shop/{id}")
    public Result<Shop> getShopById(@PathVariable("id") int id) {
        return shopService.getShopById(id);
    }

}

通用结果封装类Result

/**
 * @author LiChao
 * @version v1.0.0.20240325_base
 * @date 2024/03/25 20:06
 * @description ...
 */
@Data
public class Result<T> implements Serializable {

    private static final int SUCCESS_CODE = 1;
    private static final int ERROR_CODE = 0;

    /**
     * 状态码:1为成功,0为失败
     */
    private Integer code;

    /**
     * 错误信息
     */
    private String msg;

    /**
     * 成功数据
     */
    private T data;

    /**
     * 成功回调
     *
     * @param data
     * @param <T>
     * @return
     */
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(SUCCESS_CODE);
        result.setData(data);
        return result;
    }

    public static <T> Result<T> success() {
        Result<T> result = new Result<>();
        result.setCode(SUCCESS_CODE);
        result.setData(null);
        return null;
    }

    /**
     * 失败回调
     *
     * @param msg
     * @return
     */
    public static <T> Result<T> fail(String msg) {
        Result<T> result = new Result<>();
        result.setCode(ERROR_CODE);
        result.setMsg(msg);
        return result;
    }

}

这里的Result类就是我自己封装的一个返回通用结果的类,返回了和前端协调好的响应码,响应数据,错误信息等

前端Vue代码

这里就简单的使用ElementUI的Notification通知组件和Button组件+Axios简单的模拟用户请求,这里为了防止后端接口暴露并且希望统一往后端发起请求,所以使用了自己封装了一个http.js工具类向指定的后端URL发请求

<template>
    <div class="sigle-param">
        <el-row>
            <el-button type="primary" plain @click="getEmployeeById(938742)">根据id查询员工</el-button>
        </el-row>
        <br /><br />
        <el-row>
            <el-button type="success" plain @click="getAnimalById(33)">根据id查询动物</el-button>
        </el-row>
        <br /><br />
        <el-row>
            <el-button type="warning" plain @click="getShopById(2)">根据id查询商品</el-button>
        </el-row>
        <br /><br />
    </div>
</template>

<script>
import axios from 'axios';
export default {
    name: 'SingleParameter',
    methods: {
        getEmployeeById(id) {
            axios.get("/api/employee/" + id)
                .then(response => {
                    const result = response.data
                    if (result.code === 1) {
                        this.$notify({
                            title: '提示',
                            message: '获取到的员工姓名为:' + result.data.ename + ', 年龄为:' + result.data.eage,
                            duration: 0
                        });
                    } else {
                        this.$message.warning(result.msg)
                    }
                }).catch(error => {
                    this.$message.error("错误请求" + error)
                })
        },
        getAnimalById(id) {
            axios.get("/api/animal/" + id)
                .then(response => {
                    const result = response.data
                    if (result.code === 1) {
                        this.$notify({
                            title: '提示',
                            message: '这个动物是:' + result.data.atype + ', 它的行为是:' + result.data.aaction,
                            duration: 0
                        });
                    } else {
                        this.$message.warning(result.msg)
                    }
                }).catch(error => {
                    this.$message.error("错误请求" + error)
                })
        },
        getShopById(id) {
            axios.get("/api/shop/" + id)
                .then(response => {
                    const result = response.data
                    if (result.code === 1) {
                        this.$notify({
                            title: '提示',
                            message: '获取到的商品编号为:' + result.data.sid + ', 价格为:' + result.data.price,
                            duration: 0
                        });
                    } else {
                        this.$message.warning(result.msg)
                    }
                }).catch(error => {
                    this.$message.error("错误请求" + error)
                })
        },
    },
}
</script>

<style scoped>
.sigle-param {
    margin: auto;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}
</style>

最终效果:

点击按钮请求不同的数据

二、代码分析

1、业务层代码冗余

        如果我们每次都根据id(或者说主键)做什么什么操作时候,都需要非空判断,甚至业务复杂的时候,需要更多的判断,什么是否为会员,是否过期,名字是否合法等等,对于不同的接口,业务逻辑也不一样,所以有时候我们会在每个接口都写一遍,这就很冗余了。

2、解决方式

        我们可以定义一个通用的类,来将比如说传入一个参数的方法对数据库的查询封装起来,下次需要判断,直接调用该类的相应方法就可以,那么,我们怎么知道这个类是什么呢?参数是什么类型呢?以及这段查询数据库的方法又是什么呢?参数类型好说,使用泛型编程就可以完美的解决,类的类型可以使用反射机制拿到Class对象来推断出方法返回值的泛型,那这段查询数据库的逻辑怎么封装呢?我们知道,在Java中,有一种接口叫函数式接口,是不是就可以很好的解决我们的问题呢?那么在Java中需要传入一个参数,并且有返回值的函数式接口是谁呢?就是我们的Function<T, R>,它有一个apply方法刚好符合我们的需要,所以接下来我们创建一个类,并且把这个方法创建出来的结果就会变成这样:

/**
 * @author LiChao
 * @version v1.0.0.20240522_base
 * @date 2024/05/22 22:58
 * @description ...
 */
@Component
public class CommonCheckClient {

    public <R, P> Result<R> singleParam(P param, Class<R> type, Function<P,R> fun, String msg) {
        R r = fun.apply(param);

        // TODO: 合法性验证......

        // TODO: 是否逻辑过期验证......

        // TODO: 开启异步线程资源调度......

        // TODO: 获取分布式锁......

        // TODO: 微服务网关是否成功路由......

        // TODO:    甚至还有会员机制,业务需求是某接口的访问要判断该用户是否为会员,一般我们会
        //       写在拦截器或者微服务网关Gateway中,但是出于某些特殊场景,不适合写在全局的服务
        //       里面,也可以使用这样的方法抽象出来使代码复用
        //

        // TODO: 等等等......

        /**
         * 这里就以一个简单的非空判断作为案例说明
         * (其实这里直接 return r就可以,因为 r 为 null 就是 null,有值自然就返回了,这里只是举个例子)
         */
        if (r != null) {
            return Result.success(r);
        }
        // 都没有成立,返回错误信息
        return Result.fail(msg);
    }

}

将它注入到IOC容器中供我们依赖注入使用

3、代码优化

        这里就以AnimalService为例,我们可以将查询数据库的方法单独抽出来作为一个独立的方法,返回的就是数据库查出来的实体类对象,然后将该实体类放到通用方法中进行一系列的校验,然后返回给控制器

修改后的代码为:

/**
 * @author LiChao
 * @version v1.0.0.20240522_base
 * @date 2024/05/22 21:52
 * @description ...
 */
@Service
public class AnimalServiceImpl implements AnimalService {

    @Resource
    private AnimalMapper animalMapper;

    @Resource
    private CommonCheckClient commonCheckClient;

    @Override
    public Result<Animal> getAnimalById(Integer id) {
        return commonCheckClient.singleParam(id, Animal.class, this::getById, "查询的动物不存在");
    }

    public Animal getById(Integer id) {
        return animalMapper.getAnimalById(id);
    }

    @Override
    public Result<List<Animal>> getAllAnimal() {
        return Result.success(animalMapper.getAllAnimal());
    }
}

        这里传入参数id,再传入返回值的类型Class对象,然后将查询数据库的逻辑使用方法引用传给函数式接口,传入如果失败后的错误信息,最终返回给前端。然后就是因为这个demo中的业务实在太简单了,所以我们感觉代码量变多了,还不如原来的,可是如果判断多了,这个是不是就非常的妙了呢?

同样其他的类的代码也变得非常优雅:

三、测试

这里我临时将id改成了不存在的id,前端测试成功!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值