使用RESTful风格开发Java Web

Java电商秒杀系统性能优化(一)——电商秒杀系统框架回顾 电商秒杀系统框架回顾项目简介外部依赖框架回顾项目要点项目中存在的问题小结 课程是免费的,课程地址如下:SpringBoot搭建电商秒杀项目,课程真的很棒,作者的思路很清晰,建议各位读者可以跟着视频练习一下这个项目; 项目简介 通过SpringBoot快速搭建的前后端分离的电商基础秒杀项目。项目通过应用领域驱动型的分层模型设计方式去完成:用户otp注册、登陆、查看、商品列表、进入商品详情以及倒计时秒... 阅读详情

转载自:https://www.jianshu.com/p/91600da4df95

什么是RESTful风格?

REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移),REST 是一种体系结构,而 HTTP 是一种包含了 REST 架构属性的协议,为了便于理解,我们把它的首字母拆分成不同的几个部分:

  • 表述性(REpresentational): REST 资源实际上可以用各种形式来进行表述,包括 XML、JSON 甚至 HTML——最适合资源使用者的任意形式;
  • 状态(State): 当使用 REST 的时候,我们更关注资源的状态而不是对资源采取的行为;
  • 转义(Transfer): REST 涉及到转移资源数据,它以某种表述性形式从一个应用转移到另一个应用。

简单地说,REST 就是将资源的状态以适合客户端或服务端的形式从服务端转移到客户端(或者反过来)。在 REST 中,资源通过 URL 进行识别和定位,然后通过行为(即 HTTP 方法)来定义 REST 来完成怎样的功能。

实例说明:

在平时的 Web 开发中,method 常用的值是 GET 和 POST,但是实际上,HTTP 方法还有 PATCH、DELETE、PUT 等其他值,这些方法又通常会匹配为如下的 CRUD 动作:

CRUD 动作HTTP 方法
CreatePOST
ReadGET
UpdatePUT 或 PATCH
DeleteDELETE

尽管通常来讲,HTTP 方法会映射为 CRUD 动作,但这并不是严格的限制,有时候 PUT 也可以用来创建新的资源,POST 也可以用来更新资源。实际上,POST 请求非幂等的特性(即同一个 URL 可以得到不同的结果)使其成一个非常灵活地方法,对于无法适应其他 HTTP 方法语义的操作,它都能够胜任。

在使用 RESTful 风格之前,我们如果想要增加一条商品数据通常是这样的:

/addCategory?name=xxx

但是使用了 RESTful 风格之后就会变成:

/category

这就变成了使用同一个 URL ,通过约定不同的 HTTP 方法来实施不同的业务,这就是 RESTful 风格所做的事情了,为了有一个更加直观的理解,引用一下来自how2j.cn的图:

SpringBoot 中使用 RESTful

下面我使用 SpringBoot 结合文章:http://blog.didispace.com/springbootrestfulapi/ 来实例演示如何在 SpringBoot 中使用 RESTful 风格的编程并如何做单元测试

RESTful API 具体设计如下:

User实体定义:

public class User { 
 
    private Long id; 
    private String name; 
    private Integer age; 
 
    // 省略setter和getter 
     
}

实现对User对象的操作接口

@RestController 
@RequestMapping(value="/users")     // 通过这里配置使下面的映射都在/users下 
public class UserController { 
 
    // 创建线程安全的Map 
    static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>()); 
 
    @RequestMapping(value="/", method=RequestMethod.GET) 
    public List<User> getUserList() { 
        // 处理"/users/"的GET请求,用来获取用户列表 
        // 还可以通过@RequestParam从页面中传递参数来进行查询条件或者翻页信息的传递 
        List<User> r = new ArrayList<User>(users.values()); 
        return r; 
    } 
 
    @RequestMapping(value="/", method=RequestMethod.POST) 
    public String postUser(@ModelAttribute User user) { 
        // 处理"/users/"的POST请求,用来创建User 
        // 除了@ModelAttribute绑定参数之外,还可以通过@RequestParam从页面中传递参数 
        users.put(user.getId(), user); 
        return "success"; 
    } 
 
    @RequestMapping(value="/{id}", method=RequestMethod.GET) 
    public User getUser(@PathVariable Long id) { 
        // 处理"/users/{id}"的GET请求,用来获取url中id值的User信息 
        // url中的id可通过@PathVariable绑定到函数的参数中 
        return users.get(id); 
    } 
 
    @RequestMapping(value="/{id}", method=RequestMethod.PUT) 
    public String putUser(@PathVariable Long id, @ModelAttribute User user) { 
        // 处理"/users/{id}"的PUT请求,用来更新User信息 
        User u = users.get(id); 
        u.setName(user.getName()); 
        u.setAge(user.getAge()); 
        users.put(id, u); 
        return "success"; 
    } 
 
    @RequestMapping(value="/{id}", method=RequestMethod.DELETE) 
    public String deleteUser(@PathVariable Long id) { 
        // 处理"/users/{id}"的DELETE请求,用来删除User 
        users.remove(id); 
        return "success"; 
    } 
 
}

编写测试单元

参考文章:http://tengj.top/2017/12/28/springboot12/#Controller单元测试
看过这几篇文章之后觉得好棒,还有这么方便的测试方法,这些以前都没有接触过...

下面针对该Controller编写测试用例验证正确性,具体如下。当然也可以通过浏览器插件等进行请求提交验证,因为涉及一些包的导入,这里给出全部代码:

package cn.wmyskxz.springboot;

import cn.wmyskxz.springboot.controller.UserController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;


/**
 * @author: @我没有三颗心脏
 * @create: 2018-05-29-上午 8:39
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MockServletContext.class)
@WebAppConfiguration
public class ApplicationTests {

    private MockMvc mvc;

    @Before
    public void setUp() throws Exception {
        mvc = MockMvcBuilders.standaloneSetup(new UserController()).build();
    }

    @Test
    public void testUserController() throws Exception {
        // 测试UserController
        RequestBuilder request = null;

        // 1、get查一下user列表,应该为空
        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[]")));

        // 2、post提交一个user
        request = post("/users/")
                .param("id", "1")
                .param("name", "测试大师")
                .param("age", "20");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));

        // 3、get获取user列表,应该有刚才插入的数据
        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"测试大师\",\"age\":20}]")));

        // 4、put修改id为1的user
        request = put("/users/1")
                .param("name", "测试终极大师")
                .param("age", "30");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));

        // 5、get一个id为1的user
        request = get("/users/1");
        mvc.perform(request)
                .andExpect(content().string(equalTo("{\"id\":1,\"name\":\"测试终极大师\",\"age\":30}")));

        // 6、del删除id为1的user
        request = delete("/users/1");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));

        // 7、get查一下user列表,应该为空
        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[]")));

    }

}

MockMvc实现了对HTTP请求的模拟,从示例的代码就能够看出MockMvc的简单用法,它能够直接使用网络的形式,转换到Controller的调用,这样使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。

需要注意的就是在MockMvc使用之前需要先用MockMvcBuilders构建MockMvc对象,如果对单元测试感兴趣的童鞋请戳上面的链接哦,这里就不细说了

测试信息

运行测试类,控制台返回的信息如下:

 __      __                               __
/\ \  __/\ \                             /\ \
\ \ \/\ \ \ \    ___ ___   __  __    ____\ \ \/'\    __  _  ____
 \ \ \ \ \ \ \ /' __` __`\/\ \/\ \  /',__\\ \ , <   /\ \/'\/\_ ,`\
  \ \ \_/ \_\ \/\ \/\ \/\ \ \ \_\ \/\__, `\\ \ \ \`\\/>  </\/_/  /_
   \ `\___x___/\ \_\ \_\ \_\/`____ \/\____/ \ \_\ \_\/\_/\_\ /\____\
    '\/__//__/  \/_/\/_/\/_/`/___/> \/___/   \/_/\/_/\//\/_/ \/____/
                               /\___/
                               \/__/
2018-05-29 09:28:18.730  INFO 5884 --- [           main] cn.wmyskxz.springboot.ApplicationTests   : Starting ApplicationTests on SC-201803262103 with PID 5884 (started by Administrator in E:\Java Projects\springboot)
2018-05-29 09:28:18.735  INFO 5884 --- [           main] cn.wmyskxz.springboot.ApplicationTests   : No active profile set, falling back to default profiles: default
2018-05-29 09:28:18.831  INFO 5884 --- [           main] o.s.w.c.s.GenericWebApplicationContext   : Refreshing org.springframework.web.context.support.GenericWebApplicationContext@7c37508a: startup date [Tue May 29 09:28:18 CST 2018]; root of context hierarchy
2018-05-29 09:28:19.200  INFO 5884 --- [           main] cn.wmyskxz.springboot.ApplicationTests   : Started ApplicationTests in 1.184 seconds (JVM running for 2.413)
2018-05-29 09:28:19.798  INFO 5884 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/users/{id}],methods=[PUT]}" onto public java.lang.String cn.wmyskxz.springboot.controller.UserController.putUser(java.lang.Long,cn.wmyskxz.springboot.pojo.User)
2018-05-29 09:28:19.800  INFO 5884 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/users/],methods=[GET]}" onto public java.util.List<cn.wmyskxz.springboot.pojo.User> cn.wmyskxz.springboot.controller.UserController.getUserList()
2018-05-29 09:28:19.800  INFO 5884 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/users/],methods=[POST]}" onto public java.lang.String cn.wmyskxz.springboot.controller.UserController.postUser(cn.wmyskxz.springboot.pojo.User)
2018-05-29 09:28:19.801  INFO 5884 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/users/{id}],methods=[DELETE]}" onto public java.lang.String cn.wmyskxz.springboot.controller.UserController.deleteUser(java.lang.Long)
2018-05-29 09:28:19.801  INFO 5884 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/users/{id}],methods=[GET]}" onto public cn.wmyskxz.springboot.pojo.User cn.wmyskxz.springboot.controller.UserController.getUser(java.lang.Long)
2018-05-29 09:28:19.850  INFO 5884 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.test.web.servlet.setup.StubWebApplicationContext@42f8285e
2018-05-29 09:28:19.924  INFO 5884 --- [           main] o.s.mock.web.MockServletContext          : Initializing Spring FrameworkServlet ''
2018-05-29 09:28:19.925  INFO 5884 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : FrameworkServlet '': initialization started
2018-05-29 09:28:19.926  INFO 5884 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : FrameworkServlet '': initialization completed in 1 ms

通过控制台信息,我们得知通过 RESTful 风格能成功调用到正确的方法并且能获取到或者返回正确的参数,没有任何错误,则说明成功!

如果你想要看到更多的细节信息,可以在每次调用 perform() 方法后再跟上一句 .andDo(MockMvcResultHandlers.print()) ,例如:

        // 1、get查一下user列表,应该为空
        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[]")))
                .andDo(MockMvcResultHandlers.print());

就能看到详细的信息,就像下面这样:

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /users/
       Parameters = {}
          Headers = {}
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = cn.wmyskxz.springboot.controller.UserController
           Method = public java.util.List<cn.wmyskxz.springboot.pojo.User> cn.wmyskxz.springboot.controller.UserController.getUserList()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {Content-Type=[application/json;charset=UTF-8]}
     Content type = application/json;charset=UTF-8
             Body = []
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

总结

我们仍然使用 @RequestMapping 注解,但不同的是,我们指定 method 属性来处理不同的 HTTP 方法,并且通过 @PathVariable 注解来将 HTTP 请求中的属性绑定到我们指定的形参上。

事实上,Spring 4.3 之后,为了更好的支持 RESTful 风格,增加了几个注解:@PutMapping、@GetMapping、@DeleteMapping、@PostMapping,从名字也能大概的看出,其实也就是将 method 属性的值与 @RequestMapping 进行了绑定而已,例如,我们对UserController中的deleteUser方法进行改造:

-----------改造前-----------
@RequestMapping(value="/{id}", method=RequestMethod.DELETE)
public String deleteUser(@PathVariable Long id) {
    // 处理"/users/{id}"的DELETE请求,用来删除User
    users.remove(id);
    return "success";
}

-----------改造后-----------
@DeleteMapping("/{id}")
public String deleteUser(@PathVariable Long id) {
    // 处理"/users/{id}"的DELETE请求,用来删除User
    users.remove(id);
    return "success";
}

使用Swagger2构造RESTful API文档

参考文章:http://blog.didispace.com/springbootswagger2/

RESTful 风格为后台与前台的交互提供了简洁的接口API,并且有利于减少与其他团队的沟通成本,通常情况下,我们会创建一份RESTful API文档来记录所有的接口细节,但是这样做有以下的几个问题:

  1. 由于接口众多,并且细节复杂(需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等),高质量地创建这份文档本身就是件非常吃力的事,下游的抱怨声不绝于耳。
  2. 随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象。

Swagger2的出现就是为了解决上述的这些问题,并且能够轻松的整合到我们的SpringBoot中去,它既可以减少我们创建文档的工作量,同时说明内容又可以整合到代码之中去,让维护文档和修改代码整合为一体,可以让我们在修改代码逻辑的同时方便的修改文档说明,这太酷了,另外Swagger2页提供了强大的页面测试功能来调试每个RESTful API,具体效果如下:

让我们赶紧来看看吧:

第一步:添加Swagger2依赖:

在 pom.xml 中加入Swagger2的依赖:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.2.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.2.2</version>
</dependency>

第二步:创建Swagger2配置类

在SpringBoot启动类的同级目录下创建Swagger2的配置类 Swagger2:

@Configuration
@EnableSwagger2
public class Swagger2 {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("cn.wmyskxz.springboot"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Spring Boot中使用Swagger2构建RESTful APIs")
                .description("原文地址链接:http://blog.didispace.com/springbootswagger2/")
                .termsOfServiceUrl("http://blog.didispace.com/")
                .contact("@我没有三颗心脏")
                .version("1.0")
                .build();
    }

}

如上面的代码所示,通过 @Configuration 注解让Spring来加载该配置类,再通过 @EnableSwagger2 注解来启动Swagger2;

再通过 createRestApi 函数创建 Docket 的Bean之后,apiInfo() 用来创建该API的基本信息(这些基本信息会展现在文档页面中),select() 函数返回一个 ApiSelectorBuilder 实例用来控制哪些接口暴露给Swagger来展现,本例采用指定扫描的包路径来定义,Swagger会扫描该包下所有的Controller定义的API,并产生文档内容(除了被 @ApiIgnore 指定的请求)

第三步:添加文档内容

在完成了上述配置后,其实已经可以生产文档内容,但是这样的文档主要针对请求本身,而描述主要来源于函数等命名产生,对用户并不友好,我们通常需要自己增加一些说明来丰富文档内容。如下所示,我们通过@ApiOperation注解来给API增加说明、通过@ApiImplicitParams、@ApiImplicitParam注解来给参数增加说明。

@RestController
@RequestMapping(value="/users")     // 通过这里配置使下面的映射都在/users下,可去除
public class UserController {

    static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>());

    @ApiOperation(value="获取用户列表", notes="")
    @RequestMapping(value={""}, method=RequestMethod.GET)
    public List<User> getUserList() {
        List<User> r = new ArrayList<User>(users.values());
        return r;
    }

    @ApiOperation(value="创建用户", notes="根据User对象创建用户")
    @ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
    @RequestMapping(value="", method=RequestMethod.POST)
    public String postUser(@RequestBody User user) {
        users.put(user.getId(), user);
        return "success";
    }

    @ApiOperation(value="获取用户详细信息", notes="根据url的id来获取用户详细信息")
    @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long")
    @RequestMapping(value="/{id}", method=RequestMethod.GET)
    public User getUser(@PathVariable Long id) {
        return users.get(id);
    }

    @ApiOperation(value="更新用户详细信息", notes="根据url的id来指定更新对象,并根据传过来的user信息来更新用户详细信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long"),
            @ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
    })
    @RequestMapping(value="/{id}", method=RequestMethod.PUT)
    public String putUser(@PathVariable Long id, @RequestBody User user) {
        User u = users.get(id);
        u.setName(user.getName());
        u.setAge(user.getAge());
        users.put(id, u);
        return "success";
    }

    @ApiOperation(value="删除用户", notes="根据url的id来指定删除对象")
    @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long")
    @RequestMapping(value="/{id}", method=RequestMethod.DELETE)
    public String deleteUser(@PathVariable Long id) {
        users.remove(id);
        return "success";
    }

}

完成上述代码添加之后,启动Spring Boot程序,访问:http://localhost:8080/swagger-ui.html,就能看到前文展示的RESTful API的页面,我们可以点开具体的API请求,POST类型的/users请求为例,可找到上述代码中我们配置的Notes信息以及参数user的描述信息,如下图所示:

API文档访问与调试

在上图请求的页面中,我们可以看到一个Value的输入框,并且在右边的Model Schema中有示例的User对象模板,我们点击右边黄色的区域Value框中就会自动填好示例的模板数据,我们可以稍微修改修改,然后点击下方的 “Try it out!” 按钮,即可完成一次请求调用,这太酷了。

总结

对比之前用文档来记录RESTful API的方式,我们通过增加少量的配置内容,在原有代码的基础上侵入了忍受范围内的代码,就可以达到如此方便、直观的效果,可以说是使用Swagger2来对API文档进行管理,是个很不错的选择!

 

鼎捷T100—二次开发:客制化联络对象维护作业 aooi350 鼎捷T100二次开发,客制化 阅读详情

相关推荐

Qwen3-VL、Qwen3-Next与Qwen3.5多模态架构深度解剖

多模态大模型的核心不在参数规模,而在于视觉与文本如何协同建模。本文从基础概念切入,解析视觉语言模型中视觉编码器、跨模态注意力、特征对齐与token压缩等关键原理;阐述ViT、CLIP-ViT及动态压缩模块(HDTC)在语义表征、空间感知与计算效率上的技术权衡;强调结构设计如何决定模型在医学影像、工业质检、遥感分析等高要求场景中的真实能力边界;最终聚焦通义千问最新三套VL架构——Qwen3-VL、Qwen3-Next与Qwen3.5——揭示其背后迥异的融合哲学与工程取舍,为选型、微调与推理优化提供底层依据。

weixin_33778544的博客 818

springmvc始终跳转至首页,不报404错误(续)

上篇博客说到,当我执行程序时,springmvc的控制下,它始终跳转到首页,而不正常跳转。当时通过换一个服务器解决了问题,以为是缓存的事儿。但后来又发生了同样的事儿,顿时感觉出事儿了。就立马降低了日志输出等级,一看,果然有问题。虽然目前还没有解决,只是有一些思路,不过,也写下了记录一下! 一、程序的简单概述 页面打开,是加载到一个列表页,然后通过点击列表页的链接按钮,跳转到详情页。在详情页中,

该怎么解释?我懒得解释 1314

Windows下Python多版本共存与切换的终极指南(含环境变量详解)

本文详细解析了在Windows系统下实现Python多版本共存与切换的完整方案。通过深入讲解环境变量Path的底层机制,提供了从安装策略、精细化Path管理到创建快速切换脚本的实战指南,并介绍了py启动器和虚拟环境等高级技巧,帮助开发者彻底解决版本冲突问题,提升开发效率。

v6b7n8m9q0的博客 537

springboot 中错误处理的几种方法

错误的处理 程序启动时会默认调用 2018-09-27 15:19:43.439 |-INFO [restartedMain] org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping [543] -| Mapped "{[/error]}" onto public org.spring...

Sigod 6215

KV Cache 详解:新手也能理解的 LLM 推理加速技巧

(Autoregressive)生成的,即每次生成一个 token,然后将其作为输入继续生成下一个 token。:如果序列太长,超过了 KV Cache 的存储限制,就需要丢弃最早的 tokens(类似滑动窗口)。时,每次只会生成一个新 token,然后再把这个 token 作为输入继续生成下一个 token。,只缓存计算过的 K,VK, V,每次只计算新 token 的 K,VK, V,从而提升推理效率。:如果输入的 prompt 发生变化,之前的 KV Cache 不能复用,必须重新计算。

Harry的博客 7356

ESP32学习,驱动LED点阵屏,SD卡播放GIF动图

搬运就可以实现效果,如果有不足,请批评指正。下步该学一下如何文章上视频了,老是截图不方便展示效果。

u011625956的博客 1868

MockMvc单元测试

原始访问层代码 package com.example.demo.web; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author: * @Date: 2018/...

菜鸟黑一枚 1225

黑猫带你学eMMC协议第4篇:eMMC概述

1 前言 声明: 本文依据eMMC JEDEC5.1协议及个人工作经验整理而成,如有错误请留言。 文章为个人辛苦整理,付费内容,禁止私自转载。 本文大约3000字,从emmc的内部结构框架、供电、总线、寄存器等各方面进行框架性的介绍,本章节内容不需要深入研究,读完本篇文章,只要能对emmc有个框架下的了解,就可以了。 2 内部结构框架 由图可以看出,emmc内部由两块组成:控制器 + 存储介质。emmc内部存储介质绝大部分为nand flash。 众所周知,我们需要存储数据到nand flash中,并

黑猫奥利奥的博客 3341

SpringBoot的三种启动方式

springBoot 是什么 springboot 是一个框架,它依赖于spring,在过去我们使用springxiang

叶新东老师的博客 2万+

2026年紫光同创数字IC笔试试卷带答案

FPGA基础知识与设计要点摘要 本文围绕FPGA与数字IC设计核心概念,通过单选题、波形分析题、多选题和简答题形式,系统梳理了以下关键知识点: FPGA基础架构 查找表(LUT)是实现组合逻辑的基本单元(题1) CLB包含LUT、触发器和进位链,后者加速算术运算(题6,10) 布局布线优化是减少关键路径延迟的核心方法(题7) 时序分析要点 建立时间(时钟沿前数据需稳定)与保持时间(时钟沿后需保持)的定义(题2,简答1) 亚稳态成因及两级同步器处理跨时钟域信号的方法(多选题1,波形题5) 特殊设计技术 异步复

Open 114

Python实现将pdf,docx,xls,doc,wps,zip,xlsx,ofd链接下载并将文件保存到本地

本文是该专栏的第31篇,后面会持续分享python的各种干货知识,值得关注。在工作上,尤其是在处理爬虫项目中,会遇到这样的需求。访问某个网页或者在采集某个页面的时候,正文部分含有docx,或pdf,或xls,或doc,或wps等链接。需要你使用python自动将页面上含有的这些信息链接下载并保存到指定文件夹。遇到这种情况需要怎么做呢?别担心,跟着笔者直接往下看正文的详细解决方法。(附完整代码)在有些网页的正文中,有时会有如下图中的doc链接。比如爬虫在采集正文数据的时候,像标题,正文内容,作者,发布时间等等

Python进阶专栏《爬虫实战进阶》,《Pyppeteer从入门到精通》原创作者 4145

使用RESTful风格开发Java Web 及Swagger2简用

友情安利 目录 什么是RESTful风格? 使用Swagger2构造RESTful API文档 第一步:添加Swagger2依赖: 第二步:创建Swagger2配置类 第三步:添加文档内容 API文档访问与调试 什么是RESTful风格? REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移),REST 是一种体系结构,而 ...

水逆呱的博客 533

【实战】小红书笔记爬虫终极指南:模拟滑动加载,抓取图文(2026最新版)

小红书作为国内最大的生活方式分享社区,每天产生海量的图文笔记。对于数据分析、市场调研、竞品分析等需求,爬取小红书公开笔记成为一项常见任务。然而,小红书的反爬机制日益升级——接口加密、动态Token、滑动验证、设备指纹……本文将深入剖析小红书Web端和App端的爬取技术,手把手教你用Python实现模拟滑动加载、自动抓取图文笔记的完整爬虫系统。全文包含大量代码、原理分析和排坑经验,适合有一定爬虫基础的读者进阶学习。目录前言一、小红书反爬机制概览二、环境准备2.1 Python环境2.2 浏览器驱动。

2201_76125261的博客 766

java cxf restful_基于cxf开发restful风格的Web Service

一、写在前面webservice一些简单的其他用法和概念,就不在这里赘述了,相信大家都可以在网上查到,我也是一个新手,写这篇文章的目的一方面是想记录自己成长的历程,另一方面是因为学习这个的时候花了点时间,希望本文章能为大家节约点时间。当然描述的可能不到位,望谅解。二、创建项目2.1、创建公用接口project为了方便服务端和客户端调用接口可以先创建一个接口java project,单独建一个接口p...

weixin_33603405的博客 410

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

使用SpringMVC开发RestFul风格接口的java-web项目,tomcat部署并客户端访问

1.使用intellij idea开发,gradle构建工程。webcollection模块就是接口所在项目模块,handleMessage类就是接口类。2.接口项目模块的build.gradle配置文件内容如下:apply plugin: 'java' apply plugin: 'idea' apply plugin: 'war' group = 'com.cloudcollection' ...

用心丈量世界 4847

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Java Web 开发中,Restful 风格是一种设计 API 的风格,不是硬性标准,通过请求方式 GET、POST、PUT、DELETE,加上资源的地址 URL,服务器就能明白你想要做什么

Java Web 开发中,Restful 风格是一种设计 API 的风格,不是硬性标准,通过请求方式 GET、POST、PUT、DELETE,加上资源的地址 URL,服务器就能明白你想要做什么

道友 565

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

java rest tomcat_使用SpringMVC开发RestFul风格接口的java-web项目,tomcat部署并客户端访问...

1.使用intellij idea开发,gradle构建工程。webcollection模块就是接口所在项目模块,handleMessage类就是接口类。 2.接口项目模块的build.gradle配置文件内容如下:apply plugin: 'java'apply plugin: 'idea'apply plugin: 'war'group = 'com.cloudcollection'arch...

weixin_28862967的博客 271
上一篇: 手把手教你用Spring Cloud和Docker构建微服务
下一篇: Swagger使用指南
rich frain
博客等级 码龄9年 8粉丝 2原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值