MP的开发流程-2

RESTful的实现等级

  • 0级:传统的RPC,基于SOAP的WS,调用的服务名,参数放在HTTP协议的body里面,同时必须以POST方式提交,问题在于你必须清楚的知道所有服务,子服务,及其参数的信息,并且需要知道各种服务的不同点
  • 1级:利用resource概念,把所有服务都抽取成resource概念,从body中提取到header里,这样做的好处就是如果你知道一个服务地址,你可能无需知道具体服务是什么,依照资源的惯例就访问到服务,比如查询id=1的书籍信息时使用路径/books/1
  • 2级:利用HTTP动词,HTTP定义了4种动词,GET获取服务器资源,POST在服务器上创建新资源,PUT更改服务器上资源,DELETE删除服务器上资源,任何操作都可以看成增删改查,所以利用标准的http verb加上resource(/book/1)就能准确地操作资源,当你不知道服务具体是什么的时候也可以轻易按照惯例访问到服务,然而服务供应商更改服务也需要遵循惯例,不会像RPC那样轻易更改服务接口
  • 3级:最高级别,HATEOS超媒体既应用状态引擎。这个意思是说,对于任何服务都存在很多子服务,你只需要知道第一个服务的入口,便可以依据服务返回结构的自描述性得到下一个服务的入口,这样在服务供应商修改服务的时候,不会影响到客户端的调用

在RESTful应用中需要和前端充分沟通,建议通信的数据规范

@Data
public class JsonResult implements Serializable{
    private int code;//自定义的响应状态码,不是http规范中的响应码,一般用于给前端更详细的信息
    private String message;//服务器端生成的响应提示信息
    private Boolean success;//可有可无,给前端一个简单的响应状态提示
    private Object data;//响应数据

    public static JsonResult success(String message, Object data){
        JsonResult result = new JsonResult();
        result.setMessage(message);
        result.setData(data);
        result.setCode(2000);
        result.setSuccess(true);
        return result;
    }
}

控制器类的定义

@RestController //@Controller + @ResponseBody
@RequestMapping("/catalogs")
public class CatalogController{
    @Autowired //@Resource
    private CatalogService catalogService;
    @GetMapping
    public JsonResult getAllCatalogs(){
        List<Catalog> catalogList = catalogService.list();
        return JsonResult.success("所有类目列表",catalogList);
    }
}

7、使用Postman进行测试

在mysql数据库中插入测试数据

insert into tbl_catalogs values
    (1,"计算机图书","计算机图书"),
    (2,"烹饪图书","做菜图谱"),
    (3,"音乐图书","教你音乐入门");

postman的用法

MP的开发流程-2_第1张图片

常见问题1:应用启动报错UnsatisfiedDependencyException,查看报错信息的详细提示

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'com.yan.mapper.CatalogMapper' available: expected at
least 1 bean which qualifies as autowire candidate. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true)}
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatc
hingBeanFound(DefaultListableBeanFactory.java:1801) ~[spring-beans-
5.3.23.jar:5.3.23]
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDe
pendency(DefaultListableBeanFactory.java:1357) ~[spring-beans-5.3.23.jar:5.3.23]
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDepe
ndency(DefaultListableBeanFactory.java:1311) ~[spring-beans-5.3.23.jar:5.3.23]
at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcesso
r$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.j
ava:656) ~[spring-beans-5.3.23.jar:5.3.23]
... 39 common frames omitted

可以看到报错为com.yan.mapper.CatalogMapper没有注册,所以需要添加MyBatisPlus配置类或者在主类上添加配置

@MapperScan(basePackages = "com.ma.mapper") //注册自动扫描mapper接口,完成mapper接口的注册
@SpringBootConfiguration //用于声明当前类的一个配置类
public class MyBatisPlusConfig{
}

8、针对类目进行分页显示

分页操作可以利用MP中提供的分页插件实现,修改MP的配置类引入分页插件

@MapperScan(basePackages = "com.ma.mapper") //注册自动扫描mapper接口,完成mapper接口的注册
@SpringBootConfiguration //用于声明当前类的一个配置类
public class MyBatisPlusConfig{
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        mybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        intercetpor.addInterceptor(new PaginationInnerInterceptor(ObType.MYSQL);
        return interceptor;
    }
}

控制器类定义,这里需要传递分页参数。传递分页参数的值bean有多种写法

@Data //主要用于供前端实现分页导航
public class PageBean implements Serializable{
    private long pageNum; //当前页码值
    private long maxPage; //最大页面值
    private long rowsNum; // 总行数
    private long rowsPerPage = 15; //每页行数
//另外写法有:添加属性Object data存储查询结果集
}

控制器类

注意:这里新增业务方法listByPage目的在于后前使用redis缓存

@RestController //@Controller + @Responsebody
@RequestMapping("/catalogs")
public class CatalogController{
    @Autowird //@Resource
    private CatalogService catalogService;
    @GetMapping("show")
    public JsonResult getByPage(@RrquestParam(defaultValue = "1")Integer
page,@RequestParam(defaultValue="3") Integer size){
        PageBean pages = new PageBean();
        pages.setPageNum(page);
        pages.setRowsPerPage(size);
        List<Catalog> catalogList = catalogService.listByPage(pages);
        Map<String,Object> map = new HashMap<>();
        map.put("pages",pages);
        map.put("data",catalogList);
        return JsonResult.success("加载成功",map);
    }
}

修改业务方法的实现

实际上具体的分页实现很简单,就是在调用查询之前构建IPage类型的对象,然后调用Mapper接口中所提供的支持Page参数的方法即可

@Service
public class CatalogServiceImpl extends ServiceImpl<CatalogMapper, Catalog>
    implements CatalogService{
    
    @Override
    public List<Catalog> listByPage(PageBean pages){
        List<Catalog> res = new ArrayList<>();
        if(pages == null || pages.getRowsPerPage() < 1){
            //查询所有
            res = this.getBaseMapper().selectList(null);
        } else{
            //分页查询
            if(pages.getPageNum() < 1)
                pages.setPageNum(1);
            Page<Catalog> pageInfo = new Page<>(pages.getPageNum(),pages.getRowsPerPage());
            pageInfo = this.getBaseMapper().selectPage(pageInfo,null);
            res = pageInfo.getRecords();
            pages.setPageNum(pageInfo.getCurrent());
            pages.setRowsNum(pageInfo.getTotal());
            pages.setMaxPage(pageInfo.getPages());
        }
        return res;
    }
}

使用postman测试验证

生成的响应数据为JSON格式的字符串

{
    "code": 2000,
    "success": true,
    "message": null,
    "data": {
        "pages": {
            "pageNum": 1,
            "maxPage": 2,
            "rowsNum": 3,
            "rowsPerPage": 2
        },
        "data": [
            {
                "id": 1,
                "name": "计算机图书",
                "memo": "计算机图书"
            },
            {
                "id": 2,
                "name": "烹饪图书",
                "memo": "做菜的书"
            }
        ]
    }
}

9、针对类目信息发现一般很少修改,但是经常需要执行查询,例如添加商品等操作。比较适合使用redis缓存数据,通过浪费内存以减少数据库的查询此时,从而提供执行性能,应对更高的并发性需求redis缓存开发一般有2种方式,使用注解【推荐】和使用自定义编程实现。缓存可以添加在不同的层面上,一般针对controller缓存,多使用本地缓存Ehcache;如果针对业务层缓存,一般使用支持分布式的Redis;也可以在持久层添加缓存,例如开启的MyBatis的缓存

9.1、首先添加依赖


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
    <groupId>org.apache.commonsgroupId>
    <artifactId>commons-pool2actifactId>
dependency>

9.2、添加Redis序列化器和反序列化器的配置

@SpringBootConfiguration
public class MyRedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();//创建模板类对象
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();//创建String序列化类
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //指定使用jackson工具负责具体的序列化操作
        ObjectMapper om = new ObjectMapper(); //创建jackson的核心api的 ObjectMapper ,将bean,list,map,数组等等转成字符串
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 使用objectmapper设置bean的属性,修饰符
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);  //设置默认类型
        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);  //设置将本地时间转成字符串
        jackson2JsonRedisSerializer.setObjectMapper(om);  //将核心api objectmapper设置给jackson
        template.setConnectionFactory(factory); // 通过工厂得到连接对象
        template.setKeySerializer(redisSerializer);  //设置key序列化方式: 转成字符串
        template.setValueSerializer(jackson2JsonRedisSerializer); // 设置value序列化: 字符串
        template.setHashValueSerializer(jackson2JsonRedisSerializer); // value hashmap序列化
        return template;
    }
}

9.3、添加Redis服务器相关配置

spring:
    redis:
        host: localhost
        port: 6379
        lettuce:
            pool:
                max-active: 8
                min-idle: 3

9.4、通过spring框架提供的模板类RedisTemplate调用API操作Redis数据库

在使用redis缓存之前应该充分沟通,定义一个key的标准,或者查询公司旧有标准,注意不能随机起名

@Service
public class CatalogServiceImpl extends ServiceImpl<CatalogMapper, Catalog>
    implements CatalogService{
    @Autowired
    private RedisTemplate redisTemplate
    private ThreadLocalRandom random = ThreadLocalRandom.current();

    @Override
    public List<Catalog> listByPage(PageBean pages){
        List<Catalog> res = new ArrayList<>();
        if(pages == null || pages.getRowsPerPage() < 1){
            //查询所有
            if(redisTemplate.haskey("catalog::all")){
                //如果在具体开发中比较倾向与使用常量的方式
                Object obj = redisTemplate.opsForValue().get("catalog::all");
                if(obj != null && obj instanceof List)
                    res = (List<Catalog>) obj;
            } else {
                res = this.getBaseMapper().selectList(null);
                if(res != null && res.size() > 0){
                    //为了避免雪崩问题,所以生命周期引入随机数
                    int kk = 100 + random.nextInt(100);
                    redisTemplate.opsForValue().set("catalog:: all",res,
Duration.ofSeconds(kk));
                }
            }
        } else {
            //分页查询,实际上具体的应用中不一定针对分页数据进行缓存 catalog::page::size
            if(pages.getPageNum() < 1)
                pages.setPageNum(1);
            String key = "catalog::" + pages.getPageNum() + "::" +
pages.getRowsPerPage();
            if(this.redisTemplate.hasKey(key)){
                Object obj = redisTemplate.opsForValue().get(key);
                if(obj!=null && obj intanceof List)
                    res = (List < Catalog>) obj;
                if(this.redisTemplate.hasKey(key+"::page")){
                    obj = redisTemplate.opsForValue.get(key + "::page");
                    if(obj != null && obj instance PageBean){
                        PageBean tmp = (PageBean) obj;
                        BeanUtils.copyProPerties(tmp,pages);
                    }
                }
            }
            if(res == null || res.size() < 1){
                Page<Catalog> pageInfo = new Page<>(pages.getPageNUm(),
pages.getRowsPerPage());
                pageInfo = this.getBaseMapper().selectPage(pageInfo,null);
                res = pageInfo.getRecords();
                pages.setPageNum(pageInfo.getCurrent());
                pages.setRowsNum(pageInfo.getTotal());
                pages.setMaxPage(pageInfo.getPages());
                int kk = 100 + random.nextInt(100);
                redisTemplate.opsForValue().set(key,res,Duration.ofSeconds(kk));
            }
        }
        return res;
    }
}

9.5、使用postman测试,可以在控制台上查看是否有对应的SQL语句输出以判断缓存是否生效

你可能感兴趣的:(spring,boot,java,后端)