粉丝4.9万获赞57.2万

防不住重复提交,一秒搞出重复订单面试备问接口,防重提交,别慌,今天带你从根源到方案六大维度彻底讲透 问题根源!一次操作多次请求。场景一,用户手抖,疯狂点击,多个相同请求涌向服务器。场景二,网络延迟,页面卡顿,导致前端重复发送 后果重复订单,重复扣款,数据混乱,严重可治生产事故。解决方案一,前端拦截第一道防线。方法一,按钮禁用,点击后立即至徽家楼顶物理防连点。 方法二,请求防抖,设置定时器,最后一次操作后延迟发送请求。优点,成本低,拦截百分之九十误,操作体验好。缺点,只能防君子,无法防脚本攻击,后端才是关键。 解决方案二, token 令牌机制,后端通用防虫流程三步走向,看电影、买票领票,进入页面时先向服务器申请唯一 token, 存 radis, 并设过期时间,持票提交表单时 header 或参数带上 token 检票服务器用原子操作,如检查并删除验证 token 有 且删成功放行,无或已删,咀嚼并提示请勿重复提交 效果,一次性 token 完美防重复创建。解决方案三,分布式锁高并发场景必备试用场景秒杀、抢购等高并发请求同时到达。步骤定锁,如用户 id 加商品 id 作为业务唯一键 抢锁,用 radis 的 set next 命令抢锁成功者执行业务释放,业务完成后释放锁,其余请求提示手慢了或系统繁忙。核心,保证同一业务同一时刻仅一个县城执行。 解决方案四,唯一锁影,数据一致性兜底,最后防线绝对可靠。方法,在业务表设置唯一字段,如订单号并加数据库唯一约束效果,首次插入成功,重复插入应约束冲突被数据库拒绝处理, 代码捕获异常转为友好提示您已下单成功,请勿重复操作。解决方案,五密等设计回调接口,必备试用 支付回调等可能被多次调用的接口核心,同一操作执行多次,结果一致。以支付回调为例,收到回调先根据订单 id 查询当前状态,若状态为待支付,执行加余额更新状态等操作, 若状态为已支付,直接返回成功,不作处理结果,无论调多少次,用户余额只加一次。总结于选型前端拦截优化体验辅助防重 tock 令牌通用后端方案,适合大多数表单提交分布式锁高病发场景刚需如秒杀抢购哦唯一所以简单可靠的兜底守住数据最后一道门 密等设计外部回调标配保障结果一致哦。实战中推荐组合使用前端拦截加 token 机制加唯一锁影,构建从前到后的纵深防御体系。如果对你有帮助,记得点赞收藏,关注我,学习更多技术干货!

hello, 今天给大家分享我们 spring boot 如何实现接口的密等性,那实现接口密等性呢?有很多种方案,那我们这边呢,以前端重复提交表单为例啊, 我这边简单画了一张图啊,比如说当我多次点击提交按钮的时候,那我们后端如果没有进行密等性的一个教验,可能会有多个单据的生成,那针对这种场景,我们一般会用 token 的令牌机制呢,去实现一个密等性教验。 我在返回这个页面的时候呢,就给前端这边放了一个 token, 并且呢它是隐藏的,这个 token 呢是在我们后端生成,并且呢我们后端要以它为 k, 然后以我们的用户信息为 value, 存到我们 release 中。 那当我们点击提交按钮的时候,我们会拿着这样的一个拓客令牌呢,去后端做交验, 以 token 为 k, 去我们 redis 中获得 value 值,并且跟我们当前的用户信息呢进行比较,如果交易成功呢,则删除令牌, 那因为我第一次请求啊,我已经把我的令牌呢给删除了,那后续的请求进来,我 release 中就没有这个 k 了,那我教练呢就会失败。我们来看一下代码啊,这边有一个生辰 token 的一个方法,我们生辰 token, 并且呢把它为 k, 然后把我们的 value 为值,然后存到我们 release 中, 然后呢这边还有一个教研拓客的方法,用到了一个撸啊找本,那 k 一呢就是我们的一个 k, k 二呢就是我们传过来的 y 六值,如果教研成功,我们就会删除这样的一个 k, 我们执行之后,我们就判断一下这个 resort 是不是零,如果是零就是胶原失败的,如果不为零,那就是胶原成功,并且可以执行我们接下来的一个业务 逻辑。然后我这边呢写了一个测试方法啊,我们可以来运行一下,我们可以看到啊,只有第一次是正常调用的,后面的都是显示重复调用。那如果说大家想要这样的一个 demo 以及之前的 demo 的话呢?可以到这来 这,然后给他去发一个消息,像这样就可以拿到我们的 demo。 好,今天的一个分享呢,就到这。

给大家分享一下 think 注解使用时候的一个小问题啊,是有关于这个失效的一个问题啊,这里我们来看一段演示,代码定义两个方法,一个是 m 一,一个是 task, task 是标注了一个 think 注解的一个义补方法, 然后在 m 一里面去调用一个 task, 两个方法里面都有两行输出,然后我们来看一下 m 一方法执行的时候它的一个向后顺序, 这里我们通过这个浏览器来调用一下,看一下后台的输出啊,这个输出的话,我们可以发现啊,他其实是踏实的先执行的,而 m 一后执行的,呃,和我们的代码顺序是一样的,也就是说明呢,其实这个代码并没有说是义不执行的, 对吧?嗯,其实这个就是要分享的一个问题啊,其实换句话说就是这个 think 他失效了,那他为什么会失效呢?其实,呃,原因呢?原因有点像那个事物失效的原因啊,这次调用的那种方式,因为 性格要想生效的话,他是要走代理的,而我们这种直接调用的方式,他是不会走代理的,那我们来稍微改造一下我们的代码, ok, 这边可以看到我们还有一个类啊, sink component 的一个类,然后这个类里面呢,其实也是写了一个跟刚才他合一是一样的方式,然后呢,我们在这边去进行了一个啊 component 的一个调用,然后我们重启一下,看一下结果, ok, 启动完成,然后我们再借助于浏览器访问一下。 好,这里我们可以看到变成了 m 一先执行,然后是 task 一再执行,对吧?而且两个县城池的一个名称也发生了变化。呃,所以说 我们在进行了 sink 注解使用的时候,一定要注意一下这个问题。呃,另外 sink 注解其实它使用的时候还会有一些其他的问题。嗯,大家有遇到的话也可以在评论区交流一下,我们下期再见。

spring framework 长期以来一直是 java 企业级应用开发的一个基石,而 spring boot 则是进一步简化开发的利器。很多开发者从接触 java 开始就直接使用 spring boot, 有 时甚至会混淆它与 spring framework 的 一个关系。今天我们就来详细解析 spring boot 的 本质,以及它是如何通过标准化的一个方式解决了传统 java web 开发中的一个复杂性问题。 如果你还在盲目刷题,不知道面试到底考什么,我已经把简历优化模板、八股文标题库、面试全流程应对策略、突击学习路线图全都打包整理好了,覆盖从头递到谈心的每个环节,需要的评论区扣六六六直接领走。 首先明确一个概念, spring boot 的 不是一个新的框架,它并没有去创造新的技术,它是一个脚手架,或者说是整合包。它的核心目标只有一个,简化 spring boot 的 一个应用,你需要去手动整合各种组建。现在 spring boot 提供了一套标准化的一个解决方案,让你可以 快速启动项目。它底层依然依赖于 spring framework 的 一个核心功能,但极大地简化了搭建和配置的一个过程。 spring boot 的 一个诞生只在解决传统 spring 开发过程中长期存在的三个 主要难题,痛点,一,复杂的配置。以前你写一个简单的 web hello world, 你 需要配置 web chamele, 配置 application context icon, 配置 spring m vc, 则否写了一堆 chamele 文件代码还没开始写。 spring boot 的 解决之道呢?它是约并 由于配置,它默认帮你配置好了百分之八十的常用设置。你想开发 web, 引入 starter, 它自动帮你配置好 tomcat 和 spring mvc 查没有直接写代码。痛点二,混乱的依赖以前你要用 spring mvc, 得引入 spring web。 spring web mvc 要用 jason, 得引入 jason。 最要命的是,你得小心翼翼地选择版本,防止冲突。 spring boot 的 解决之道呢,是使用 starter 机制,它把一组兼容的一个依赖打包在一起。 spring boot starter web 呢, 一个坐标,包含了所有外部开发需要的包,而且版本由 spring boot 外皮管理,再也不用担心冲突了。痛点三,繁琐的部署一千写完代码,打包成 word 包,安装 tomcat, 把 word 扔进去,启动 tomcat。 如果环境不一样,还得改配置。 spring boot 的 解决之道呢,是内嵌容器,它直接把 tomcat jelly 塞到了这儿包里面。你的应用就是一个普通 java 程序, java 干炸就能直接运行了,走到哪运行到哪,恢复容器化部署简直不要太方便。总结下, supreme boot 的 一个核心价值就是四个字,开箱即用。通过自动配置解决了配置繁琐的问题,通过 starter 解决了依赖管理的问题,通过内嵌容器解决了部署复杂的一个问题。它是 java 生态能够适应云原生微服务时代的一个关键,也是每个 java 程序员必须掌握的一个生产力工具。

那么上节课呢,我们已经通过磁屏簿的框架快速的搭建了一个后端的工程,并且呢在里面增加了一个接口, 我们的前端呢,通过网址就可以访问到我们对应的接口,并且获取到后端所返回的数据。那么这种方式呢,我们叫做 risk api, 那么 spring boot 呢,对 risk api 的 支持主要是通过注解的方式来完成的,有时候我们会把网址和请求方式啊,通过注解的形式呢给它绑定起来, 那么我们的前端呢,通过发对应的网址和请求就可以找到我们在服务器端啊所编写的接口,进而呢可以通过接口对服务器端的数据进行一些操作。 那么在这里呢,我们对 rest api 啊它的组成要一个了解,这样话呢,我们在编写后段代码的时候,才可以正确地用到我们的注解方式。 那么 rest api 呢,它的组成实际上就是由网址和请求方式两部分来组成,一般呢,我们的一次请求,首先呢会确定是以什么方式进行请求的,我们所指的方式呢,比较常见的就是有 get post 啊,除此之外呢,还有 put, delete 等等等等。那网址呢啊,这个大家比较好理解,一般指的就是我们在呃 协议啊,域名后面那部分啊,那部分网址是在瑞士 api 中比较关键的,那我们通过网址呢,就可以把我们后端的接口方法和要请求的呃,要要前端所发送的请求呢,给大家绑定到一块了, 所以网址是用来识别我到底该去执行后端的哪一个接口方法。那么我们一般在使用的时候呢, 我们常见的数据操作主要就是增删改查啊,分别是查询信息、增加信息、修改信息和删除信息。但这个地方呢,会有一个不成名的规范吧,就是我们会用不同的呃请求方式来对应 啊,我们服务器上的数据的增删改查,但这个呢也并不是一个绝对的,就说我们按照这个规范来写的话呢, 呃在开发上会更加的好维护一些吧。一般情况下呢,我们在进行查询的时候呢,主要用的是 get 方式来进行查询的啊,这里包括我们查询所有数据或者是根据条件进行查询,就通常都是用 get。 那么 get 的 话呢啊,它在传递参数的时候呢,主要是通过网址来进行传递的,而且这个传递的参数呢,是在我们浏览器上是可见的,有时候它会显示到我们的 呃浏览器的地址栏上啊,那么意味着我们查询所用到的条件实际上是可见的。 那么 pos 的 话呢,一般是咱们在做新增的时候会比较多啊,比如说咱们注册一个用户信息啊,或者是新增一个商品信息,那这时候我们都会写表单啊,那么通过表单把大量的数据写到我们的 呃这个请求的 body 里,然后发送给后端来进行处理。为什么这时候用 pos 呢?一般咱们新增的数据的数量是比较大的啊, pos 的 话呢,它的数据存储在我们的呃 htp 请求的包堆部分的,包堆部分呢,可存的信息要比咱们网址上要传递信息会很多啊,所以一般咱们新增呢用的是 post, 那 么修改的话呢,一般用的是 put 啊,咱们修改用户的话呢,其实有一个关键就是我要把用户的主键传过去,因为咱要确定你要修改哪个用户,那么根据主键来查找 我们已有的用户,然后再把信息进行修改,所以一般咱们就是 put, put 之后呢,我们在网址的后面会把一般用户的主键传过去,用来查询用户所使用的。那么删除的话呢,跟修改很像 啊,也是要根据主键查到对应的信息,然后再进行下一步处理。那么一般咱们用的方式呢是 delete 啊,比如说 delete 这个方式的请求一般都是来做删除用户的,但是呢这个地方大家注意他只是一个规范啊,就是我们大部分操作呢,都是按照这个模式进行编写的,但是有的时候呢,可能遇到一些特殊的情况,我们可能会采用 根据当时啊比较适合的方式来进行选择啊,不一定是严格按照我们所说这个模式啊进行选择。那么在这里呢,我们说一下他有几个比较重要的概念啊,第一个呢就是资源的问题啊,资源的问题就是我们在整个呃前端请求的时候呢啊,他一般是用网址 来去访问我们的某一条数据或某一类数据,那要访问这个数据呢,可以认为是资源,比如说他具体在访问什么,我们只能通过网址来进行区分啊,这也就是为什么咱们在写后端接口的时候,我们一定会把每一个方法去绑定到一个网址上,这网址上是特别重要的, 那么接下来的话呢,就是状态的问题啊,状态问题呢,指的就是我们前端在请求的时候啊,每次请求 他会得到一个响应,那么当他得到响应之后呢,这次请求就结束了,那么下次请求呢,跟上一次请求之间是没有关联的,这个大家要注意一下,比如说每次请求和响应是一个独立的 啊,而并不是说我们上一次请求对下一次请求之间是有关联的,他不是这样的,所以咱们服务器呢,每次只处理当前这次请求啊,所以请求请求之间是并没有连续的这个概念。 那么接下来的话呢,就是我们每次啊接收到前端请求之后,我们后端做的主要的事情就是要给前端一个响应的结果,对吧?比如说你查数据,那我可能要把当前你要查的数据给你返回来,那么你新增数据,我可能要把新增之后成不成功的结果给你返回回去。 就说无论我们的前端在做什么样的操作,一般情况下我们的后端都要给前端一个对应的结果,那么这个结果返回的时候呢,就存在我们返回结果的一个格式的问题啊,这是很多前端是特别关心的。那么对后端来说,我们返回的格式, 通常情况下现在用的比较多的就是 jason 格式,因为 jason 这种格式的话呢,无论对于我们做前端是小程序还是 app 还是网站,它基本上是一种通用的格式 啊,所以说我们现在用的最多的是单调形式啊,单调形式的话呢,其实就是 k 和 y 六这种模式啊,一般咱们的数据呢,是由 k 和它所对应的值来组成,会有很多 k 啊,每个 k 呢会对应自己的一个值,那么这个数据呢,我们发给前端之后,前端就可以去解析它啊,然后放在我们的前端 ui 上来进行一个展示 啊,所以这个筛选形式啊,是咱们返回比较多的。当然呢,除了筛选形式以外,其实还有别的形式啊,比如说 text 形式啊, image 形式啊等等等等啊,但筛选形式呢,咱们用的是最多的, 那么关于请求方式呢,刚才咱们讲的最常见的就是四种, get、 post、 put 和 delete, 那 么每个我们的用途的话呢,都有一个不成文的规定啊,这个咱们前面已经介绍过了,那么在写 get 的 时候呢,呃,我们一般情况下呢, 存在两种情况,一种情况下呢,就是我查所有的数据,那这时候呢,就不需要从个前端去传送条件过来,对吧?啊,那么还有种情况下呢,就是我们要从前端传送一个条件过来啊,比如说根据 id 进行用户查找,那么这时候呢,我们把 id 啊写到我们的网址上 啊,那么接下来呢,我们就可以通过注解来获取到这个 id, 然后进行以下一步的查询操作就可以了啊,那么这时候呢,我们用的注解啊,就是 at get 模式啊, 当然呢,具体这个参数是如何接收到的,怎么去处理?咱们在下一课讲参数的时候呢,会重点来讲到这地方,大家只要知道就是我们 get 呢,在传递参数的时候啊,大部分我们都是通过 url 来进行传递的。那么第二种呢,就是 pos 了, pos 呢,咱们用的这个注解啊,就是艾特 pos 的 啊,那么 pos 呢,传递的数据是通过 body 来传送的,所以一般呢,咱们会在 body 里去获取它的这个数据。 那 put 的 话呢啊,一般用的是 at put 卖屏, at put 卖屏啊啊,那么它有一部分数据呢,会通过网址来传递,当然也有一部分数据呢,是通过 body 来进行传递啊,我们网址来传递呢,主要是用来查询数据的一个条件。 body 来传递呢,当然就是新数据了,因为咱们 put 主要是用来做数据更新的, 那么删除的话呢,呃,一般情况下,只要传一个数据的筛选条件,一般是主键,我们通过也是通过网址来进行传递的啊,那么注解呢,用的是 at delete mapping。 好, 那么大家呢,就可以把我们这四个注解要记一下,有时候我们完成不同操作的时候呢,所用的注解是有明显区别的。 那么最后就是一个状态的问题啊,咱们每次 h t e p。 请求啊,操作完之后,实际上它是有一种状态的,这个状态码呢,其实是 h t e p。 协议所规定的啊,比如说我们成功了,一般就是两百 啊,那么两百的话呢,我们在前端一般他会判断,如果两百,说明我们整个这个 a t p 啊,他的请求和响应这个整个处理环节是成功的,但两百并不代表说我的业务执行成功了。大家注意, h t p 的 状态码呢,只能代表 h t p 的 整个请求和响应的操作是完成的,因为假设我们的服务器是关闭状态, 那么他的状态就不可能得到是两百,但是具体你这个程序业务上成不成功,一般咱们是有单独的状态来进行判断的啊,并不是说通过这个两百来进行处理的,这个大家要注意一下, 那么结合这个内容的话呢,我们就知道了,我们刚才写的这个 get 的 这种接口,一般常用于就是来做什么呀?做查询所使用的啊。那么如果你想做一个新增,一般咱们用的就是这个 post 修改就是 put, 删除呢,就是 delete。 那 么我们在编辑的时候呢?为什么社长可推荐大家用这个 post 慢呢 啊?它有个最大好处就是我们陀螺曼就是可以模拟这四种啊方式的请求来进行一个测试啊,这块呢,我们来测试一下啊, 好,我们给他新增三个方法 啊,我们只是为他做一个测试啊,所以说不写具体业务的,那这个呢,我们用的是这个 post, 然后这个咱们用的是 put, 最后呢,咱们是用的是 delete, ok, 那 么这几个写完之后呢,大家会发现一个现象,就实际上我这四个请求的网址是一样的,是吧?都是 index 的 啊,但这四个是可以区分出来的,为什么?因为它的方式不一样,所以说我们一个接口在评判 啊,他到底该响应什么样,响应的时候什么样。我们这个前端的请求的时候,他不仅仅是通过网址来进行判断的,实际上他要配合到我们请求的方式,这两个加一起来进行一个判断啊。那现在呢,咱们就给他重启一下, 接下来呢,我们打开我们的 pos 的 慢啊,当用 pos 的 慢来进行测试的时候呢,我们说最大的好处就是它是可以切换的啊,比如说咱们用 get 发送一下啊,这是 get 返回来的结果,然后咱们切换成 post 啊,这是 post 返回的结果,然后 put 最后是 delete 啊,那么大家看一下,我有了这个工具之后啊,在操作不同的方式请求的时候就很方便了,那么你像 put 和 delete, 我们在用浏览器来进行模拟的时候呢,就会比较麻烦一些啊,这也是为什么呃,希望大家呢,一定要会用 pos 慢一个最主要的原因就是在测试的时候呢,会很方便啊,那么这一课的话呢,其实对于很多做过开发人呢,应该是比较了解的,那么主要针对于没有做过后端的同学呢,来做一个概念的 表述啊,就说大家要对我们的 recise recite a p i 啊,有一定的了解,就是我们通过请求方式加网址,两者结合在一起啊,来去完成我们接口的一个绑定啊,然后通过不同方式呢,最终决定了我们这接口应该完成哪些的业务 啊,这是我们关于 recise 的 a p i 啊,它的一个介绍在这呢,当然大家对我们所用到的这四个注解肯定要先要去了解了。

那我们在写后端接口的时候呢,嗯,最主要处理的两个问题,一个就是如何把我们的接口方法和网址做好,一个映涉关系,就说有哪些网址会掉,我们哪些接口的方法。 那第二个要处理的话呢,就是啊,请求参数的问题,我们的前端呢,会通过发送请求带来一些参数,那么这些参数呢,它所发送的位置可能不太一样,有些是在网址部分来发送的,比如说在 url 里进行发送的,那么有一些呢,可能是在我们的请求的啊 body 部分来进行发送的, 那么呃,我们后端如何去啊?解析到这些对象啊,或者说类似的把它给获取过来。 那么这是我们要解决的两大问题,一个是接口应设的问题,一个是请求参数的问题,那么这两项呢,在 spring boot 里,它都是通过注解的形式,帮助我们来做了一些简化的操作。那么今天呢,我们就把接口应设和请求参数啊这两部分给大家做一个介绍,但重点呢,介绍一下请求参数这个问题,因为接口应设这部分呢,我们在前面已经给大家介绍过了, 那么在前面呢,咱们已经学了四个注解了,一个就是 get 麦屏、 post 麦屏、 post 麦屏和 delete 麦屏。那么第四个注解呢,它不仅是来去确定我们请求的方式是 get post put 还是 delete, 那 么同时呢,它也是有一种呃我们的网址映色规则的,比如说在我们的 get 麦屏或是 post 麦屏里面呢,我们会去写一个网址 啊,那么这个网址呢,就是咱们去跟前端请求来匹配的这样的网址,那么当网址匹配之后呢,他就会调用我们具体的方法了,这个咱们前面已经给他介绍过了,我们来看一下,比如说在咱们之前代码中呢,我们在所有的麦屏后面都是写了一个网址的,那么他就通过请求这个网址来匹配,我们该走哪些方法 啊?有时候我们这个注解呢,其实它有两大功能,一个是确定请求的方法,一个是来映射网址的,那么在我们的 spring boot 中呢,还有一个单独的注解这个注解呢啊,它的功能呃是比较通用的啊,我们叫 啊 request 的 麦屏啊,这个 request 的 麦屏呢,实际上是这些麦屏的一个基础注解,那么这个注解主要作用呢?就是来去呃把我们的网址来进行映射的一个主要的注解方式。好,我们来看一下啊, 我们在 get 麦屏里啊,大家打开之后啊,你会发现它本身实际上是包含有我们的 request 麦屏的啊,有时候 get 麦屏,它本身也是一个注解的集合啊,那在里面呢,主要起的一个最主要作用就是它有一个叫 request 的 麦屏啊, request 麦屏中呢,有一个参数叫做 master, 这个 master 呢,实际上就指定我们用什么样的方法 啊,但它 get 麦屏呢,默认传递的是呃我们的 get 这样的一个模式,那么我们再看一下啊,在 pos 的 麦屏里, 那么在 post 麦屏里呢,它同样也是用的我们这个 request 麦屏来去做网址映设的,只是说它的 method 呢,指定的是这个 pos 的 啊,比如说我们在这个地方呢,如果不用 get 麦屏的话,你也可以去用我们的啊,直接用它通用的 request 的 麦屏 好,然后呢去映设一个网址,只说呢,这时候我们在这里面要给它单独指定它的 method, 比如说叫 get 啊,然后接下来呢,我们指定它的这个 y 啊,这 y 对 应的就是网址啊,我们叫 u 字啊,就这样写法呢,跟你直接写一个啊 get 麦屏啊,里面放网址啊,这个效果是一模一样的,没什么区别啊啊,所以说,呃,我们现在用的这些 get 麦屏,普通麦屏啊,其实它本质还是用的是 request 的 麦屏,所以 request 麦屏呢,是一个比较基础的网址映设的方法。 那么一般情况下呢,咱们并不会在方法这直接用这 request 埋平啊,因为这样写法首先比较麻烦对吧,而且在维护的钱呢,不够简洁啊,比如说我们这个用 get 埋平,一看大家主要是用 get 模式来进行传递的,对吧?啊,这个话还要再写一个参数啊,那么 request 埋平主要用在哪比较多呢?一般是用在我们的类上面 好啊,有时候我们不光可以在方法上面去做网址的一个映设,我们还可以在内上面去做一个网址映设啊,比如说我们在这来加一个 request 命令,然后我们在这呢给它加一个,比如叫 user 啊, 那么呃,这时候呢,当咱们在内上面加了一个网址之后,那么我们具体这个方法的网址啊,就会是两个网址的叠加了啊,有时候咱们原本呢,呃,在没有写这个的时候呢,咱们访问的网址叫 好,我们在这写下啊, h t t p 然后我们的域名是吧, local host 的 端口,然后对应的,然后后半部分就是你要映设这个网址,比如说以前的话呢,直接由域名后面来写第一集就可以了,就可以映设到了,当然当你加了我们 这个 request 命令在我们的类前面的话呢啊,这时候呢,后面这一集就不再指的是方法中的啊应设的网址了啊,它就会指向的是我们类上面应设的网址了,那么方法应设网址呢?会后面再加一集,也相当我们的啊,这个域名啊, 需要写两级啊,这个我们不叫 uz 啊,已经重复了,我们叫 uz ctrl 啊,有时候我们叫 uz ctrl 啊,那这个 uz ctrl 呢,指的是这个位置啊,那么有时候我们下面所有这个方法里的啊,这个类里面的所有的接口方法啊,那么前面都需要加一个通用的啊,这样的一个呃 地址,那这样做有什么好处呢?其实这样做一个最大的好处呢,就是可以统一规范咱们多个 control 的 类 啊,可以很好的给它去问出来啊。有的时候呢,我们可能在下一层次啊啊,定义的我们这个网址啊,都是相同的,对吧?比如说我们写了,呃, 两个现在 control 类,对吧?那比如说我们在 first 的 control 类里啊,用的是 index 的, 那么在 user 里呢,可能也有一个 index 的, 那么这时候呢啊,如果不在咱们类前面加个映涉关系的话呢,这两个 index 就 重复了,我们来解一下什么意思啊,比如说咱们在这里呢,也加一个这个 index, 加个 index 啊,然后它也是返回一个叫 user index, 就 用户的首页,对吧?那么我们把它的呢网址咱们也起成是 index, 那 这时候大家会发现它出现一个什么问题呢啊,那么也说我们当前这个接口对应的这个域名啊,就转换成了 htp 啊, local host 八零八零斜杠 index, 对 吧?啊,那同样这个网址呢,它对应在 first 里也是有这样的一个 接口的,对吧?嗯,这个接口也是 index 的, 那么大家想一下,这两个时间就冲突了,对吧?我们不可能说一个网址会对应两个接口啊,这个我们在执行 speed 的 时候也会报错误,因为它会发现你出现了两个网址对应接口是同一个的,你在运行的时候它也会报错误啊,看它报错了, 对吧?就是我们映射的这个方法相同的啊,那么有的时候呢,咱们在一个比较大的项目里呢,有可能是多人开发啊,比如说我们第一个控制器是 a 来进行负责的啊,第二个控制器呢,是 b 来进行负责的,那么你没法保证 a 和 b 他 所用到的映射这个地址是相同的,对吧?有可能他俩就喜欢相同的,这个你是控制不了的,因为,呃,我们当代码量足够大的时候呢,很多时候这个网址啊会重覆, 那怎么办呢?我们就在上面加类,上面加它的 request 卖屏,因为类的话呢,它数量毕竟是有限的啊,那么类名不可能冲突,是吧?那么类所对应的这个 第一级的网址也不会冲突,那么当你加了每个类上面加一个网址之后啊,那么即使方法相同的 方法里对应的映射的网址相同的,都叫 index, 是 吧?但是要拼到前面之后,它就不同了啊,比如说我们在 user control 里呢,当咱们在这儿加入 hyperlink 之后啊,它的网址就变成了叫 user controller 斜杠 index, 那 么你写在我们的 first control 里里的这个网址呢? 它已经叫 first controller index, 那 这两个肯定是不相同的,对吧?不相同的,那么这可这样就可以保证什么呢?即使它在两个 类里面所定义的接口方法所映设的网址是相同的,但由于它整个类的对应的网址是不同的,也会导致它并不会冲突 啊,这是一个很好的保证代码啊,不冲突的一个方式啊。所以一般情况下呢,我们 request my 屏一般都是放在类上面的,我会把每一个类会定义到一个 request my 屏下。当然有同学会说,那我为什么不只写一个 control 类,是吧? 啊,这个还是基于我们项目考虑的,但项目足够庞大的时候呢,为了更好维护,我们还会把同样的业务模块的 control 放在同一个类里,不会把所有代码都放在一个类里了,对吧?啊,即使你都放在一个类里,由于方法接口比较多,也可能会出现重复的情况,所以我们这样的一个方式啊,啊,实际上是可以避免啊,咱们映射接口相同,导致这样的一个问题出现啊, 这些都是基于我们多人开发啊,会导致的一个情况,更好利于代码的一个维护啊。所以说我们的 requestmail 呢,独立用的时候,更多是用在我们类前面来定义映设呃来去使用的。那么在具体方法上呢,由于要指定是什么样的方法 啊,所以咱们不会用 request 麦屏啊,这个大家要注意一下,那这是 request 麦屏的一个核心功能,有,它是咱们最基础的接口营设的一个注解。好,那么呃,其他的话这四个注解我们就不介绍了啊,这是让对应的对应的一个请留方法在这里面,那下面咱们来讲一下关于参数的问题。

spring framework 长期以来一直是 java 企业及应用开发的一个基石,而 spring boot 则是进一步简化开发的利器。很多开发者从接触 java 开始就直接使用 spring boot, 有 时甚至会混淆它与 spring framework 的 一个关系。今天我们就来详细解析 book 的 本质,以及它是如何通过标准化的一个方式解决了传统 java web 开发中的一个复杂性问题。如果你还在盲目刷题,不知道面试到底考什么,我已经把简历优化模板、八股文稿、应聘库、面试全流程、应对策略、突击信息路线图全都打包整理好了,覆盖从头递到谈心的每个环节,需要的评论区扣六六六直接领走。 首先明确一个概念, springboard 不是 一个新的框架,它并没有去创造新的技术,它是一个脚手架,或者说是整合包。它的核心目标只有一个,简化 spring 应用的一个开发。想象一下,以前构建一个应用,你需要去手动整合各种组建。线下 springboard 的 提 提供了一套标准化的一个解决方案,让你可以跨快速启动项目。它底层依然依赖于 spring framework 的 一个核心功能,但极大地简化了搭建和配置的一个过程。 spring 部的一个诞生只在解决传统 spring 开发过程中长期存在的三个主要难题。痛点,一,复杂的配置。以前你写一个简单的外盘 logo, 你 需要配置 web 叉 mill, 配置 application content 叉 mill, 配置 three m v c 叉 mill。 写了一堆叉 mill 文件代码,还没开始写 supreme, 不 懂的解决之道呢,它是约并异于配置,它默认帮你配置好了百分之八十的常用设置。你想开发 web, 引入 startup, 它自动帮你配置好 tomcat 和 supreme m o c。 零叉 mill, 直接写代码,痛点啊,混乱的依赖。以前你要用 supreme m o c, 得引入 spring web, spring web mvc 要用 jason, 得引入 jason。 最要命的是你得小心翼翼的选择版本,防止冲突。 spring web 解决之道呢,是使用 java 机制,它把一组兼容的一个依赖打包在一起。 spring boot java web 呢,一个坐标,包含了所有 web 开发需要的包,而且版本由 spring boot 统一管理,再也不用担心冲突了。 tony 三,繁琐的部署以前你写完代码,打包成袜包,安装 tomcat, 把袜扔进去,启动 tomcat。 如果环境不一样,还得改配置。 spring boot 的 解决之道呢,还是 内嵌容器,它直接把 tomcat jitter 塞到了炸包里面。你的应用就是一个普通的 java 螃蟹, java 干炸就能直接运行了,走到哪运行到哪,微腐容器化部署简直不要太方便。 总结下四个 word 的 一个核心价值就是四个字,开箱即用它通过自动配置解决了配置繁琐的问题,通过 start 解决了依赖管理的问题,通过内嵌容器解决了部署复杂的一个问题。它是 java 生态能够适应云原生非服务时代的一个关键,也是每个 java 程序员必须掌握的一个生产力工具。

spring boot 之所以能一键启动,不像以前那样写一大堆叉 ml 配置核心就是空地渗透系列注解,它是 spring 四点零引入的,作用很简单,只有满足特定条件才创建这个病。今天我们就来看看这个简单的一个机制,它是如何撑起庞大的一个自动装配生态的。如果你还在盲目刷题,不知道面试到底考什么,我已经把简历优化模板,把古文高清题库、 面试全流程印的策略铺垫、学习路线图全都打包整理好了,覆盖从投递到谈心的每个环节,需要的评论区扣六六六直接领走。 首先, coindenum 的 一个原理其实非常直观,它的原码里只定义了一个属性,这个 coindenum 是 一个接口,里面只有更 max 的 一个方法。 spring 在 解析配置类时会调用这个 max 的 一个方法,如果返回处,则要注册这个病, 如果返回 false, 则直接忽略,就这么简单。 spring 容器在加载病待分离生的一个阶段,就是通过这一道光卡来筛选这个病的。在 spring bug 中,我们很少直接用这个 coindenum 的 一个注解 是用它的一个太深注简最常用的有三个,第一个 conditional on class, 当 class pass 下存在某个类时才生效。比如你有 jessica, spring 就 会给你配这个 register。 第二, conditional on property, 当配置文件中有某个属性且值为 true 的 时候才生效。比如 serial import 三个 conditional on missing, 当容器里面没有某个病时,它才会生效。这是 spring boot 能够允许用户覆盖默认配置的 关键。在实战中,我用它解决过最棘手的问题就是多环境的一个配置,假设我们有一个消息发送服务 message service, 在 开发环境我们要把消息打印到控制台,方便调试。在生产环境,我们要把消息真正发送到 mq, 我 们可以定义两个实现类,分别加上注解,这样只需要在 application 点 properties 里面改一个配置, spring 就 会自动帮我们切换时间类,完全不需要去改代码。最后总结下, conditional 是 spring 动态装配的一个灵魂,是把硬编码变成了可配置。建议大家没事多翻翻 spring boot 的 auto configuration 的 源码,看看官方是怎么花式使用 on class on being on expression 的, 这不仅能加深理解,还能学到很多优雅的配置技巧。

基于 view 和 spring boot 的 农产品销售系统,首先使用管理员的账号进入系统, 管理员可以维护管理员的数据,管理员可以维护商家的数据,管理员可以维护用户的数据,管理员可以维护农产品的数据, 管理员可以维护农产品评价的数据。管理员可以维护农产品收藏的数据。管理员可以维护农产品订单的数据。管理员可以维护交流论坛的数据。 管理员可以维护系统公告的数据。管理员可以维护基础类型的数据,管理员可以维护系统轮播图的数据。 接下来切换到商家角色的账号, 以上是商家角色的菜单权限。 最后切换到用户网页端, 以上是用户网页端的首页界面 以上是用户网页端交流论坛模块的界面 以上是用户网页端公告模块的界面。以上是用户网页端农产品模块的界面。 以上是用户网页端商家模块的界面。 以上是用户网页端的个人中心界面。以上是用户网页端购物车模块的界面。 农产品销售系统演示完成。

很多同学看这个 spring bing 的 一个生命周期啊,都可能很懵逼,他一堆步骤,他要做一堆的事,他看完之后完全不知道为什么要做这么些多的事,然后就开始死记硬背啊,给他硬记住,但实际开发的时候,哎,又不会用,所以 那这一期就彻底给讲明白这个 bing 的 生命周期,他为什么要这么做,而且会给出一个完整的例子,讲明白他为什么要这么设计, 那在家里面创建一个对象来使用,其实就是这么几个大步骤啊。那首先呢,把对象给拗出来,那这个时候呢,对象里面的属性可能还没赋值,所以那第二步就是给对象里的属性去赋值, 那给属性赋值完之后,他可能还需要一些新的属性,就把 a 属性和 b 属性融合起来,计算出一个 c 属性,让当前的对象更加完整,或者说他可能还要做一些其他的事情,然后才能去使用。所以这一步他是什么叫初始化, 初使化,初使化一些资源啊,或者初使化一些属性计算啊等等的,那初使化完了,它就真正可以用了,那用完之后呢,就需要把这个对象销毁, 销毁前呢,还可能还需要做一些事情,可能还需要做一些操作,那这就是正常的对象的一个完整的生生命周期啊,从创建到这个属性赋值,到初使化到使用,然后最后销毁的整个过程。 ok, 刚才呢,可能还是有点稍抽象啊,所以举一个最最简单的例子,好吧, 有一个数据库连接池的一个类,那你第一步就是把这个数数据库连接池类去扭一下,扭出个对象出来,那第二步呢,就是给里面属性赋值,它里边可能有什么 u r l 啊,啥用户名啊,密码等等的这些属性,然后呢给它赋值,那 这是第二步,那第三步就是初识化,那这个时候就可以去呃通过你的 u r l 用户名,密码啊,去创建几个数据库连接,先给它放着,后面需要使用的时候拿出来去用。 ok, 这就是初识化,初识化完成之后你就可以直接去用了。 ok, 然后第四步,那就去用它数据库连接已经有了用的时候直接拿出来用啊。第五步呢,那就是销毁,那系统运行结束了,然后在对象这个数据库连接池的对象销毁之前呢,可能需要做一些资源销毁啊,连接释放的一个操作,那这个时候呢,就可以在销毁这个对象销毁之前就可以去做, 所以这个 spring b 的 一个生命周期也是完全按照这个流程设计的。那第一步呢,就是 spring 呢,会通过反射去创建一个 b 的 实力去给它扭出来,只不过是通过反射去注入的这个对象,通过反射调用赛的方法去给这个 b 赋值啊,这还是是一个属性赋值的过程,只不过 spring 这里是用的是一个依赖注入,通过这个呃 i o c 去做的。那第三步呢?是 spring 这里是用的是一个依赖注入,通过这个呃 i o c 去做的。那第三步呢?就是 spring 这里是用的是一个依赖注入,通过这个呃 i o c 去做的。那第三步, 抽象化的一个前置处理,抽象化操作本身和抽象的一个后置处理,那前置处理就是说它会调用这个 pinpo pinpost processor 这个接口的一个实现类啊,去调它的什么 post process before initial lesson 这个方法去,呃,去做一些事情, 然后呢,那第二个呢,就是真正的抽象阶段呢,真正的抽象阶段呢,就是说那如果一个 pin 它实现了 initial lesson 闭的一个接口,那就可以去执行它的一个叫 after property site 这个方法。 当然了,其实初识化呢,还有第二种方式啊,就如果说我通过注解去指定了一个 unit method 的 一个方法,那就调用这个 unit method 的 对应的方法,这也是初识化,对吧?然后呢,初识化还有一个,就对应的一个后置处理,那它会调用这个 pin post processor 这个接口的时间链去执行一个什么 post process after initialization 这个方法,这就是初识化的前置处理,前置化本身,然后初识化的一个后置处理, ok, 那第四步,那我就会在代码里去使用这个 bing, 通过 auto word 去注入,或者通过其他方式就从这个 spring ioc 拿出来去用。 那第五步呢,就是销毁 bing, 因为销毁 bing 之前呢,它需要做一些资源释放的一些操作,然后呢,所以 spring 它会去看这个 bing 是 否实现了什么 disobeying 的 一个接口,如果实现了,就可以去执行一个 disdraw 的 方法,那这个方法呢,就可以去做一些释放资源的一个操作。那同样的, 如果你配置 destroy master 的 方法,那也会调用这个 destroy master 对 应那个方法去做一些资源释放啊,一些销毁的一些前置的一些处理啊, 然后呢,这就是整个的对象,从创建到属性赋值,到初识化,然后到使用到这个 销毁的整个全流程啊。当然了,有可能很多人在这个生命周期里面去看到什么额外接口啊,去做一些事情或者其他的,这些东西其实没那么重要,和你们开发的基本没什么关系啊,所以, 呃,这个东西可以不用太了解,你把上面讲的几大步骤想明白了就够了,那个才是最最最核心的流程。 当然了,可能很多同学会发现有两个注解好像也是在生命周期里的,有时候开发也会用到。一个呢,是 post construct 注解,另外一个是 predestroy 注解。其实呢,这两个注解它不是 spring 的 注解,它是 java e 定义的注解。 这两个注解在 spring 中秋这个生命周期里面儿,其实也是可以用的。 postconstruct 注解呢,其实和引领 method 其实差不多,都是初始化的时候用的。 free destroy 注解就销毁之前用的,其实和 destroy method 也差不多,都是销毁前去用的。 只是啊, java e 这两个注解执行的时间稍微的比什么引领 method 比这个 destroy method 稍微早一点点啊,但是作用都是相似的,基本上都是可以互相替代着使用的。 那 ok 呢,来写一个简单 demo, 大 致的一个需求是这样的,就是系统运行过程中,有些数据呢,需要记录到文件里面,然后呢,系统运行完之后呢,需要把记录这个文件路径和文件大小去给它存到数据库里面,方便后续的去查看这些文件。 ok, 那 你要记录这个文件,那就需要去自己写一个叫 file record 这个类,这个类呢有一个属性叫 file service, 这个 service 呢,就是把文件路径和文件大小去保存到数据库里面。 那对于 spring 来说,那第一步可以通过反射去创建 filerecord 的 这个类对象。那第二步呢,它是不是要从容器中取出 fileservice 这个类,去复制给这个 filerecord 的 这个属性啊?然后呢,再第三步就是抽象,就是调用 postconstruct 注解对应的一个抽象方法,当然这里写到这个 metadata 其实都可以啊,都是一样的。 这个方法里面呢,我们就可以在本地去创建一个文件文件,也就是说 spring 去把这个 b 拗出来属性复制之后,它就会调这个方法,自动地在本地创建一个文件。 ok, 那 第四步呢,你就可以去呃,去依赖注入去使用这个类对象了。然后我这里写了一个 record 的 方法,就可以把呃这个数据记录到文件里面。 ok, 那 第五步,那系统运行完之后,那我销毁对象之前,那就可以调用这个 pretty 作为 draw 注解对应的一个方法,去把这个文件路径和文件大小给它存到数据库里面。当然你这里用什么 destroy method 其实也是同样的效果啊,这就是借助了 spring 一个生命周期的一个拓展点,去实现了一个小工具。

提到 spi, 很多同学会想到 g d k。 自带的那个机制 spring boot, 其实把它玩出了花,甚至可以说没有 spi 就 没有 spring boot 的 一个自动装配。今天我们就来深入拆解 spring boot, 它是如何利用 spi 实现这个组建化扩展的。 如果你还在盲目刷题,不知道面试到底考什么,我已经把简历优化模板、八股文高频提库、面试全流程硬的策略突击学习路线图全都打包整理好了,覆盖从头到谈心的每个环节,需要的评论区扣六六六直接领走。首先,什么是 spi 呢?简单来说, spi 它是一种服 不发现机制。通常我们写代码是 a p i 模式,接口和实心类都在我们手里,直接拎或者注入都行。但在框架开发中,框架定义了接口,但不知道谁来实现。比如 g d b c 定义了 driver 接口。数据库厂商 my c o rick, 他 负责实现,这时候就需要 s p i。 框架,他去寻找并加载 class pass 下的所有实心类。一句话总结, a p i 是 你调用接口, s p i 呢?是接口寻找实现。既然 g d k。 已经有 sales load 了,为什么 spring boot 还要自己造一套呢? g d k s p i 的 痛点是,它会一次性实力化 class pass 下所有实现类。如果你只想用其中一个,或者想按照顺序加载 g d k, 它做不到 spring boot s p i 的 一个进化呢? spring, 它就定义了自己的一个 spring factory loader, 它读取的是 metal info spring 点 factories 的 文件格式是 key value。 它有这么一些优势,第一点,它更高效加载配置类,但不一定立刻实力 话。第二点,支持排序,它可以通过 order 注解控制加载的一个顺序。第三点,支持扩展,不仅仅是接口的一个实现,还可以自动配置类的一个列表。 spring boot 的 一个 s p i 最核心的应用场景就是自 动装配。当你引入 spring boot startup web 时, spring boot 它启动的时候会做这几件事情,第一,扫描所有账号包下的一个 met in for spring 点 factories, 找到 key 引 enable auto configuration 的 一个配置类的一个列表,它加载这些类,比如 web m v c auto configuration。 接着它根据条件注解决定这些是否生效。这就是为什么你只要引入依赖什么都不用配 web 服务就能跑起来的一个原因。最后提个醒,如果你在使用 spring boot 二点七或者三点零 加 s p i 文件,它变了。为了提高启动的一个速度, spring boot 逐渐废弃了。 spring 点 factories 里面的一个自动配置的部分,取代的是 matter input 的 文件。如果你自己在开发 starter, 一定要注意这个版本的差异,否则你的一个配置可能不会生效。总结下, s p i 是 解偶的一个关键机制,实现了接口定义与 实现的一个分离。 spring boot 它增强了 s p i, 加上了条件装配和排序,成就了自动配置的一个基石。开发 starter 必会。如果你想分装通用的一个中间件,理解 s p i, 它是第一步。理解了 s p i, 你 就不再是 spring boot 的 一个使用者,而是能够驾驭它的一个扩展者。