分享新鲜事
photo_camera
公开分享 2019-03-03
创建应用

开发者使用支付宝账号登录开放平台(需实名认证的支付宝账号),根据实际需求创建应用(如“支付应用”)。

备注:创建应用时的应用状态为“开发中”,无法在线上正式调用接口。

生活号(原服务窗)应用请参考:创建生活号应用。

填写应用基础信息 应用基础信息在开发应用过程中可以无需审核随时完善。

需要完善的内容 作用 应用名称 应用名称和应用图标会在授权、分享的场景中露 出,请准确填写相关信息 应用图标

注意:应用名称和应用图标会在应用申请上线时进行审核,所以在配置时,建议先了解相关审核规则,点此了解。

添加应用功能 开发者在创建应用时,需要先选择“使用场景”:

商户服务型应用:适用于服务商,为商户开发应用,拓展商户使用,详见供他人使用; 自用型应用:使用开放的功能,为自己或自己公司开发应用,详见自己使用。 TIPS:开发者和使用者的区别,开发者:应用(功能或者服务)的开发人员(或者企业),使用者:购买和使用应用(功能或者服务)的人员(或者企业)。当开发者自己使用自己开发的应用时,开发者和使用者的两个身份是重合的。 开发者在开发过程中,可以添加自己需要的功能到“功能列表”。 不同的功能会有不同的使用条件,如果某个功能的使用条件为签约,则使用者在使用此功能前,需要签约对应的产品,具体签约方式,可参考“如何使用应用”。

配置应用环境 开发者所需配置内容请参考:

字段名称 字段描述 应用网关

(对应下图1)

用于接收支付宝异步通知,例如口碑开店中,需要配置此网关来接收开发者门店被动通知。

授权回调地址

(对应下图2)

第三方授权或用户信息授权后回调地址。授权链接中配置的redirect_uri的值必须与此值保持一致。(如:https://www.alipay.com) 注:当填入该地址时,系统会自动进行安全检测,详情请参考安全检测。

RSA(SHA256)密钥

(对应下图3)

开发者要保证接口中使用的私钥与此处的公钥匹配,否则无法调用接口。可参考密钥的生成与配置,且接口参数sign_type=RSA2。

RSA(SHA1)密钥

(对应下图4)

同上,且接口参数sign_type=RSA。

界面请参考:

如何生成与配置密钥详见 签名专区。 TIPS:必须填写“接口加密方式”(加密方式只需填写一个),才可以提交审核。 应用申请上线 应用开发完成后,请开发者自行进行验收并完成安全性检查(安全性检查可参考《开放平台第三方应用安全开发指南》),验收检查完成后,可“提交审核”。应用上线成功后,状态变为已上线,该状态下的应用能够调用生产环境的接口。

开发者点击“提交审核”后,预计会有1个工作日的审核时间,请耐心等待。如需更快收到通知,请使用支付宝客户端扫码接收审核结果。

应用上线后可新增功能、删除功能,操作后实时生效。删除功能时请谨慎操作,如果线上已经有用户使用此功能,删除功能后会导致无法使用。

协作费相关说明 协作协议可在应用申请上线时以及应用上线后申请。

一、应用申请上线时

如下图,如果开发者选择的功能可签署协作协议,例如“当面付”,就可以在应用申请上线时确认签署协作协议。查看如何获得协作费。

注意:由于部分协作协议之间会有互斥关系,在签约协作协议的时候,会同时作废掉之前签约的互斥协议。而作废协议会导致协作费费率等的变化,因此当页面提示有作废的协议时,请谨慎选择。

二、应用上线后申请

应用上线后再添加的功能中,如果可签署协作协议,页面上会看到相关提示:

签约协作协议后,在“服务商中心”菜单“返佣管理——返佣协议信息”页面可查看协议信息:

三、历史应用补充签约协作协议

如果开发者的应用在2016.07.28日前已经上线,且未在上线时签约协作费协议,此时如想获得协作费,需要创建一个新的应用,并选择可获得协作费的功能,如当面付等产品(获得协作费的条件可参考协作费公告)。这样在应用申请上线时就会出现确认协作协议的页面,此时只要勾选该协议,并提交应用上线审核即可,提交后,即可在“账户及密钥管理”页面查看协作协议是否生效。协议生效后,账号下的所有应用在满足协作费条件的情况下,就能获得协作费。

协作费计算方式 :开发者可在“规则中心-公告区”查看协作费计算方式。

完整阅读
favorite 2
reply 0
公开分享 2019-03-03
重拾后端之Spring Boot(一):REST API的搭建可以这样简单

话说我当年接触Spring的时候着实兴奋了好一阵,IoC的概念当初第一次听说,感觉有种开天眼的感觉。记得当时的web框架和如今的前端框架的局面差不多啊,都是群雄纷争。但一晃好多年没写过后端,代码这东西最怕手生,所以当作重新学习了,顺便写个学习笔记。 Spring Boot是什么? 还恍惚记得当初写Spring的时候要配置好多xml(在当时还是相对先进的模式),虽然实现了松耦合,但这些xml却又成为了项目甩不掉的负担 — 随着项目越做越大,这些xml的可读性和可维护性极差。后来受.Net平台中Annotation的启发,Java世界中也引入了元数据的修饰符,Spring也可以使用这种方式进行配置。到了近些年,随着Ruby on Rails的兴起而流行开的 Convention over configuration 理念开始深入人心。那什么是 Convention over configuration 呢?简单来说就是牺牲一部分的自由度来减少配置的复杂度,打个比方就是如果你如果遵从我定义的一系列规则(打个比方,文件目录结构必须是blablabla的样子,文件命名必须是nahnahnah 的样子),那么你要配置的东西就非常简单甚至可以零配置。既然已经做到这个地步了,各种脚手架项目就纷纷涌现了,目的只有一个:让你更专注在代码的编写,而不是浪费在各种配置上。这两年前端也有类似趋势,各种前端框架的官方CLI纷纷登场:create-react-app,angular-cli,vue-cli等等。 那么Spring Boot就是Spring框架的脚手架了,它可以帮你快速搭建、发布一个Spring应用。官网列出了Spring Boot的几个主要目标

提供一种快速和广泛适用的Spring开发体验 开箱即用却又可以适应各种变化 提供一系列开发中常用的“非功能性”的特性(比如嵌入式服务器、安全、度量、自检及外部配置等) 不生成任何代码,不需要xml配置

安装Spring Boot 官方推荐的方式是通过sdkman( sdkman.io/install.htm… )来进行安装,当然这是对 *nix 而言。题外话,如果你使用的是Windows 10,真心希望大家安装Windows 10的Linux子系统,微软官方出品、原生支持,比虚拟机不知道快到那里去了 具体安装过程可以参考 linux.cn/article-720… 。安装 sdkman 的步骤非常简单,就两步:

打开一个terminal,输入 curl -s “https://get.sdkman.io“ | bash 安装结束后,重启terminal,输入 source “$HOME/.sdkman/bin/sdkman-init.sh”

可以在terminal中验证一下是否安装成功 sdk version,如果你看到了版本号就是安装好了。 接下来,就可以安装Spring Boot了,还是打开terminal输入 sdk install springboot就ok了。 当然其实Mac的童鞋可以省略掉之前的sdkman安装直接使用 brew 安装,也是两步:

在terminal中输入 brew tap pivotal/tap 然后 brew install springboot

验证的话可以输入 spring —version 看看是否正常输出了版本号。 创建一个工程 有很多种方法可以创建一个Spring Boot项目,其中最简单的一种是通过一个叫Spring Initializr的在线工具 http://start.spring.io/ 进行工程的生成。如下图所示,只需填写一些参数就可以生成一个工程包了。 使用Spring Initializr进行工程的生成 如果你使用Intellij IDEA进行开发,里面也集成了这个工具,大家可以自行尝试。 Intellij IDEA中集成了 Spring Initializr 但下面我们要做的不是通过这种方式,而是手动的通过命令行方式创建。创建的是gradle工程,而不是maven的,原因呢是因为个人现在对于xml类型的配置文件比较无感;-),官方推荐使用gradle 2.14.1版本,请自行安装gradle。下面来建立一个gradle工程,其实步骤也不算太难:

新建一个工程目录 mkdir todo 在此目录下使用gradle进行初始化 gradle init(就和在node中使用 npm init 的效果类似)

这个命令帮我们建立一个一个使用gradle进行管理的模版工程:

build.gradle:有过Android开发经验的童鞋可能觉得很亲切的,这个就是我们用于管理和配置工程的核心文件了。 gradlew:用于 *nix 环境下的gradle wrapper文件。 gradlew.bat:用于 Windows 环境下的gradle wrapper文件 setting.gradle:用于管理多项目的gradle工程时使用,单项目时可以不做理会。 gradle目录:wrapper的jar和属性设置文件所在的文件夹。

简单说两句什么是 gradle wrapper。你是否有过这样的经历?在安装/编译一个工程时需要一些先决条件,需要安装一些软件或设置一些参数。如果这一切比较顺利还好,但很多时候我们会发现这样那样的问题,比如版本不对,参数没设置等等。gradle wrapper 就是这样一个让你不会浪费时间在配置问题上的方案。它会对应一个开发中使用的gradle版本,以确保任何人任何时候得到的结果是一致的。

./gradlew : 在 *nix 平台上运行,例如Linux或Mac OS X gradlew 在Windows平台运行(是通过gradlew.bat来执行的)

更多关于wrapper的知识可以去 docs.gradle.org/current/use… 查看。 那么下面我们打开默认生成的 build.gradle 文件,将其改造成下面的样子: /*

这个build文件是由Gradle的 init 任务生成的。 * 更多关于在Gradle中构建Java项目的信息可以查看Gradle用户文档中的 Java项目快速启动章节 https://docs.gradle.org/3.3/userguide/tutorial_java_projects.html / // 在这个段落中你可以声明你的build脚本需要的依赖和解析下载该依赖所使用的仓储位置 buildscript { ext { springBootVersion = '1.4.3.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } / 在这个段落中你可以声明使用哪些插件 apply plugin: ‘java’ 代表这是一个Java项目,需要使用java插件 如果想生成一个 Intellij IDEA 的工程,类似的如果要生成 eclipse工程,就写 apply plugin: ‘eclipse’ 同样的我们要学的是Spring Boot,所以应用Spring Boot插件 */ apply plugin: ‘java’ apply plugin: ‘idea’ apply plugin: ‘eclipse’ apply plugin: ‘org.springframework.boot’ // 在这个段落中你可以声明编译后的Jar文件信息 jar { baseName = ‘todo’ version = ‘0.0.1-SNAPSHOT’ }

// 在这个段落中你可以声明在哪里可以找到你的项目依赖 repositories { // 使用 ‘jcenter’ 作为中心仓储查询解析你的项目依赖。 // 你可以声明任何 Maven/Ivy/file 类型的依赖类库仓储位置 mavenCentral() }

// 在这个段落中你可以声明源文件和目标编译后的Java版本兼容性 sourceCompatibility = 1.8 targetCompatibility = 1.8

// 在这个段落你可以声明你的项目的开发和测试所需的依赖类库 dependencies { compile(‘org.springframework.boot:spring-boot-starter-web’) testCompile(‘org.springframework.boot:spring-boot-starter-test’) }复制代码首先脚本依赖中的 spring-boot-gradle-plugin 有什么作用呢?它提供了以下几个功能:

简化执行和发布:它可以把所有classpath的类库构建成一个单独的可执行jar文件,这样可以简化你的执行和发布等操作。 自动搜索入口文件:它会扫描 public static void main() 函数并且标记这个函数的宿主类为可执行入口。 简化依赖:一个典型的Spring应用还是需要很多依赖类库的,想要配置正确这些依赖挺麻烦的,所以这个插件提供了内建的依赖解析器会自动匹配和当前Spring Boot版本匹配的依赖库版本。

在最后一个段落中,我们看到我们的项目依赖两个类库,一个是 spring-boot-starter-web ,另一个是 spring-boot-starter-test。Spring Boot提供了一系列依赖类库的“模版”,这些“模版”封装了很多依赖类库,可以让我们非常方便的引用自己想实现的功能所需要的类库。如果我们去看看这个 spring-boot-starter-web 中究竟引用了什么,我们可以看看它的artifact文件(到 search.maven.org/ 可以查看):

4.0.0 org.springframework.boot spring-boot-starters 1.4.3.RELEASE spring-boot-starter-web Spring Boot Web Starter Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container http://projects.spring.io/spring-boot/ Pivotal Software, Inc. http://www.spring.io ${basedir}/../.. org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-tomcat org.hibernate hibernate-validator com.fasterxml.jackson.core jackson-databind org.springframework spring-web org.springframework spring-webmvc 复制代码IDE支持 一般做Java开发,大部分团队还是喜欢用一个IDE,虽然我还是更偏爱文本编辑器类型的(比如sublime,vscode,atom等)。但是如果非挑一个重型IDE的话,我更喜欢Intellij IDEA。 使用IDEA的import project功能选中 build.gradle,将工程导入。由于是个gradle工程,请把 View->Tools Window->Gradle 的视图窗口调出来。 Gradle工具窗口 点击左上角的刷新按钮可以将所有依赖下载类库下来。注意IDEA有时提示是否要配置wrapper使用带源码的gradle包。 提示使用带源码的gradle以便有API的文档 如果遇到不知道什么原因导致一直刷新完成不了的情况,请在项目属性中选择 Use local gradle distribution image_1b77vqast1d2h1qioeepsdm1tkb9.png-192.5kB 第一个Web API 领域对象 那么我们的源代码目录在哪里呢?我们得手动建立一个,这个目录一般情况下是 src/main/java。好的,下面我们要开始第一个RESTful的API搭建了,首先还是在 src/main/java 下新建一个 package。既然是本机的就叫 dev.local 吧。我们还是来尝试建立一个 Todo 的Web API,在 dev.local 下建立一个子 package: todo,然后创建一个Todo的领域对象: package dev.local.todo;

/**

Todo是一个领域对象(domain object) Created by wangpeng on 2017/1/24. */ public class Todo { private String id; private String desc; private boolean completed;

public String getId() {

return id; }

public void setId(String id) {

this.id = id; }

public String getDesc() {

return desc; }

public void setDesc(String desc) {

this.desc = desc; }

public boolean isCompleted() {

return completed; }

public void setCompleted(boolean completed) {

this.completed = completed; } }复制代码这个对象很简单,只是描述了todo的几个属性: id 、 desc 和 completed 。我们的API返回或接受的参数就是以这个对象为模型的类或集合。 构造Controller 我们经常看到的RESTful API是这样的:http://local.dev/todos、http://local.dev/todos/1 。Controller就是要暴露这样的API给外部使用。现在我们同样的在 todo 下建立一个叫 TodoController 的java文件 package dev.local.todo;

import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList; import java.util.List;

/**

使用@RestController来标记这个类是个Controller */ @RestController public class TodoController { // 使用@RequstMapping指定可以访问的URL路径 @RequestMapping(“/todos”) public List getAllTodos() { List todos = new ArrayList<>(); Todo item1 = new Todo(); item1.setId("1"); item1.setCompleted(false); item1.setDesc("go swimming"); todos.add(item1); Todo item2 = new Todo(); item2.setId("1"); item2.setCompleted(true); item2.setDesc("go for lunch"); todos.add(item2); return todos; } }复制代码上面这个文件也比较简单,但注意到以下几个事情: @RestController 和 @RequestMapping 这两个是元数据注释,原来在.Net中很常见,后来Java也引进过来。一方面它们可以增加代码的可读性,另一方面也有效减少了代码的编写。具体机理就不讲了,简单来说就是利用Java的反射机制和IoC模式结合把注释的特性或属性注入到被注释的对象中。 我们看到 List getAllTodos() 方法中简单的返回了一个List,并未做任何转换成json对象的处理,这个是Spring会自动利用 Jackson 这个类库的方法将其转换成了json。

完整阅读
favorite_border 0
reply 0
公开分享 2019-02-28
重拾后端之Spring Boot(二):MongoDB的无缝集成

上一节,我们做的那个例子有点太简单了,通常的后台都会涉及一些数据库的操作,然后在暴露的API中提供处理后的数据给客户端使用。那么这一节我们要做的是集成MongoDB ( www.mongodb.com )。 MongoDB是什么? MongoDB是一个NoSQL数据库,是NoSQL中的一个分支:文档数据库。和传统的关系型数据库比如Oracle、SQLServer和MySQL等有很大的不同。传统的关系型数据库(RDBMS)已经成为数据库的代名词超过20多年了。对于大多数开发者来说,关系型数据库是比较好理解的,表这种结构和SQL这种标准化查询语言毕竟是很大一部分开发者已有的技能。那么为什么又搞出来了这个什么劳什子NoSQL,而且看上去NoSQL数据库正在飞快的占领市场。 NoSQL的应用场景是什么? 假设说我们现在要构建一个论坛,用户可以发布帖子(帖子内容包括文本、视频、音频和图片等)。那么我们可以画出一个下图的表关系结构。 论坛的简略ER图 这种情况下我们想一下这样一个帖子的结构怎么在页面中显示,如果我们希望显示帖子的文字,以及关联的图片、音频、视频、用户评论、赞和用户的信息的话,我们需要关联八个表取得自己想要的数据。如果我们有这样的帖子列表,而且是随着用户的滚动动态加载,同时需要监听是否有新内容的产生。这样一个任务我们需要太多这种复杂的查询了。 NoSQL解决这类问题的思路是,干脆抛弃传统的表结构,你不是帖子有一个结构关系吗,那我就直接存储和传输一个这样的数据给你,像下面那样。 { "id":"5894a12f-dae1-5ab0-5761-1371ba4f703e", "title":"2017年的Spring发展方向", "date":"2017-01-21", "body":"这篇文章主要探讨如何利用Spring Boot集成NoSQL", "createdBy":User, "images":["http://dev.local/myfirstimage.png","http://dev.local/mysecondimage.png"], "videos":[ {"url":"http://dev.local/myfirstvideo.mp4", "title":"The first video"}, {"url":"http://dev.local/mysecondvideo.mp4", "title":"The second video"} ], "audios":[ {"url":"http://dev.local/myfirstaudio.mp3", "title":"The first audio"}, {"url":"http://dev.local/mysecondaudio.mp3", "title":"The second audio"} ] }复制代码NoSQL一般情况下是没有Schema这个概念的,这也给开发带来较大的自由度。因为在关系型数据库中,一旦Schema确定,以后更改Schema,维护Schema是很麻烦的一件事。但反过来说Schema对于维护数据的完整性是非常必要的。 一般来说,如果你在做一个Web、物联网等类型的项目,你应该考虑使用NoSQL。如果你要面对的是一个对数据的完整性、事务处理等有严格要求的环境(比如财务系统),你应该考虑关系型数据库。 在Spring中集成MongoDB 在我们刚刚的项目中集成MongoDB简单到令人发指,只有三个步骤:

在 build.gradle 中更改 compile('org.springframework.boot:spring-boot-starter-web') 为 compile("org.springframework.boot:spring-boot-starter-data-rest") 在 Todo.java 中给 private String id; 之前加一个元数据修饰 @Id 以便让Spring知道这个Id就是数据库中的Id 新建一个如下的 TodoRepository.java

package dev.local.todo;

import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource(collectionResourceRel = "todo", path = "todo") public interface TodoRepository extends MongoRepository<Todo, String>{ }复制代码此时我们甚至不需要Controller了,所以暂时注释掉 TodoController.java 中的代码。然后我们 ./gradlew bootRun 启动应用。访问 http://localhost:8080/todo 我们会得到下面的的结果。 { _embedded: { todo: [ ] }, _links: { self: { href: "http://localhost:8080/todo" }, profile: { href: "http://localhost:8080/profile/todo" } }, page: { size: 20, totalElements: 0, totalPages: 0, number: 0 } }复制代码我勒个去,不光是有数据集的返回结果 todo: [ ] ,还附赠了一个links对象和page对象。如果你了解 Hypermedia 的概念,就会发现这是个符合 Hypermedia REST API返回的数据。 说两句关于 MongoRepository<Todo, String> 这个接口,前一个参数类型是领域对象类型,后一个指定该领域对象的Id类型。 Hypermedia REST 简单说两句Hypermedia是什么。简单来说它是可以让客户端清晰的知道自己可以做什么,而无需依赖服务器端指示你做什么。原理呢,也很简单,通过返回的结果中包括不仅是数据本身,也包括指向相关资源的链接。拿上面的例子来说(虽然这种默认状态生成的东西不是很有代表性):links中有一个profiles,我们看看这个profile的链接 http://localhost:8080/profile/todo 执行的结果是什么: { "alps" : { "version" : "1.0", "descriptors" : [ { "id" : "todo-representation", "href" : "http://localhost:8080/profile/todo", "descriptors" : [ { "name" : "desc", "type" : "SEMANTIC" }, { "name" : "completed", "type" : "SEMANTIC" } ] }, { "id" : "create-todo", "name" : "todo", "type" : "UNSAFE", "rt" : "#todo-representation" }, { "id" : "get-todo", "name" : "todo", "type" : "SAFE", "rt" : "#todo-representation", "descriptors" : [ { "name" : "page", "doc" : { "value" : "The page to return.", "format" : "TEXT" }, "type" : "SEMANTIC" }, { "name" : "size", "doc" : { "value" : "The size of the page to return.", "format" : "TEXT" }, "type" : "SEMANTIC" }, { "name" : "sort", "doc" : { "value" : "The sorting criteria to use to calculate the content of the page.", "format" : "TEXT" }, "type" : "SEMANTIC" } ] }, { "id" : "patch-todo", "name" : "todo", "type" : "UNSAFE", "rt" : "#todo-representation" }, { "id" : "update-todo", "name" : "todo", "type" : "IDEMPOTENT", "rt" : "#todo-representation" }, { "id" : "delete-todo", "name" : "todo", "type" : "IDEMPOTENT", "rt" : "#todo-representation" }, { "id" : "get-todo", "name" : "todo", "type" : "SAFE", "rt" : "#todo-representation" } ] } }复制代码这个对象虽然我们暂时不是完全的理解,但大致可以猜出来,这个是todo这个REST API的元数据描述,告诉我们这个API中定义了哪些操作和接受哪些参数等等。我们可以看到todo这个API有增删改查等对应功能。 其实呢,Spring是使用了一个叫 ALPS (alps.io/spec/index.… 的专门描述应用语义的数据格式。摘出下面这一小段来分析一下,这个描述了一个get方法,类型是 SAFE 表明这个操作不会对系统状态产生影响(因为只是查询),而且这个操作返回的结果格式定义在 todo-representation 中了。 todo-representation { "id" : "get-todo", "name" : "todo", "type" : "SAFE", "rt" : "#todo-representation" }复制代码还是不太理解?没关系,我们再来做一个实验,启动 PostMan (不知道的同学,可以去Chrome应用商店中搜索下载)。我们用Postman构建一个POST请求: 用Postman构建一个POST请求添加一个Todo 执行后的结果如下,我们可以看到返回的links中包括了刚刚新增的Todo的link http://localhost:8080/todo/588a01abc5d0e23873d4c1b8 ( 588a01abc5d0e23873d4c1b8 就是数据库自动为这个Todo生成的Id),这样客户端可以方便的知道指向刚刚生成的Todo的API链接。 执行添加Todo后的返回Json数据 再举一个现实一些的例子,我们在开发一个“我的”页面时,一般情况下除了取得我的某些信息之外,因为在这个页面还会有一些可以链接到更具体信息的页面链接。如果客户端在取得比较概要信息的同时就得到这些详情的链接,那么客户端的开发就比较简单了,而且也更灵活了。 其实这个描述中还告诉我们一些分页的信息,比如每页20条记录(size: 20)、总共几页(totalPages:1)、总共多少个元素(totalElements: 1)、当前第几页(number: 0)。当然你也可以在发送API请求时,指定page、size或sort参数。比如 http://localhost:8080/todos?page=0&size=10 就是指定每页10条,当前页是第一页(从0开始)。 魔法的背后 这么简单就生成一个有数据库支持的REST API,这件事看起来比较魔幻,但一般这么魔幻的事情总感觉不太托底,除非我们知道背后的原理是什么。首先再来回顾一下 TodoRepository 的代码: @RepositoryRestResource(collectionResourceRel = "todo", path = "todo") public interface TodoRepository extends MongoRepository<Todo, String>{ }复制代码Spring是最早的几个IoC(控制反转或者叫DI)框架之一,所以最擅长的就是依赖的注入了。这里我们写了一个Interface,可以猜到Spring一定是有一个这个接口的实现在运行时注入了进去。如果我们去 spring-data-mongodb 的源码中看一下就知道是怎么回事了,这里只举一个小例子,大家可以去看一下 SimpleMongoRepository.java ( 源码链接 ),由于源码太长,我只截取一部分: public class SimpleMongoRepository<T, ID extends Serializable> implements MongoRepository<T, ID> {

private final MongoOperations mongoOperations; private final MongoEntityInformation<T, ID> entityInformation; /** * Creates a new {@link SimpleMongoRepository} for the given {@link MongoEntityInformation} and {@link MongoTemplate}. * * @param metadata must not be {@literal null}. * @param mongoOperations must not be {@literal null}. */ public SimpleMongoRepository(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) { Assert.notNull(mongoOperations); Assert.notNull(metadata); this.entityInformation = metadata; this.mongoOperations = mongoOperations; } /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object) */ public <S extends T> S save(S entity) { Assert.notNull(entity, "Entity must not be null!"); if (entityInformation.isNew(entity)) { mongoOperations.insert(entity, entityInformation.getCollectionName()); } else { mongoOperations.save(entity, entityInformation.getCollectionName()); } return entity; } ... public T findOne(ID id) { Assert.notNull(id, "The given id must not be null!"); return mongoOperations.findById(id, entityInformation.getJavaType(), entityInformation.getCollectionName()); } private Query getIdQuery(Object id) { return new Query(getIdCriteria(id)); } private Criteria getIdCriteria(Object id) { return where(entityInformation.getIdAttribute()).is(id); } ...

}复制代码也就是说其实在运行时Spring将这个类或者其他具体接口的实现类注入了应用。这个类中有支持各种数据库的操作。我了解到这步就觉得ok了,有兴趣的同学可以继续深入研究。

完整阅读
favorite_border 0
reply 0
公开分享 2019-02-28
FeignClient注解及参数

转载form https://www.cnblogs.com/moonandstar08/p/7565442.html

一、FeignClient注解

FeignClient注解被@Target(ElementType.TYPE)修饰,表示FeignClient注解的作用目标在接口上

1 2 3 4 5 @FeignClient(name = "github-client", url = "https://api.github.com", configuration = GitHubExampleConfig.class) public interface GitHubClient { @RequestMapping(value = "/search/repositories", method = RequestMethod.GET) String searchRepo(@RequestParam("q") String queryStr); }  声明接口之后,在代码中通过@Resource注入之后即可使用。@FeignClient标签的常用属性如下:

name:指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现 url: url一般用于调试,可以手动指定@FeignClient调用的地址 decode404:当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException configuration: Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口 fallbackFactory: 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码 path: 定义当前FeignClient的统一前缀 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @FeignClient(name = "github-client", url = "https://api.github.com", configuration = GitHubExampleConfig.class, fallback = GitHubClient.DefaultFallback.class) public interface GitHubClient { @RequestMapping(value = "/search/repositories", method = RequestMethod.GET) String searchRepo(@RequestParam("q") String queryStr);

/** * 容错处理类,当调用失败时,简单返回空字符串 */ @Component public class DefaultFallback implements GitHubClient { @Override public String searchRepo(@RequestParam("q") String queryStr) { return ""; } }

}  在使用fallback属性时,需要使用@Component注解,保证fallback类被Spring容器扫描到,GitHubExampleConfig内容如下:

1 2 3 4 5 6 7 @Configuration public class GitHubExampleConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }   在使用FeignClient时,Spring会按name创建不同的ApplicationContext,通过不同的Context来隔离FeignClient的配置信息,在使用配置类时,不能把配置类放到Spring App Component scan的路径下,否则,配置类会对所有FeignClient生效.

二、Feign Client 和@RequestMapping 当前工程中有和Feign Client中一样的Endpoint时,Feign Client的类上不能用@RequestMapping注解否则,当前工程该endpoint http请求且使用accpet时会报404 Controller: 1 2 3 4 5 6 7 8 9 10 11 12 13 @RestController @RequestMapping("/v1/card") public class IndexApi {

@PostMapping("balance") @ResponseBody public Info index() { Info.Builder builder = new Info.Builder(); builder.withDetail("x", 2); builder.withDetail("y", 2); return builder.build(); }

} Feign Client

1 2 3 4 5 6 7 8 9 10 11 12 13 @FeignClient( name = "card", url = "http://localhost:7913", fallback = CardFeignClientFallback.class, configuration = FeignClientConfiguration.class ) @RequestMapping(value = "/v1/card") public interface CardFeignClient {

@RequestMapping(value = "/balance", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) Info info();

}   if @RequestMapping is used on class, when invoke http /v1/card/balance, like this :

如果 @RequestMapping注解被用在FeignClient类上,当像如下代码请求/v1/card/balance时,注意有Accept header:

1 2 3 4 Content-Type:application/json Accept:application/json

POST http://localhost:7913/v1/card/balance 那么会返回 404。

如果不包含Accept header时请求,则是OK:

1 2 Content-Type:application/json POST http://localhost:7913/v1/card/balance 或者像下面不在Feign Client上使用@RequestMapping注解,请求也是ok,无论是否包含Accept:

1 2 3 4 5 6 7 8 9 10 11 12 13 @FeignClient( name = "card", url = "http://localhost:7913", fallback = CardFeignClientFallback.class, configuration = FeignClientConfiguration.class )

public interface CardFeignClient {

@RequestMapping(value = "/v1/card/balance", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) Info info();

}

三、Feign请求超时问题 Hystrix默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入fallback代码。而首次请求往往会比较慢(因为Spring的懒加载机制,要实例化一些类),这个响应时间可能就大于1秒了 解决方案有三种,以feign为例。 方法一 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000 该配置是让Hystrix的超时时间改为5秒 方法二 hystrix.command.default.execution.timeout.enabled: false 该配置,用于禁用Hystrix的超时时间 方法三 feign.hystrix.enabled: false 该配置,用于索性禁用feign的hystrix。该做法除非一些特殊场景,不推荐使用。 参见:http://www.itmuch.com/spring-cloud-sum-feign/

完整阅读
favorite 1
reply 0
公开分享 2019-02-28
springcloud系列之feign服务间远程调用

1.场景还原     在微服务架构中,服务间调用是家常便饭;如何进行服务间远程调用呢?今天笔者就springcloud中feign远程调用给大伙讲解一番,希望能够有所帮助

2.实现方案 ①加入pom依赖

org.springframework.cloud spring-cloud-starter-feign ②启动类的注解配置

@SpringBootApplication @ComponentScan("com.yivi") @MapperScan({"com.yivi.yiviproj.microservicepayment.dao","com.yivi.yivisender.dispatchdata.dao"}) @EnableTransactionManagement @EnableCaching @EnableEurekaClient @EnableCircuitBreaker @EnableFeignClients public class MicroServicePaymentApplication{

public static void main(String[] args) { SpringApplication.run(MicroServicePaymentApplication.class, args); } } ③feign远程调用接口申明

@FeignClient(name = "micro-service-dispatch",fallback = PaymentDispatchHystrixClientFallback.class) public interface PaymentDispatchFeignClient {

@RequestMapping(value = "/v1.0/dispatch/selectByPrimaryKey/{dispatchId}", method = RequestMethod.GET) YiViDispatchOrder selectByPrimaryKey(@PathVariable("dispatchId")String dispatchId); @RequestMapping(value = "/v1.0/dispatch/updateByPrimaryKeySelective", method = RequestMethod.POST) int updateByPrimaryKeySelective(@RequestBody YiViDispatchOrder record); @RequestMapping(value = "/v1.0/dispatch/insert", method = RequestMethod.POST) int insert(@RequestBody YiViDispatchOrderStatusLog record); @RequestMapping(value = "/v1.0/dispatch/insert/{dispatchOrderCode}", method = RequestMethod.GET) YiViDispatchOrder findDispatchOrderByOrderCode(@PathVariable("dispatchOrderCode")String dispatchOrderCode);

} 这里得注意,请求方式不能简写,如@GetMapping或者@PostMapping,必须以@RequestMapping(method = RequestMethod.Get)替代

④请求失败回调类

@Component @Slf4j public class PaymentDispatchHystrixClientFallback implements PaymentDispatchFeignClient {

@Override public YiViDispatchOrder selectByPrimaryKey(String dispatchId) { return null; }

@Override public int updateByPrimaryKeySelective(YiViDispatchOrder record) { return 0; }

@Override public int insert(YiViDispatchOrderStatusLog record) { return 0; }

@Override public YiViDispatchOrder findDispatchOrderByOrderCode(String dispatchOrderCode) { return null; }

} feign自身支持hystrix熔断回调,防止一个服务挂掉,殃及另一个服务,俗称雪崩效应。

3.feign配置 ①测试用例中你可能会遇到测试用例运行成功了却抛出如下一串错:

org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'eurekaAutoServiceRegistration': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!) ②添加feign配置

/**

  • @auther zx

  • @date 2018/8/11 14:26

  • @discribution 处理eurekaAutoServiceRegistration异常 */ @Component public class FeignBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { if (containsBeanDefinition(beanFactory, "feignContext", "eurekaAutoServiceRegistration")) { BeanDefinition bd = beanFactory.getBeanDefinition("feignContext"); bd.setDependsOn("eurekaAutoServiceRegistration"); } }

    private boolean containsBeanDefinition(ConfigurableListableBeanFactory beanFactory, String... beans) { return Arrays.stream(beans).allMatch(b -> beanFactory.containsBeanDefinition(b)); } } 好了,这里笔者就不测试了;我是张星,欢迎加入博主技术交流群,群号:526601468

作者:eagle-zhang 来源:CSDN 原文:https://blog.csdn.net/zhangxing52077/article/details/81587431 版权声明:本文为博主原创文章,转载请附上博文链接!

完整阅读
favorite_border 0
reply 0
公开分享 2019-03-04

我是带带大俊龙 我是android天才 学android找我

完整阅读
favorite_border 0
reply 0
公开分享 2019-02-28
重拾后端之Spring Boot(四):使用JWT和Spring Security保护REST API

通常情况下,把API直接暴露出去是风险很大的,不说别的,直接被机器攻击就喝一壶的。那么一般来说,对API要划分出一定的权限级别,然后做一个用户的鉴权,依据鉴权结果给予用户开放对应的API。目前,比较主流的方案有几种:

用户名和密码鉴权,使用Session保存用户鉴权结果。 使用OAuth进行鉴权(其实OAuth也是一种基于Token的鉴权,只是没有规定Token的生成方式) 自行采用Token进行鉴权

第一种就不介绍了,由于依赖Session来维护状态,也不太适合移动时代,新的项目就不要采用了。第二种OAuth的方案和JWT都是基于Token的,但OAuth其实对于不做开放平台的公司有些过于复杂。我们主要介绍第三种:JWT。 什么是JWT? JWT是 Json Web Token 的缩写。它是基于 RFC 7519 标准定义的一种可以安全传输的 小巧 和 自包含 的JSON对象。由于数据是使用数字签名的,所以是可信任的和安全的。JWT可以使用HMAC算法对secret进行加密或者使用RSA的公钥私钥对来进行签名。 JWT的工作流程 下面是一个JWT的工作流程图。模拟一下实际的流程是这样的(假设受保护的API在/protected中)

用户导航到登录页,输入用户名、密码,进行登录 服务器验证登录鉴权,如果改用户合法,根据用户的信息和服务器的规则生成JWT Token 服务器将该token以json形式返回(不一定要json形式,这里说的是一种常见的做法) 用户得到token,存在localStorage、cookie或其它数据存储形式中。 以后用户请求/protected中的API时,在请求的header中加入 Authorization: Bearer xxxx(token)。此处注意token之前有一个7字符长度的 Bearer 服务器端对此token进行检验,如果合法就解析其中内容,根据其拥有的权限和自己的业务逻辑给出对应的响应结果。 用户取得结果

JWT工作流程图 为了更好的理解这个token是什么,我们先来看一个token生成后的样子,下面那坨乱糟糟的就是了。 eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ3YW5nIiwiY3JlYXRlZCI6MTQ4OTA3OTk4MTM5MywiZXhwIjoxNDg5Njg0NzgxfQ.RC-BYCe_UZ2URtWddUpWXIp4NMsoeq2O6UF-8tVplqXY1-CI9u1-a-9DAAJGfNWkHE81mpnR3gXzfrBAB3WUAg复制代码但仔细看到的话还是可以看到这个token分成了三部分,每部分用 . 分隔,每段都是用 Base64 编码的。如果我们用一个Base64的解码器的话 ( www.base64decode.org/ ),可以看到第一部分 eyJhbGciOiJIUzUxMiJ9 被解析成了: { "alg":"HS512" }复制代码这是告诉我们HMAC采用HS512算法对JWT进行的签名。 第二部分 eyJzdWIiOiJ3YW5nIiwiY3JlYXRlZCI6MTQ4OTA3OTk4MTM5MywiZXhwIjoxNDg5Njg0NzgxfQ 被解码之后是 { "sub":"wang", "created":1489079981393, "exp":1489684781 }复制代码这段告诉我们这个Token中含有的数据声明(Claim),这个例子里面有三个声明:sub, created 和 exp。在我们这个例子中,分别代表着用户名、创建时间和过期时间,当然你可以把任意数据声明在这里。 看到这里,你可能会想这是个什么鬼token,所有信息都透明啊,安全怎么保障?别急,我们看看token的第三段 RC-BYCe_UZ2URtWddUpWXIp4NMsoeq2O6UF-8tVplqXY1-CI9u1-a-9DAAJGfNWkHE81mpnR3gXzfrBAB3WUAg。同样使用Base64解码之后,咦,这是什么东东 D X DmYTeȧLUZcPZ0$gZAY_7wY@复制代码最后一段其实是签名,这个签名必须知道秘钥才能计算。这个也是JWT的安全保障。这里提一点注意事项,由于数据声明(Claim)是公开的,千万不要把密码等敏感字段放进去,否则就等于是公开给别人了。 也就是说JWT是由三段组成的,按官方的叫法分别是header(头)、payload(负载)和signature(签名): header.payload.signature复制代码头中的数据通常包含两部分:一个是我们刚刚看到的 alg,这个词是 algorithm 的缩写,就是指明算法。另一个可以添加的字段是token的类型(按RFC 7519实现的token机制不只JWT一种),但如果我们采用的是JWT的话,指定这个就多余了。 { "alg": "HS512", "typ": "JWT" }复制代码payload中可以放置三类数据:系统保留的、公共的和私有的:

系统保留的声明(Reserved claims):这类声明不是必须的,但是是建议使用的,包括:iss (签发者), exp (过期时间),sub (主题), aud (目标受众)等。这里我们发现都用的缩写的三个字符,这是由于JWT的目标就是尽可能小巧。 公共声明:这类声明需要在 IANA JSON Web Token Registry 中定义或者提供一个URI,因为要避免重名等冲突。 私有声明:这个就是你根据业务需要自己定义的数据了。

签名的过程是这样的:采用header中声明的算法,接受三个参数:base64编码的header、base64编码的payload和秘钥(secret)进行运算。签名这一部分如果你愿意的话,可以采用RSASHA256的方式进行公钥、私钥对的方式进行,如果安全性要求的高的话。 HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)复制代码JWT的生成和解析 为了简化我们的工作,这里引入一个比较成熟的JWT类库,叫 jjwt ( github.com/jwtk/jjwt )。这个类库可以用于Java和Android的JWT token的生成和验证。 JWT的生成可以使用下面这样的代码完成: String generateToken(Map<String, Object> claims) { return Jwts.builder() .setClaims(claims) .setExpiration(generateExpirationDate()) .signWith(SignatureAlgorithm.HS512, secret) //采用什么算法是可以自己选择的,不一定非要采用HS512 .compact(); }复制代码数据声明(Claim)其实就是一个Map,比如我们想放入用户名,可以简单的创建一个Map然后put进去就可以了。 Map<String, Object> claims = new HashMap<>(); claims.put(CLAIM_KEY_USERNAME, username());复制代码解析也很简单,利用 jjwt 提供的parser传入秘钥,然后就可以解析token了。 Claims getClaimsFromToken(String token) { Claims claims; try { claims = Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } catch (Exception e) { claims = null; } return claims; }复制代码JWT本身没啥难度,但安全整体是一个比较复杂的事情,JWT只不过提供了一种基于token的请求验证机制。但我们的用户权限,对于API的权限划分、资源的权限划分,用户的验证等等都不是JWT负责的。也就是说,请求验证后,你是否有权限看对应的内容是由你的用户角色决定的。所以我们这里要利用Spring的一个子项目Spring Security来简化我们的工作。 Spring Security Spring Security是一个基于Spring的通用安全框架,里面内容太多了,本文的主要目的也不是展开讲这个框架,而是如何利用Spring Security和JWT一起来完成API保护。所以关于Spring Secruity的基础内容或展开内容,请自行去官网学习( projects.spring.io/spring-secu… )。 简单的背景知识 如果你的系统有用户的概念的话,一般来说,你应该有一个用户表,最简单的用户表,应该有三列:Id,Username和Password,类似下表这种

ID USERNAME PASSWORD

10 wang abcdefg

而且不是所有用户都是一种角色,比如网站管理员、供应商、财务等等,这些角色和网站的直接用户需要的权限可能是不一样的。那么我们就需要一个角色表:

ID ROLE

10 USER

20 ADMIN

当然我们还需要一个可以将用户和角色关联起来建立映射关系的表。

USER_ID ROLE_ID

10 10

20 20

这是典型的一个关系型数据库的用户角色的设计,由于我们要使用的MongoDB是一个文档型数据库,所以让我们重新审视一下这个结构。 这个数据结构的优点在于它避免了数据的冗余,每个表负责自己的数据,通过关联表进行关系的描述,同时也保证的数据的完整性:比如当你修改角色名称后,没有脏数据的产生。 但是这种事情在用户权限这个领域发生的频率到底有多少呢?有多少人每天不停的改的角色名称?当然如果你的业务场景确实是需要保证数据完整性,你还是应该使用关系型数据库。但如果没有高频的对于角色表的改动,其实我们是不需要这样的一个设计的。在MongoDB中我们可以将其简化为 { _id: <id_generated> username: 'user', password: 'pass', roles: ['USER', 'ADMIN'] }复制代码基于以上考虑,我们重构一下 User 类, @Data public class User { @Id private String id;

@Indexed(unique=true, direction= IndexDirection.DESCENDING, dropDups=true) private String username; private String password; private String email; private Date lastPasswordResetDate; private List<String> roles;

}复制代码当然你可能发现这个类有点怪,只有一些field,这个简化的能力是一个叫lombok类库提供的 ,这个很多开发过Android的童鞋应该熟悉,是用来简化POJO的创建的一个类库。简单说一下,采用 lombok 提供的 @Data 修饰符后可以简写成,原来的一坨getter和setter以及constructor等都不需要写了。类似的 Todo 可以改写成: @Data public class Todo { @Id private String id; private String desc; private boolean completed; private User user; }复制代码增加这个类库只需在 build.gradle 中增加下面这行 dependencies { // 省略其它依赖 compile("org.projectlombok:lombok:${lombokVersion}") }复制代码引入Spring Security 要在Spring Boot中引入Spring Security非常简单,修改 build.gradle,增加一个引用 org.springframework.boot:spring-boot-starter-security: dependencies { compile("org.springframework.boot:spring-boot-starter-data-rest") compile("org.springframework.boot:spring-boot-starter-data-mongodb") compile("org.springframework.boot:spring-boot-starter-security") compile("io.jsonwebtoken:jjwt:${jjwtVersion}") compile("org.projectlombok:lombok:${lombokVersion}") testCompile("org.springframework.boot:spring-boot-starter-test") }复制代码你可能发现了,我们不只增加了对Spring Security的编译依赖,还增加 jjwt 的依赖。 Spring Security需要我们实现几个东西,第一个是UserDetails:这个接口中规定了用户的几个必须要有的方法,所以我们创建一个JwtUser类来实现这个接口。为什么不直接使用User类?因为这个UserDetails完全是为了安全服务的,它和我们的领域类可能有部分属性重叠,但很多的接口其实是安全定制的,所以最好新建一个类: public class JwtUser implements UserDetails { private final String id; private final String username; private final String password; private final String email; private final Collection<? extends GrantedAuthority> authorities; private final Date lastPasswordResetDate;

public JwtUser( String id, String username, String password, String email, Collection<? extends GrantedAuthority> authorities, Date lastPasswordResetDate) { this.id = id; this.username = username; this.password = password; this.email = email; this.authorities = authorities; this.lastPasswordResetDate = lastPasswordResetDate; } //返回分配给用户的角色列表 @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @JsonIgnore public String getId() { return id; } @JsonIgnore @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } // 账户是否未过期 @JsonIgnore @Override public boolean isAccountNonExpired() { return true; } // 账户是否未锁定 @JsonIgnore @Override public boolean isAccountNonLocked() { return true; } // 密码是否未过期 @JsonIgnore @Override public boolean isCredentialsNonExpired() { return true; } // 账户是否激活 @JsonIgnore @Override public boolean isEnabled() { return true; } // 这个是自定义的,返回上次密码重置日期 @JsonIgnore public Date getLastPasswordResetDate() { return lastPasswordResetDate; }

}复制代码这个接口中规定的很多方法我们都简单粗暴的设成直接返回某个值了,这是为了简单起见,你在实际开发环境中还是要根据具体业务调整。当然由于两个类还是有一定关系的,为了写起来简单,我们写一个工厂类来由领域对象创建 JwtUser,这个工厂就叫 JwtUserFactory 吧: public final class JwtUserFactory {

private JwtUserFactory() { } public static JwtUser create(User user) { return new JwtUser( user.getId(), user.getUsername(), user.getPassword(), user.getEmail(), mapToGrantedAuthorities(user.getRoles()), user.getLastPasswordResetDate() ); } private static List<GrantedAuthority> mapToGrantedAuthorities(List<String> authorities) { return authorities.stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); }

}

完整阅读
favorite_border 0
reply 0
公开分享 2019-03-03
MyBatis学习笔记(一)入门

首先给大家推荐几个网页:

http://www.mybatis.cn/

http://blog.csdn.net/isea533/article/category/2092001

http://www.mybatis.org/mybatis-3/zh/index.html

http://www.mybatis.org/spring/zh/index.html

http://www.mybatis.tk/

这些也是我朋友介绍的。刚刚接触MyBatis可以看看。接下来就开始学习mybatis了。

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

一、理解什么是MyBatis? MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。 MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及对结果集的检索。 MyBatis 可以使用简单的XML 或注解用于配置和原始映射,将接口和 Java 的 POJO( Plain Old Java Objects,普通的Java 对象)映射成数据库中的记录.

1)MyBATIS 目前提供了三种语言实现的版本,包括:Java、.NET以及Ruby。(我主要学习java,就讲java的使用) 2)它提供的持久层框架包括SQL Maps和Data Access Objects(DAO)。 3)mybatis与hibernate的对比?

mybatis提供一种“半自动化”的ORM实现。 这里的“半自动化”,是相对Hibernate等提供了全面的数据库封装机制的“全自动化”ORM实现而言,“全自动”ORM实现了POJO和数据库表之间的映射,以及 SQL 的自动生成和执行。

而mybatis的着力点,则在于POJO与SQL之间的映射关系。 二、简单例子(快速入门) 1)首先建立项目java web

2)导入mybatis所需的jar包

mybatis需要jar包:mybatis-3.3.0.jar

mysql驱动jar包:mysql-connector-java-5.1.15.-bin.jar

日志记录jar包:log4j.jar

3)创建数据库数据(mysql)

4)mysql驱动配置文件(这样可以优化性能)

我个人喜欢把所需要使用到的包先建立好。配置文件先创建好。

5)添加mybatis配置文件mybatis.cfg.xml

复制代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 复制代码

6)创建对应的实体对象

对应的java代码:

View Code

7)创建方法接口UserMapper.java和定义操作t_user表的sql映射文件UserMapper.xml

提供简单的增删改查数据信息。

复制代码 1 package com.cy.mybatis.mapper; 2 3 import java.util.List; 4 5 import com.cy.mybatis.beans.UserBean; 6 7 public interface UserMapper { 8 / 9 新增用戶 10 @param user 11 @return 12 @throws Exception 13 / 14 public int insertUser(UserBean user) throws Exception; 15 / 16 修改用戶 17 @param user 18 @param id 19 @return 20 @throws Exception 21 / 22 public int updateUser (UserBean user,int id) throws Exception; 23 / 24 刪除用戶 25 @param id 26 @return 27 @throws Exception 28 / 29 public int deleteUser(int id) throws Exception; 30 / 31 根据id查询用户信息 32 @param id 33 @return 34 @throws Exception 35 / 36 public UserBean selectUserById(int id) throws Exception; 37 / 38 查询所有的用户信息 39 @return 40 @throws Exception 41 */ 42 public List selectAllUser() throws Exception; 43 } 复制代码

UserMapper.xml

复制代码 1 2 3 4 5 6 7 8 9 10 11 12 14 15 16 17 18 19 insert into t_user (username,password,account) values (#{username},#{password},#{account}) 20 21 22 23 update t_user set username=#{username},password=#{password},account=#{account} where id=#{id} 24 25 26 27 delete from t_user where id=#{id} 28 29 30 31 select from t_user where id=#{id} 32 33 34 35 select from t_user 36 37 38 39 复制代码

这时需要为mybatis.cfg.xml里注册UserMapper.xml文件。

复制代码 1 2 3 4 5 6 7 8 9 10

完整阅读
favorite 1
reply 0
公开分享 2019-02-28
MyBatis学习笔记(一)入门

首先给大家推荐几个网页:

http://www.mybatis.cn/

http://blog.csdn.net/isea533/article/category/2092001

http://www.mybatis.org/mybatis-3/zh/index.html

http://www.mybatis.org/spring/zh/index.html

http://www.mybatis.tk/

这些也是我朋友介绍的。刚刚接触MyBatis可以看看。接下来就开始学习mybatis了。

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

一、理解什么是MyBatis? MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。 MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及对结果集的检索。 MyBatis 可以使用简单的XML 或注解用于配置和原始映射,将接口和 Java 的 POJO( Plain Old Java Objects,普通的Java 对象)映射成数据库中的记录.

1)MyBATIS 目前提供了三种语言实现的版本,包括:Java、.NET以及Ruby。(我主要学习java,就讲java的使用) 2)它提供的持久层框架包括SQL Maps和Data Access Objects(DAO)。 3)mybatis与hibernate的对比?

mybatis提供一种“半自动化”的ORM实现。 这里的“半自动化”,是相对Hibernate等提供了全面的数据库封装机制的“全自动化”ORM实现而言,“全自动”ORM实现了POJO和数据库表之间的映射,以及 SQL 的自动生成和执行。

而mybatis的着力点,则在于POJO与SQL之间的映射关系。

二、简单例子(快速入门) 1)首先建立项目java web

2)导入mybatis所需的jar包

mybatis需要jar包:mybatis-3.3.0.jar

mysql驱动jar包:mysql-connector-java-5.1.15.-bin.jar

日志记录jar包:log4j.jar

3)创建数据库数据(mysql)

4)mysql驱动配置文件(这样可以优化性能)

我个人喜欢把所需要使用到的包先建立好。配置文件先创建好。

5)添加mybatis配置文件mybatis.cfg.xml

复制代码 1 2 3 4 5 6 7
8 9 10 11 12 13
14 15 16 17 18 19 20 21 22 23 24 25 26
27 复制代码

6)创建对应的实体对象

对应的java代码:

View Code

7)创建方法接口UserMapper.java和定义操作t_user表的sql映射文件UserMapper.xml

提供简单的增删改查数据信息。

复制代码 1 package com.cy.mybatis.mapper; 2 3 import java.util.List; 4 5 import com.cy.mybatis.beans.UserBean; 6 7 public interface UserMapper { 8 /** 9 * 新增用戶 10 * @param user 11 * @return 12 * @throws Exception 13 / 14 public int insertUser(UserBean user) throws Exception; 15 /* 16 * 修改用戶 17 * @param user 18 * @param id 19 * @return 20 * @throws Exception 21 / 22 public int updateUser (UserBean user,int id) throws Exception; 23 /* 24 * 刪除用戶 25 * @param id 26 * @return 27 * @throws Exception 28 / 29 public int deleteUser(int id) throws Exception; 30 /* 31 * 根据id查询用户信息 32 * @param id 33 * @return 34 * @throws Exception 35 / 36 public UserBean selectUserById(int id) throws Exception; 37 /* 38 * 查询所有的用户信息 39 * @return 40 * @throws Exception 41 */ 42 public List selectAllUser() throws Exception; 43 } 复制代码

UserMapper.xml

复制代码 1 2 3 4 5 6 7 8 9 10 11
12
14 15 16 17 18 19 insert into t_user (username,password,account) values (#{username},#{password},#{account}) 20 21
22 23 update t_user set username=#{username},password=#{password},account=#{account} where id=#{id} 24 25
26 27 delete from t_user where id=#{id}
28 29
30 31 select * from t_user where id=#{id} 32 33
34 35 select * from t_user 36 37
38
39
复制代码

这时需要为mybatis.cfg.xml里注册UserMapper.xml文件。

复制代码 1 2 3 4 5 6 7
8
9 10

完整阅读
favorite_border 0
reply 0
公开分享 2019-02-28
重拾后端之Spring Boot(一):REST API的搭建可以这样简单

话说我当年接触Spring的时候着实兴奋了好一阵,IoC的概念当初第一次听说,感觉有种开天眼的感觉。记得当时的web框架和如今的前端框架的局面差不多啊,都是群雄纷争。但一晃好多年没写过后端,代码这东西最怕手生,所以当作重新学习了,顺便写个学习笔记。 Spring Boot是什么? 还恍惚记得当初写Spring的时候要配置好多xml(在当时还是相对先进的模式),虽然实现了松耦合,但这些xml却又成为了项目甩不掉的负担 -- 随着项目越做越大,这些xml的可读性和可维护性极差。后来受.Net平台中Annotation的启发,Java世界中也引入了元数据的修饰符,Spring也可以使用这种方式进行配置。到了近些年,随着Ruby on Rails的兴起而流行开的 Convention over configuration 理念开始深入人心。那什么是 Convention over configuration 呢?简单来说就是牺牲一部分的自由度来减少配置的复杂度,打个比方就是如果你如果遵从我定义的一系列规则(打个比方,文件目录结构必须是blablabla的样子,文件命名必须是nahnahnah 的样子),那么你要配置的东西就非常简单甚至可以零配置。既然已经做到这个地步了,各种脚手架项目就纷纷涌现了,目的只有一个:让你更专注在代码的编写,而不是浪费在各种配置上。这两年前端也有类似趋势,各种前端框架的官方CLI纷纷登场:create-react-app,angular-cli,vue-cli等等。 那么Spring Boot就是Spring框架的脚手架了,它可以帮你快速搭建、发布一个Spring应用。官网列出了Spring Boot的几个主要目标

提供一种快速和广泛适用的Spring开发体验 开箱即用却又可以适应各种变化 提供一系列开发中常用的“非功能性”的特性(比如嵌入式服务器、安全、度量、自检及外部配置等) 不生成任何代码,不需要xml配置

安装Spring Boot 官方推荐的方式是通过sdkman( sdkman.io/install.htm… )来进行安装,当然这是对 *nix 而言。题外话,如果你使用的是Windows 10,真心希望大家安装Windows 10的Linux子系统,微软官方出品、原生支持,比虚拟机不知道快到那里去了 具体安装过程可以参考 linux.cn/article-720… 。安装 sdkman 的步骤非常简单,就两步:

打开一个terminal,输入 curl -s "https://get.sdkman.io" | bash 安装结束后,重启terminal,输入 source "$HOME/.sdkman/bin/sdkman-init.sh"

可以在terminal中验证一下是否安装成功 sdk version,如果你看到了版本号就是安装好了。 接下来,就可以安装Spring Boot了,还是打开terminal输入 sdk install springboot就ok了。 当然其实Mac的童鞋可以省略掉之前的sdkman安装直接使用 brew 安装,也是两步:

在terminal中输入 brew tap pivotal/tap 然后 brew install springboot

验证的话可以输入 spring --version 看看是否正常输出了版本号。 创建一个工程 有很多种方法可以创建一个Spring Boot项目,其中最简单的一种是通过一个叫Spring Initializr的在线工具 http://start.spring.io/ 进行工程的生成。如下图所示,只需填写一些参数就可以生成一个工程包了。 使用Spring Initializr进行工程的生成 如果你使用Intellij IDEA进行开发,里面也集成了这个工具,大家可以自行尝试。 Intellij IDEA中集成了 Spring Initializr 但下面我们要做的不是通过这种方式,而是手动的通过命令行方式创建。创建的是gradle工程,而不是maven的,原因呢是因为个人现在对于xml类型的配置文件比较无感;-),官方推荐使用gradle 2.14.1版本,请自行安装gradle。下面来建立一个gradle工程,其实步骤也不算太难:

新建一个工程目录 mkdir todo 在此目录下使用gradle进行初始化 gradle init(就和在node中使用 npm init 的效果类似)

这个命令帮我们建立一个一个使用gradle进行管理的模版工程:

build.gradle:有过Android开发经验的童鞋可能觉得很亲切的,这个就是我们用于管理和配置工程的核心文件了。 gradlew:用于 *nix 环境下的gradle wrapper文件。 gradlew.bat:用于 Windows 环境下的gradle wrapper文件 setting.gradle:用于管理多项目的gradle工程时使用,单项目时可以不做理会。 gradle目录:wrapper的jar和属性设置文件所在的文件夹。

简单说两句什么是 gradle wrapper。你是否有过这样的经历?在安装/编译一个工程时需要一些先决条件,需要安装一些软件或设置一些参数。如果这一切比较顺利还好,但很多时候我们会发现这样那样的问题,比如版本不对,参数没设置等等。gradle wrapper 就是这样一个让你不会浪费时间在配置问题上的方案。它会对应一个开发中使用的gradle版本,以确保任何人任何时候得到的结果是一致的。

./gradlew : 在 *nix 平台上运行,例如Linux或Mac OS X gradlew 在Windows平台运行(是通过gradlew.bat来执行的)

更多关于wrapper的知识可以去 docs.gradle.org/current/use… 查看。 那么下面我们打开默认生成的 build.gradle 文件,将其改造成下面的样子: /*

  • 这个build文件是由Gradle的 init 任务生成的。
  • 更多关于在Gradle中构建Java项目的信息可以查看Gradle用户文档中的
  • Java项目快速启动章节
  • https://docs.gradle.org/3.3/userguide/tutorial_java_projects.html / // 在这个段落中你可以声明你的build脚本需要的依赖和解析下载该依赖所使用的仓储位置 buildscript { ext { springBootVersion = '1.4.3.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } /
  • 在这个段落中你可以声明使用哪些插件
  • apply plugin: 'java' 代表这是一个Java项目,需要使用java插件
  • 如果想生成一个 Intellij IDEA 的工程,类似的如果要生成
  • eclipse工程,就写 apply plugin: 'eclipse'
  • 同样的我们要学的是Spring Boot,所以应用Spring Boot插件 */ apply plugin: 'java' apply plugin: 'idea' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot'

// 在这个段落中你可以声明编译后的Jar文件信息 jar { baseName = 'todo' version = '0.0.1-SNAPSHOT' }

// 在这个段落中你可以声明在哪里可以找到你的项目依赖 repositories { // 使用 'jcenter' 作为中心仓储查询解析你的项目依赖。 // 你可以声明任何 Maven/Ivy/file 类型的依赖类库仓储位置 mavenCentral() }

// 在这个段落中你可以声明源文件和目标编译后的Java版本兼容性 sourceCompatibility = 1.8 targetCompatibility = 1.8

// 在这个段落你可以声明你的项目的开发和测试所需的依赖类库 dependencies { compile('org.springframework.boot:spring-boot-starter-web') testCompile('org.springframework.boot:spring-boot-starter-test') }复制代码首先脚本依赖中的 spring-boot-gradle-plugin 有什么作用呢?它提供了以下几个功能:

简化执行和发布:它可以把所有classpath的类库构建成一个单独的可执行jar文件,这样可以简化你的执行和发布等操作。 自动搜索入口文件:它会扫描 public static void main() 函数并且标记这个函数的宿主类为可执行入口。 简化依赖:一个典型的Spring应用还是需要很多依赖类库的,想要配置正确这些依赖挺麻烦的,所以这个插件提供了内建的依赖解析器会自动匹配和当前Spring Boot版本匹配的依赖库版本。

在最后一个段落中,我们看到我们的项目依赖两个类库,一个是 spring-boot-starter-web ,另一个是 spring-boot-starter-test。Spring Boot提供了一系列依赖类库的“模版”,这些“模版”封装了很多依赖类库,可以让我们非常方便的引用自己想实现的功能所需要的类库。如果我们去看看这个 spring-boot-starter-web 中究竟引用了什么,我们可以看看它的artifact文件(到 search.maven.org/ 可以查看):

4.0.0 org.springframework.boot spring-boot-starters 1.4.3.RELEASE spring-boot-starter-web Spring Boot Web Starter Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container http://projects.spring.io/spring-boot/ Pivotal Software, Inc. http://www.spring.io ${basedir}/../.. org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-tomcat org.hibernate hibernate-validator com.fasterxml.jackson.core jackson-databind org.springframework spring-web org.springframework spring-webmvc 复制代码IDE支持 一般做Java开发,大部分团队还是喜欢用一个IDE,虽然我还是更偏爱文本编辑器类型的(比如sublime,vscode,atom等)。但是如果非挑一个重型IDE的话,我更喜欢Intellij IDEA。 使用IDEA的import project功能选中 build.gradle,将工程导入。由于是个gradle工程,请把 View->Tools Window->Gradle 的视图窗口调出来。 Gradle工具窗口 点击左上角的刷新按钮可以将所有依赖下载类库下来。注意IDEA有时提示是否要配置wrapper使用带源码的gradle包。 提示使用带源码的gradle以便有API的文档 如果遇到不知道什么原因导致一直刷新完成不了的情况,请在项目属性中选择 Use local gradle distribution image_1b77vqast1d2h1qioeepsdm1tkb9.png-192.5kB 第一个Web API 领域对象 那么我们的源代码目录在哪里呢?我们得手动建立一个,这个目录一般情况下是 src/main/java。好的,下面我们要开始第一个RESTful的API搭建了,首先还是在 src/main/java 下新建一个 package。既然是本机的就叫 dev.local 吧。我们还是来尝试建立一个 Todo 的Web API,在 dev.local 下建立一个子 package: todo,然后创建一个Todo的领域对象: package dev.local.todo;

/**

  • Todo是一个领域对象(domain object)

  • Created by wangpeng on 2017/1/24. */ public class Todo { private String id; private String desc; private boolean completed;

    public String getId() { return id; }

    public void setId(String id) { this.id = id; }

    public String getDesc() { return desc; }

    public void setDesc(String desc) { this.desc = desc; }

    public boolean isCompleted() { return completed; }

    public void setCompleted(boolean completed) { this.completed = completed; } }复制代码这个对象很简单,只是描述了todo的几个属性: id 、 desc 和 completed 。我们的API返回或接受的参数就是以这个对象为模型的类或集合。 构造Controller 我们经常看到的RESTful API是这样的:http://local.dev/todos、http://local.dev/todos/1 。Controller就是要暴露这样的API给外部使用。现在我们同样的在 todo 下建立一个叫 TodoController 的java文件 package dev.local.todo;

import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList; import java.util.List;

/**

  • 使用@RestController来标记这个类是个Controller */ @RestController public class TodoController { // 使用@RequstMapping指定可以访问的URL路径 @RequestMapping("/todos") public List getAllTodos() { List todos = new ArrayList<>(); Todo item1 = new Todo(); item1.setId("1"); item1.setCompleted(false); item1.setDesc("go swimming"); todos.add(item1); Todo item2 = new Todo(); item2.setId("1"); item2.setCompleted(true); item2.setDesc("go for lunch"); todos.add(item2); return todos; } }复制代码上面这个文件也比较简单,但注意到以下几个事情:

@RestController 和 @RequestMapping 这两个是元数据注释,原来在.Net中很常见,后来Java也引进过来。一方面它们可以增加代码的可读性,另一方面也有效减少了代码的编写。具体机理就不讲了,简单来说就是利用Java的反射机制和IoC模式结合把注释的特性或属性注入到被注释的对象中。 我们看到 List getAllTodos() 方法中简单的返回了一个List,并未做任何转换成json对象的处理,这个是Spring会自动利用 Jackson 这个类库的方法将其转换成了json。

完整阅读
favorite_border 0
reply 0
公开分享 2019-02-28
IPython使用学习笔记

学习《利用python进行数据分析》第三章 IPython:一种交互式计算和开发环境的笔记,共享给大家,同时为自己作为备忘用。

安装ipython用pip即可。ps.博主用的是win7系统,所以接下来的都是在windows系统下操作的。

一.Ipython基础

启动:开始菜单-输入cmd-回车-输入ipython

初尝试

二.Tab键自动完成

在shell中输入表达式时,只要按下Tab键,当前命令控件中任何与输入的字符串相匹配的变量(对象、函数等)就会被找出来。

ps.之前我发现我的ipython没有Tab键自动补全功能,tab键的功能是缩进。最后 pip install pyreadline即可解决。

下面这个例子,输入b.后按下 键即可得到下图的结果。

三.内省

在变量的前面或后面加上一个问号(?)就可以将有关该对象的一些通用信息显示出来。这就叫做对象的内省。

如果对象是一个函数或实例方法,则其docstring也会被现实出来。

使用??还将显示出该函数的源代码。

一些字符串配以通配符(*)即可显示出所有与该通配符表达式相匹配的名称。

例如,我们可以列出Numpy顶级命名空间中含有“load”的所有函数

简直太棒了有木有!!!!!!!

四.%run命令

在ipython会话环境中,所有文件都可以通过%run命令当做Python程序来运行。

输入 %run 路径+python文件名称即可。

ps.《集体智慧编程》里面代码的执行可以用这个

五.中断正在执行的代码(急性子必备23333)

按下Ctrl-C就会引发一个KeyboardInterrupt。除了一些非常特殊的情况下,绝大部分python程序都会立即停止执行

六.执行剪贴板中的代码

书上说使用ctrl+shift+v将剪贴板的代码片段粘贴出来,然而windows系统似乎不可行,所以鼠标右键粘贴好了。

%paste可以承载剪贴板中的一切文本,并在shell中以整体形式执行。

%cpaste跟%paste差不多,只不过它多出了一个用于粘贴代码的特殊提示符而已。如果发现粘贴的代码有错,只需要按下ctrl+c即可终止%cpaste提示如。

七.键盘快捷键

其实不到真正使用我也记不住23333,在此粘贴给大家作为备忘。

Ctrl-P 或上箭头键 后向搜索命令历史中以当前输入的文本开头的命令 Ctrl-N 或下箭头键 前向搜索命令历史中以当前输入的文本开头的命令 Ctrl-R 按行读取的反向历史搜索(部分匹配) Ctrl-Shift-v 从剪贴板粘贴文本 Ctrl-C 中止当前正在执行的代码 Ctrl-A 将光标移动到行首 Ctrl-E 将光标移动到行尾 Ctrl-K 删除从光标开始至行尾的文本 Ctrl-U 清除当前行的所有文本译注12 Ctrl-F 将光标向前移动一个字符 Ctrl-b 将光标向后移动一个字符 Ctrl-L 清屏 八.异常和跟踪

九.魔术命令

粘贴在此以作备忘

命令 说明 %quickref 显示IPython的快速参考 %magic 显示所有魔术命令的详细文档 %debug 从最新的异常跟踪的底部进入交互式调试器 %hist 打印命令的输入(可选输出)历史 %pdb 在异常发生后自动进入调试器 %paste 执行剪贴板中的Python代码 %cpaste 打开一个特殊提示符以便手工粘贴待执行的Python代码 %reset 删除interactive命名空间中的全部变量/名称 %page OBJECT 通过分页器打印输出OBJECT %run script.py 在IPython中执行一个Python脚本文件 %prun statement 通过cProfile执行statement,并打印分析器的输出结果 %time statement 报告statement的执行时间 %timeit statement 多次执行statement以计算系综平均执行时间。对那些执行时 间非常小的代码很有用 %who、%who_ls、%whos 显示interactive命名空间中定义的变量,信息级别/冗余度可变 %xdel variable 删除variable,并尝试清除其在IPython中的对象上的一切引用

十.基于Qt的富GUI控制台

Ipython团队开发了一个基于Qt框架(其目的是为终端应用程序提供诸如内嵌图片、多行编辑、语法高亮之类的富文本剪辑功能)的GUI控制台。

使用

ipython qtconsole --pylab=inline 来启动的话可为其添加绘图功能。

ps.这一步刚开始无法执行,我的解决方法是:

pip install qtconsole,就可以运行了- -..

十一.matplotlib集成与pylab模式

通常我们通过在启动Ipython时加上--pylab标记来集成matplotlib

注意空格啊~是

ipython --pylab

十二.使用历史命令

十三.搜索并重用历史命令

历史命令用上下箭头就好啦,ctrl+p和ctrl+n 太麻烦啦。

ctrl+r用于实现部分增量搜索,按下ctrl+r并输入你想搜索的行中的几个字符。按下ctrl+r将会循环搜索历史命令中每一条与输入相符的行。

十四.输入和输出变量

ipython将最近的两个输出结果保存在_(一个下划线)和__(两个下划线)变量中

输入的文本被保存在名为_iX的变量中,其中X是输入行的行号。比如说,在输入完27行后,就会产生两个新变量_27(输出变量)和_i27(输入变量)

我这儿就拿第八行举例子吧2333

十五.记录输入和输出

执行%logstart既可开始记录日志

十六.与操作系统交互

十七.shell命令和别名(这儿我好多代码执行不出来,待解决。)

完整阅读
favorite_border 0
reply 0
公开分享 2019-02-28
Spring cloud多模块开发下Feign的使用,以及@FeignClient注入bean找不到异常解决

一、关于Feign 在微服务架构开发是,我们常常会在一个项目中调用其他服务,其实使用Spring Cloud Rbbon就能实现这个需求,利用RestTemplate 的请求拦截来实现对依赖服务的接口调用, 但是实际项目中对服务依赖的调用可能不止于 一 处,往往 一 个接口会被多处调用,所以我们通常都会针对各个微服务自行封装 一 些客户端类来包装这些依赖服务的调用。 这个时候我们会发现,由于 RestTemplate 的封装,几乎每 一 个调用都是简单的模板化内容。

Spring Cloud Feign 在此基础上做了进 一 步封装,由它来帮助我们定义和实现依赖服务接口的定义。在 Spring Cloud Feign 的实现下, 我们只需创建 一 个接口并用注解(@FeignClient)的方式来配置它, 即可完成对服务提供方的接口绑定,简化了在使用 Spring Cloud Ribbon 时自行封装服务调用客户端的开发量。 

 

二、多模块方式构建一个Feign项目 1、准备,

启动一个Eureka服务注册中心,后面的两个服务都会启动注册到这个上面。

2、编写第一服务--商品服务

(1). 创建一个父模块

  File-->New-->Project-->Maven

  不需要任何勾选,直接填写GAV,然后填写项目名就可以了

  因为这是一个父模块,对于创建出来的Maven项目,可以直接删除src文件

 

(2). 创建子模块common

  在父模块上右键New-->Module,创建一个模块,该模块即为子模块;

  同样不选择Create from archetype选项,因为是普通模块,Next;

  GroupId       默认父项目的groupId

  Version       默认父项目的version

  ArtifactId    本模块的名字product-common

  然后填写项目名即可common

 

(3). 同理创建子模块client

  在父模块上右键New-->Module,创建一个子模块;

  同样不选择Create from archetype选项,因为是普通模块,Next;

  GroupId       默认父项目的groupId

  Version       默认父项目的version

  ArtifactId    本模块的名字product-client

  然后填写项目名即可client

 

(4). 同理创建子模块server

  在父模块上右键New-->Module,创建一个子模块;

  同样不选择Create from archetype选项,因为是普通模块,Next;

  GroupId       默认父项目的groupId

  Version       默认父项目的version

  ArtifactId    本模块的名字product-server

  然后填写项目名即可server

 

(5). 在server模块下编写一个服务接口

  例如提供一个/product/ listForOrder,这个服务会在下面的订单类中调用

@RestController @RequestMapping("/product") public class ProductController { @Autowired private ProductService productService;

@PostMapping("/listForOrder") public List<ProductInfoOutput> listForOrder(@RequestBody List<String> productIdList){

    return productService.findList(productIdList);

}

} (6). 在client模块下创建一个接口类

在这个接口类上加注解@FeignClient(name = "product"),其中product是配置的服务在注册中心上的名字

import com.yore.product.common.ProductInfoOutput; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody;

import java.util.List;

@FeignClient(name = "product") public interface ProductClient {

    @PostMapping("/product/listForOrder")

    List listForOrder(@RequestBody List productIdList);

} (7). 接此项目提交到Maven仓库

直接可以使用Idea右侧的Maven Projects里的install,打包提交到Maven仓库,或者使用Maven命令:

mvn -Dmaven.test.skip=true -U clean install (8). 启动项目,将项目注册到注册中心,

启动成功后会在注册中心的UI上看到服务的注册信息

 

3、编写第二服务—订单服务

(1). 同第2.2创建商品项目一样,创建一个订单Maven项目

(2). 在项目中把商品类的client依赖引入项目

(3). 在订单项目的Server模块的应用启动类上添加注解

@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients(basePackages = "com.yore.product.client") public class OrderApplication {

    public static void main(String[] args) {         SpringApplication.run(OrderApplication.class,args);     }

} (4). 在Server模块调用商品服务

这里比如在服务层调用,只需要在该类把订单类提供的ProductClient接口自动注解进来,就可以使用商品类向外提供的接口服务

三、项目引入的依赖 Spring Cloud确实开发更加方便了,Spring Cloud版本更新也很快,但是头疼的就是个个版本的兼容性就是很不方便的地方,经常因为版本问题会调入坑里不能自拔,所以如果有时排查后确定不是项目代码问题时,实在没有办法时还是降版本吧。

1、商品类引用的依赖

父pom文件

4.0.0

<groupId>com.yore</groupId> <artifactId>product</artifactId> <version>0.0.1-SNAPSHOT</version> <modules> <module>common</module> <module>client</module> <module>server</module> </modules> <packaging>pom</packaging> <name>product</name> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.M9</spring-cloud.version> <product-common.version>0.0.1-SNAPSHOT</product-common.version> </properties> <!-- 依赖的版本管理 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- 项目中引用common --> <dependency> <groupId>com.yore</groupId> <artifactId>product-common</artifactId> <version>${product-common.version}</version> </dependency> </dependencies> </dependencyManagement> client模块Pom文件

product com.yore 0.0.1-SNAPSHOT 4.0.0

<artifactId>product-client</artifactId> <dependencies> <!-- 项目中用到了Feign注解,引入此包 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- 项目中用到了PostMapping,引入此包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> </dependencies> service模块Pom文件

product com.yore 0.0.1-SNAPSHOT 4.0.0

<artifactId>product-server</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
完整阅读
favorite_border 0
reply 0
公开分享 2019-02-28
重拾后端之Spring Boot(三):找回熟悉的Controller,Service

找回熟悉的Controller,Service Controller哪儿去了? 对于很多习惯了Spring开发的同学来讲,Controller,Service,DAO 这些套路突然间都没了会有不适感。其实呢,这些东西还在,只不过对于较简单的情景下,这些都变成了系统背后帮你做的事情。这一小节我们就先来看看如何将Controller再召唤回来。召唤回来的好处有哪些呢?首先我们可以自定义API URL的路径,其次可以对参数和返回的json结构做一定的处理。 如果要让 TodoController 可以和 TodoRepository 配合工作的话,我们当然需要在 TodoController 中需要引用 TodoRepository。 public class TodoController { @Autowired private TodoRepository repository; //省略其它部分 }复制代码@Autowired 这个修饰符是用于做依赖性注入的,上面的用法叫做 field injection,直接做类成员的注入。但Spring现在鼓励用构造函数来做注入,所以,我们来看看构造函数的注入方法: public class TodoController {

private TodoRepository repository; @Autowired public TodoController(TodoRepository repository){ this.repository = repository; } //省略其它部分

}复制代码当然我们为了可以让Spring知道这是一个支持REST API的 Controller ,还是需要标记其为 @RestController。由于默认的路径映射会在资源根用复数形式,由于todo是辅音后的o结尾,按英语习惯,会映射成 todoes。但这里用 todos 比 todoes 更舒服一些,所以我们再使用另一个 @RequestMapping("/todos") 来自定义路径。这个 Controller 中的其它方法比较简单,就是利用repository中的方法去增删改查即可。 package dev.local.todo;

import org.bson.types.ObjectId; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController @RequestMapping("/todos") public class TodoController {

private TodoRepository repository; @Autowired public TodoController(TodoRepository repository){ this.repository = repository; } @RequestMapping(method = RequestMethod.GET) public List<Todo> getAllTodos(@RequestHeader(value = "userId") String userId) { return repository.findByUserId(new ObjectId(userId)); } @RequestMapping(method = RequestMethod.POST) Todo addTodo(@RequestBody Todo addedTodo) { return repository.insert(addedTodo); } @RequestMapping(value = "/{id}", method = RequestMethod.GET) public Todo getTodo(@PathVariable String id) { return repository.findOne(id); } @RequestMapping(value = "/{id}", method = RequestMethod.PUT) Todo updateTodo(@PathVariable String id, @RequestBody Todo updatedTodo) { updatedTodo.setId(id); return repository.save(updatedTodo); } @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) Todo removeTodo(@PathVariable String id) { Todo deletedTodo = repository.findOne(id); repository.delete(id); return deletedTodo; }

}复制代码上面的代码中需要再说明几个要点:

为什么在类上标记 @RequestMapping("/todos") 后在每个方法上还需要添加 @RequestMapping?类上面定义的 @RequestMapping 的参数会默认应用于所有方法,但如果我们发现某个方法需要有自己的特殊值时,就需要定义这个方法的映射参数。比如上面例子中 addTodo,路径也是 todos,但要求 Request的方法是 POST,所以我们给出了 @RequestMapping(method = RequestMethod.POST)。但 getTodo 方法的路径应该是 todos/:id,这时我们要给出 @RequestMapping(value = "/{id}", method = RequestMethod.GET) 这些方法接受的参数也使用了各种修饰符,@PathVariable 表示参数是从路径中得来的,而 @RequestBody 表示参数应该从 Http Request的body 中解析,类似的 @RequestHeader 表示参数是 Http Request的Header中定义的。

在可以测试之前,我们还需要使用 @Repository 来标记 TodoRepository,以便于Spring可以在依赖注入时可以找到这个类。 package dev.local.todo;

import org.bson.types.ObjectId; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.stereotype.Repository;

import java.util.List;

/**

  • Created by wangpeng on 2017/1/26. */ @Repository public interface TodoRepository extends MongoRepository<Todo, String>{ List findByUserId(ObjectId userId); }复制代码接下来就可以用PostMan做一下测试: 测试一下Controller Service呢?在哪里? 熟悉Spring的童鞋肯定会问,我们刚才的做法等于直接是Controller访问Data了,隔离不够啊。其实我觉得有很多时候,这种简单设计是挺好的,因为业务还没有到达那步,过于复杂的设计其实没啥太大意义。但这里我们还是一步步来实践一下,找回大家熟悉的感觉。 回到原来的熟悉模式再简单不过的,新建一个 TodoService 接口,定义一下目前的增删改查几个操作: public interface TodoService { Todo addTodo(Todo todo); Todo deleteTodo(String id); List findAll(String userId); Todo findById(String id); Todo update(Todo todo); }复制代码为预防我们以后使用 MySQL 等潜在的 “可扩展性”,我们给这个接口的实现命名为 MongoTodoServiceImpl,然后把 Controller 中的大部分代码拿过来改改就行了。当然为了系统可以找到这个依赖并注入需要的类中,我们标记它为 @Service @Service public class MongoTodoServiceImpl implements TodoService{ private final TodoRepository repository;

    @Autowired MongoTodoServiceImpl(TodoRepository repository) { this.repository = repository; }

    @Override public Todo addTodo(Todo todo) { return repository.insert(todo); }

    @Override public Todo deleteTodo(String id) { Todo deletedTodo = repository.findOne(id); repository.delete(id); return deletedTodo; }

    @Override public List findAll(String userId) { return repository.findByUserId(new ObjectId(userId)); }

    @Override public Todo findById(String id) { return repository.findOne(id); }

    @Override public Todo update(Todo todo) { repository.save(todo); return todo; } }复制代码最后把Controller中的所有方法改为使用Service的简单调用就大功告成了。 public class TodoController {

    private TodoService service;

    @Autowired public TodoController(TodoService service){ this.service = service; }

    @RequestMapping(method = RequestMethod.GET) public List getAllTodos(@RequestHeader(value = "userId") String userId) { return service.findAll(userId); }

    @RequestMapping(method = RequestMethod.POST) Todo addTodo(@RequestBody Todo addedTodo) { return service.addTodo(addedTodo); }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET) public Todo getTodo(@PathVariable String id) { return service.findById(id); }

    @RequestMapping(value = "/{id}", method = RequestMethod.PUT) Todo updateTodo(@PathVariable String id, @RequestBody Todo updatedTodo) { updatedTodo.setId(id); return service.update(updatedTodo); }

    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) Todo removeTodo(@PathVariable String id) { return service.deleteTodo(id); } }复制代码说实话如果每个简单类都这么写,我深深地赶脚背离了Spring Boot的意图,虽然你能举出1000个理由这么做有好处。类似的,DAO或DTO要写起来也很简单,但我还是建议在业务没有复杂之前还是享受Spring Boot带给我们的便利吧。

完整阅读
favorite_border 0
reply 0
公开分享 2019-01-03
微信打开有些问题。
  1. 微信打开有些问题。
完整阅读
favorite_border 0
reply 0