Spring Cloud 微服务漫游

微服务是松耦合的分布式软件服务,这些服务执行 少量的 定义明确的任务。 ——《Spring微服务实战》

对微服务的认识

之前做项目,代码都是在一个工程里面,所有代码写完后,打一个 jar 包或 war 包,就放到服务器上面去跑了,这叫做单体架构。如果项目中有一点点需要修改,我们不得不整个工程重新编译打包,再重新部署。现在,我们决定用分布式和集群的方式,把业务功能拆分成多个子项目(服务),子项目可以单独运行,子项目与子项目之间暴露 http 或 rpc 接口,供外部或内部其他服务调用,然后,用一套规范的方式把众多子项目管理起来,这就是微服务架构。

Spring Boot 就是用于快速构建单个微服务的框架,而 Spring Cloud 则是各个微服务的管理者。

阅读更多

Spring(八)SpringBoot 集成 JPA

什么是 JPA ?

之前在 Spring Boot 工程中,一直用 Mybatis 注解方式作为持久层框架。但是 Mybatis 需要手写 SQL 语句,对于简单的项目稍显麻烦。最近发现了 JPA ,使用 JPA 我们几乎可以不用写一句 SQL 语句,非常适合 CURD 场景。JPA 是 Java Persistence API(Java持久化接口) 的缩写。JPA 让我们的应用程序以统一的方式访问持久层。JPA 是 Hibernate 的一个抽象,是一种 ORM 规范,是可以理解为是 Hibernate 功能的一个子集。

阅读更多

Spring(七)深入理解Spring MVC

我们通过 Spring Boot 来创建一个 Web 应用,发挥作用的是 Spring MVC 框架。当我们在IDE里敲入以下代码时,究竟发生了什么呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@GetMapping("/")
public String hello() {
return "login";
}

@PostMapping("/login")
public ModelAndView login(LoginData loginData) {
if (LOGIN.equals(loginData.getLogin())
&& PASSWORD.equals(loginData.getPassword())) {
return new ModelAndView("success",
Collections.singletonMap("login", loginData.getLogin()));
} else {
return new ModelAndView("failure",
Collections.singletonMap("login", loginData.getLogin()));
}
}
阅读更多

使用 JWT 进行认证

什么是 JWT ?

JWT 的全称是 JSON Web Token,是一种跨域认证解决方案。

所谓认证,就是获取用户的身份信息。我们知道,Http 是一种无状态协议,为了实现认证和跟踪用户信息,开发者发明了 cookie-session 方案,该方案流程如下:

  1. 用户向服务器发送用户名和密码;
  2. 服务器验证通过后,在 session 里保存相关数据;
  3. 服务器返回一个 session_id,写入客户端的 Cookie;
  4. 之后,用户的每次请求都会通过 Cookie 把 session_id 传回给服务器;
  5. 服务器通过 session_id,找到先前保存的数据,得到用户信息。

这种方案存在几个问题:

  1. 如果是服务器集群,要求 session 数据要共享,要求每一台服务器都能够读取并同步 session;
  2. 前后端分离,跨域访问的情况下,每次请求的 session_id 都会不一样;
  3. 如果是多端(ios/Android/Web)共用一套后端 API 服务,移动端无法储存 Cookie,需要另辟蹊径。
  4. session 数据是保存在服务器的内存中,无形中增加了服务器的压力。

而 JWT 解决了上述问题,它的思想是:服务器不保存 session 数据了,数据全部保存在客户端,每次请求的时候都发回服务器验证。

阅读更多

Spirng(六) IoC容器探究

Spring

Spring(一)从 传统Java Web到SpirngBoot 中对 Ioc 的概念已经有了初步认识:Spring 通过一个配置文件描述 Bean 与 Bean 之间的依赖关系,利用 Java 的类加载器和反射机制实例化 Bean 并建立 Bean 之间的依赖关系。

我们将调用类对某一接口实现类的依赖关系交由 Spring 容器管理,容器在我们需要的时候,通过注入及时地将对象进行实例化并装配好 bean,无需我们自己 new 。

除此之外,由于JDK提供的访问资源的类对底层资源并不友好,缺少从类路径或者Web容器的上下文获取资源的操作类,Spring重新设计了一个 Resource 接口,用于更强的底层资源访问能力。有了这个资源类,就可以将Spring的配置信息放在任何地方(数据库、LDAP)。而为了访问不同类型的资源,Spring还提供了一个强大的加载资源的机制,定义了一套资源加载的接口 ResourceLoader 及其实现类,可以访问包括classpath:file:http://ftp://等地址前缀资源。

这一篇具体讲讲关于 Spring Ioc的更多内容。

阅读更多

Spring(五)使用 Thymeleaf 模板引擎

thy

什么是 Thymeleaf

Thymeleaf的官方定义为

Thymeleaf is a modern server-side Java template engine for both web and standalone environments。

简单地讲,Thymeleaf 是一种现代的Java服务器端模板引擎。可以达到和JSP一样的效果,但是比起JSP对于前端测试更加友好。JSP需要运行起来才能看到效果,而 Thymeleaf 本身就是html格式,无需服务器也能看到效果。

阅读更多

Spring(四)使用 RESTful 风格

RESTful

什么是 RESTful ?

REST这个词由 Roy Thomas Fielding 在他2000年的博士论文中提出。全称是 Representational State Transfer ,这个词省略了主语 Resource,因此翻译成中文是:资源表述性状态转化。

资源(Resource)

之前在 HTTP之旅 一文中提到过,一个 Web 页面包含了很多对象,这些对象可以是 html ,可以是 JSON、XML,或者可以是图片、嵌入的视频,还可以是java小程序等等。这些对象都可以称为资源。我们通过 统一资源定位符(URI)去定位资源。

表述性(REpresentational)

正如上面提到的,资源可以用各种形式来进行表述,我们可以使用最适合资源使用者的任意形式来表述资源。资源具体呈现出来的形式,就是资源的表述性(REpresentational)。

状态转化(State Transfer)

HTTP协议是无状态协议。这意味着,资源的所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生”状态转化”(State Transfer)。

客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

简单总结:资源通过 URL 进行识别和定位,然后通过行为(即 HTTP 方法)来定义应该完成怎样的功能。

阅读更多

Json初探

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,通常用于在客户端和服务器之间传递数据。

JSON 类似下面这样:

1
{"id":4,"name":"梅西","pwd":"6666"}

JSON 的优点:

  • 轻量级交互语言
  • 结构简单
  • 易于解析
阅读更多

Spring(二) SpringBoot 集成 Mybatis

mybatis

使用 Mybatis 简化 JDBC 操作 中,简单描述了 Mybatis 的使用。这一篇主要记录下如何集成 Spring boot


Spring Boot 集成 Mybatis 简明过程

创建一个 Spring Initalizr 工程,依赖选择 web、MySQL、Mybatis

在application.properties填入以下内容

1
2
3
4
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/neu?characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=YourPassword
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
  • 创建pojo包,创建 Student 实体类,跟数据库对应
1
2
3
4
5
6
7
8
public class Student {
Integer id;
String name;
String major;
Integer grade;

// 省略 getter setter
}
  • 创建mapper包,创建StudentMapper接口

    在SpringBootApplication类中,添加@MapperScan("io.jerrysheh.student.mapper")注解,即可不用在 mapper 包下面的每一个接口都注解Mapper了。

1
2
3
4
5
6
@Mapper
public interface StudentMapper {

@Select("SELECT * FROM student")
List<Student> findAll();
}

如果有多个参数,用 @Param 注解

1
2
@Update("update user set password=#{password} WHERE id=#{id}")
void updatePassword(@Param("password") String password, @Param("id") Integer id);
  • 创建Controller包,创建StudentController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
public class StudentController {

@Autowired
StudentMapper studentMapper;

@GetMapping("/listStudent")
public void listStudent(){
List<Student> studentList = studentMapper.findAll();
for (Student student:
studentList) {
System.out.println(student.getName());
}
}
}

这样,运行后访问 127.0.0.1:8080/listStudent ,可看到控制台输出数据库查到的所有 student 名字。

xml 方式

有时候为了将SQL和Java代码隔离 ,会将 SQL 抽到 xml 里面,配置方法如下:

在application.properties填入以下内容(重要!!!)

1
2
3
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.jerrysheh.fun.entity
mybatis.configuration.map-underscore-to-camel-case=true

在 resources 目录下创建 mapper 文件夹,再创建 productmapper.xml 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.jerrysheh.fun.mapper.ProductMapper">

<select id="selectAll" resultType="com.jerrysheh.fun.entity.Product">
SELECT * FROM fun_product;
</select>

<insert id="addProduct" >
insert into fun_product(product_name, product_price) values (#{productName}, #{productPrice})
</insert>
</mapper>

注意:namespace一定要填写对应的 mapper 接口,不能只到 package

编写单元测试

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootTest
public class test {

@Autowired
ProductMapper productMapper;

@Test
public void test(){
List<Product> productList = productMapper.selectAll();
productList.forEach(System.out::println);
}

}

以下为Mybatis知识点

井字符和美元符的区别

#相当于对数据加上双引号,$相当于直接显示数据

#方式能够很大程度防止sql注入。$方式无法防止Sql注入。


动态SQL

虽然我们使用了注解,但是还是在注解接口CategoryMapper中使用了原生 SQL 语句。

1
2
@Insert(" insert into category_ ( name ) values (#{name}) ")
public int add(Category category);

其实,我们可以提供一个类,专门用来生成SQL语句

CategoryDynaSqlProvider.java

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
28
29
30
31
32
33
34
35
36
37
38
39
package com.jerrysheh.mapper;
import org.apache.ibatis.jdbc.SQL;

public class CategoryDynaSqlProvider {
public String list() {
return new SQL()
.SELECT("*")
.FROM("category_")
.toString();

}
public String get() {
return new SQL()
.SELECT("*")
.FROM("category_")
.WHERE("id=#{id}")
.toString();
}

public String add(){
return new SQL()
.INSERT_INTO("category_")
.VALUES("name", "#{name}")
.toString();
}
public String update(){
return new SQL()
.UPDATE("category_")
.SET("name=#{name}")
.WHERE("id=#{id}")
.toString();
}
public String delete(){
return new SQL()
.DELETE_FROM("category_")
.WHERE("id=#{id}")
.toString();
}
}

然后修改我们的CategoryMapper接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.jerrysheh.mapper;

import com.jerrysheh.pojo.Category;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface CategoryMapper {

@InsertProvider(type=CategoryDynaSqlProvider.class,method="add")
public int add(Category category);

@DeleteProvider(type=CategoryDynaSqlProvider.class,method="delete")
public void delete(int id);

@SelectProvider(type=CategoryDynaSqlProvider.class,method="get")
public Category get(int id);

@UpdateProvider(type=CategoryDynaSqlProvider.class,method="update")
public int update(Category category);

@SelectProvider(type=CategoryDynaSqlProvider.class,method="list")
public List<Category> list();
}

这样就可以动态生成SQL语句了

  • 注解中的 type= 填入我们的动态生成SQL类CategoryDynaSqlProvider.class
  • method=填入CategoryDynaSqlProvider类里的方法

@Results结果映射

如果 javabean 的属性字段 跟 数据库字段一一对应,名字保持一致,则直接可以:

1
2
@Select("select *from Demo where id=#{id}")  
public Demo selectById(int id);

但如果不对应,就要用@Result修饰返回的结果集,而@Results注解将指定的数据库列与指定JavaBean属性映射起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Select("SELECT * FROM `wx_message_config` WHERE `content_key_words` IS NOT NULL AND LENGTH(content_key_words) > 0")
@Results({
@Result(property = "msgType", column = "msg_type"),
@Result(property = "eventType", column = "event_type"),
@Result(property = "eventKey",column = "event_key"),
@Result(property = "contentKeyWords",column = "content_key_words")
})
List<WxMessageConfig> queryAllKeyWords();

@Select("SELECT * FROM `wx_message_config` WHERE `id` = #{id}")
@Results({
@Result(property = "msgType", column = "msg_type"),
@Result(property = "eventType", column = "event_type"),
@Result(property = "eventKey",column = "event_key"),
@Result(property = "contentKeyWords",column = "content_key_words")
})
WxMessageConfig queryKwById(int id);

这样会导致写很多重复内容,可以用 @ResultMap(“id”)

1
2
3
4
5
6
7
8
9
@Select("SELECT id, name, password FROM user WHERE id = #{id}")
@Results(id = "userMap", value = { @Result(column = "id", property = "id", javaType = Integer.class),
@Result(column = "name", property = "name", javaType = String.class),
@Result(column = "password", property = "password", javaType = String.class) })
User findById(Integer id);

@Select("SELECT * FROM user")
@ResultMap("userMap")
List<User> fingAll();

一对多查询

假设有一张 商品表 和 一张 图片表, 一个商品对应多张图片

那么如何取出一个商品,包含商品的所有属性,以及对应的所有图片呢?

实体类

商品的所有字段,同时,要添加一个 List<String> 表示多张图片的集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Product {
private Integer id;
private Integer user_id;
private String name;
private String price;
private Date gmt_create;
private String description;
private Integer cate_id;
private Integer number;

// 关键!
private List<String> link;

// 省略 getter setter
}

图片的所有属性,用 String 表示图片地址

1
2
3
4
5
public class Image {
private Integer id;
private Integer product_id;
private String link;
}

Mapper接口

在图片的Mapper接口中,根据商品id找到对应的所有图片

1
2
3
// 根据商品id找图片
@Select("SELECT link from image WHERE product_id = #{product_id}")
List<String> getImageLinksByProductId(Integer product_id);

在商品的Mapper接口中,通过 @Results@Many 来进行关联

1
2
3
4
5
6
7
8
9
10
// 取出在售的所有商品,最新的排前面
@Select("select * from product WHERE number > 0 ORDER BY id DESC")
@Results({
// 这里要对id进行限定,否则 id 会为 null
@Result(property = "id", column = "id"),

// 将 image 的 link 和 product 的 id 绑定,通过 @Many 查询 返回 List
@Result(property = "link", column = "id", javaType = List.class, many = @Many(select = "com.zsc.tradeplatform.mapper.ImageMapper.getImageLinksByProductId")),
})
List<Product> getAll();

控制器

1
2
3
4
5
@ResponseBody
@GetMapping("/api/product")
public List<Product> getAllProduct(){
return productService.getAllProduct();
}

访问 127.0.0.1:8080/api/product 查看结果

Spring(一)从传统Java Web到SpirngBoot

从 MVC 结构到 Web 框架

Java Web 技术日新月异,如今,我们开发 Java Web 项目都是靠框架快速搭建。然而,为什么需要框架?还得从传统的 MVC 结构说起。

传统 Model-View-Controller 架构

典型的Java Web应用架构如下:

MVC

  1. 浏览器发送 HTTP 请求到服务端,被 Controller(Servlet) 获取并进行处理(参数解析、请求转发)
  2. Controller 调用核心业务逻辑 —— Model
  3. Model 进行数据库存取操作,并将操作结果返回
  4. Controller 将业务逻辑处理结果交给View(JSP),动态输出 HTML 内容
  5. 动态生成的 HTML 内容返回到浏览器显示

封装 Servlet

在这个过程中,我们要操作 Servlet 写大量的功能代码,为了简化,我们可以把 Servlet 中经常要实现的功能封装起来并提供一层公共抽象,这样我们只要编写简单的 POJO 代码或者实现一些接口,就能完成复杂的 Web 请求的后端逻辑处理。 Spring MVC 就是这样的一个框架。它提供了一个DispacherServlet,我们只需实现 Spring MVC 提供的接口就可以完成复杂的操作,而不用写大量的 Servlet 代码。

阅读更多

Java Web(二)JavaServer Pages (JSP)

我们知道,Servlet 中可以对客户端发来的信息进行处理(doGet、doPost等),可是,在 Servlet 里面输出 HTML 代码是一件很酸爽的事情。

如果我们直接写 HTML 代码,然后在需要动态获取的地方用 Java 代码来实现,不是很方便?

JSP 就是干这个事的!

维基百科定义: JSP(全称JavaServer Pages)是由Sun Microsystems公司主导建立的一种动态网页技术标准。 JSP部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成HTML、XML或其他格式文档的Web网页,然后返回给请求者。

阅读更多

Java Web(一)web容器和Servlet

在聊 Servlet 之前,先讲讲什么是 Web服务器 和 应用服务器。

Web服务器

无论何种 Web 资源,想被远程计算机访问,都必须有一个与之对应的网络通信程序,当用户来访问时,这个网络通信程序读取 Web 资源数据,并把数据发送给来访者。

Web服务器就是一个网络通信程序,它用于完成底层网络通迅。具体来说,它将某个主机上的资源映射为一个URL供外界访问。

使用 Web服务器,Web 应用的开发者只需要关注 Web 资源怎么编写,而不需要关心资源如何发送到客户端手中,从而极大的减轻了开发者的开发工作量。


应用服务器 - Tomcat

我们的 Web 应用要运行起来,是需要部署在应用服务器上而不是Web服务器。因为web服务器只负责资源映射,而程序业务逻辑需要另外的容器来处理。

应用服务器一般装载着我们的后端应用程序,帮助我们接收请求、处理请求、响应请求。Tomcat 就是一种常见的应用服务器,但也具有web服务器的功能,所以直接访问也可以。

通常,Tomcat 装载着我们的 Servlet 对象。那什么是 Servlet 呢?下文会讲到。

在实际的生产环境中,由于负载均衡,cdn加速等原因,我们还是需要在应用服务器的前端再加一个web服务器来提高访问效率,常用的有 Nginx, Apache 这样的服务器。

阅读更多
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×