Posted in Spring, Spring Boot, 技术

Spring Boot 整合 Mybatis 实现 Druid 多数据源详解

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!

“清醒时做事,糊涂时跑步,大怒时睡觉,独处时思考”

本文提纲
一、多数据源的应用场景
二、运行 springboot-mybatis-mutil-datasource 工程案例
三、springboot-mybatis-mutil-datasource 工程代码配置详解

一、多数据源的应用场景

目前,业界流行的数据操作框架是 Mybatis,那 Druid 是什么呢?
Druid 是 Java 的数据库连接池组件。Druid 能够提供强大的监控和扩展功能。比如可以监控 SQL ,在监控业务可以查询慢查询 SQL 列表等。Druid 核心主要包括三部分:
1. DruidDriver 代理 Driver,能够提供基于 Filter-Chain 模式的插件体系。
2. DruidDataSource 高效可管理的数据库连接池
3. SQLParser

当业务数据量达到了一定程度,DBA 需要合理配置数据库资源。即配置主库的机器高配置,把核心高频的数据放在主库上;把次要的数据放在从库,低配置。开源节流嘛,就这个意思。把数据放在不同的数据库里,就需要通过不同的数据源进行操作数据。这里我们举个 springboot-mybatis-mutil-datasource 工程案例:
user 用户表在主库 master 上,地址表 city 在从库 cluster 上。下面实现获取 根据用户名获取用户信息,包括从库的地址信息 REST API,那么需要从主库和从库中分别获取数据,并在业务逻辑层组装返回。逻辑如图:

下面就运行这个案例。

 

二、运行 springboot-mybatis-mutil-datasource 工程案例

git clone 下载工程 springboot-learning-example ,项目地址见 GitHub – https://github.com/JeffLi1993/springboot-learning-example。下面开始运行工程步骤(Quick Start):
1.数据库准备
a.创建 cluster 数据库 springbootdb:

CREATE DATABASE springbootdb;

b.创建表 city :(因为我喜欢徒步)

DROP TABLE IF EXISTS `city`;
CREATE TABLE `city` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '城市编号',
`province_id` int(10) unsigned NOT NULL COMMENT '省份编号',
`city_name` varchar(25) DEFAULT NULL COMMENT '城市名称',
`description` varchar(25) DEFAULT NULL COMMENT '描述',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

c.插入数据

INSERT city VALUES (1 ,1,'温岭市','BYSocket 的家在温岭。');

然后,再创建一个 master 数据库
a.创建数据库 springbootdb_cluster:

CREATE DATABASE springbootdb_cluster;

b.创建表 user :

DROP TABLE IF EXISTS `city`;
CREATE TABLE user
(
id INT(10) unsigned PRIMARY KEY NOT NULL COMMENT '用户编号' AUTO_INCREMENT,
user_name VARCHAR(25) COMMENT '用户名称',
description VARCHAR(25) COMMENT '描述'
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

c.插入数据

INSERT user VALUES (1 ,'泥瓦匠','他有一个小网站 bysocket.com');

(以上数据库创建无先后顺序)

2. 项目结构介绍
项目结构如下图所示:
org.spring.springboot.config.ds – 配置层,这里是数据源的配置,包括 master 和 cluster 的数据源配置
org.spring.springboot.controller – Controller 层
org.spring.springboot.dao – 数据操作层 DAO,细分了 master 和 cluster 包下的 DAO 操作类
org.spring.springboot.domain – 实体类
org.spring.springboot.service – 业务逻辑层
Application – 应用启动类
application.properties – 应用配置文件,应用启动会自动读取配置

3.改数据库配置
打开 application.properties 文件, 修改相应的主从数据源配置,比如数据源地址、账号、密码等。(如果不是用 MySQL,自行添加连接驱动 pom,然后修改驱动名配置。)

4.编译工程
在项目根目录 springboot-learning-example,运行 maven 指令:

mvn clean install

 

5.运行工程
右键运行 Application 应用启动类(位置:/springboot-learning-example/springboot-mybatis-mutil-datasource/src/main/java/org/spring/springboot/Application.java)的 main 函数,这样就成功启动了 springboot-mybatis-mutil-datasource 案例。

在浏览器打开:
http://localhost:8080/api/user?userName=泥瓦匠
浏览器返回 JSON 结果:

{
    "id": 1,
    "userName": "泥瓦匠",
    "description": "他有一个小网站 bysocket.com",
    "city": {
        "id": 1,
        "provinceId": 1,
        "cityName": "温岭市",
        "description": "BYSocket 的家在温岭。"
    }
}

这里 city 结果体来自 cluster 库,user 结果体来自 master 库。

 

三、springboot-mybatis-mutil-datasource 工程代码配置详解

代码共享在我的 GitHub 上:
https://github.com/JeffLi1993/springboot-learning-example/tree/master/springboot-mybatis-mutil-datasource

首先代码工程结构如下:
org.spring.springboot.config.ds 包包含了多数据源的配置,同样有第三个数据源,按照前几个复制即可
resources/mapper 下面有两个模块,分别是 Mybatis 不同数据源需要扫描的 mapper xml 目录

├── pom.xml
└── src
    └── main
        ├── java
        │   └── org
        │       └── spring
        │           └── springboot
        │               ├── Application.java
        │               ├── config
        │               │   └── ds
        │               │       ├── ClusterDataSourceConfig.java
        │               │       └── MasterDataSourceConfig.java
        │               ├── controller
        │               │   └── UserRestController.java
        │               ├── dao
        │               │   ├── cluster
        │               │   │   └── CityDao.java
        │               │   └── master
        │               │       └── UserDao.java
        │               ├── domain
        │               │   ├── City.java
        │               │   └── User.java
        │               └── service
        │                   ├── UserService.java
        │                   └── impl
        │                       └── UserServiceImpl.java
        └── resources
            ├── application.properties
            └── mapper
                ├── cluster
                │   └── CityMapper.xml
                └── master
                    └── UserMapper.xml

 

1. 依赖 pom.xml
Mybatis 通过 Spring Boot Mybatis Starter 依赖
Druid 是数据库连接池依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>springboot</groupId>
    <artifactId>springboot-mybatis-mutil-datasource</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-mybatis-mutil-datasource :: Spring Boot 实现 Mybatis 多数据源配置</name>

    <!-- Spring Boot 启动父依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>

    <properties>
        <mybatis-spring-boot>1.2.0</mybatis-spring-boot>
        <mysql-connector>5.1.39</mysql-connector>
        <druid>1.0.18</druid>
    </properties>

    <dependencies>

        <!-- Spring Boot Web 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Boot Test 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- Spring Boot Mybatis 依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis-spring-boot}</version>
        </dependency>

        <!-- MySQL 连接驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector}</version>
        </dependency>

        <!-- Druid 数据连接池依赖 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid}</version>
        </dependency>

        <!-- Junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>

2. application.properties 配置两个数据源配置
数据源配置会被数据源数据源配置如下

## master 数据源配置
master.datasource.url=jdbc:mysql://localhost:3306/springbootdb?useUnicode=true&characterEncoding=utf8
master.datasource.username=root
master.datasource.password=123456
master.datasource.driverClassName=com.mysql.jdbc.Driver

## cluster 数据源配置
cluster.datasource.url=jdbc:mysql://localhost:3306/springbootdb_cluster?useUnicode=true&characterEncoding=utf8
cluster.datasource.username=root
cluster.datasource.password=123456
cluster.datasource.driverClassName=com.mysql.jdbc.Driver

3. 数据源配置
多数据源配置的时候注意,必须要有一个主数据源,即 MasterDataSourceConfig 配置:

@Configuration
// 扫描 Mapper 接口并容器管理
@MapperScan(basePackages = MasterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDataSourceConfig {

    // 精确到 master 目录,以便跟其他数据源隔离
    static final String PACKAGE = "org.spring.springboot.dao.master";
    static final String MAPPER_LOCATION = "classpath:mapper/master/*.xml";

    @Value("${master.datasource.url}")
    private String url;

    @Value("${master.datasource.username}")
    private String user;

    @Value("${master.datasource.password}")
    private String password;

    @Value("${master.datasource.driverClassName}")
    private String driverClass;

    @Bean(name = "masterDataSource")
    @Primary
    public DataSource masterDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(user);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean(name = "masterTransactionManager")
    @Primary
    public DataSourceTransactionManager masterTransactionManager() {
        return new DataSourceTransactionManager(masterDataSource());
    }

    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(masterDataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(MasterDataSourceConfig.MAPPER_LOCATION));
        return sessionFactory.getObject();
    }
}

@Primary 标志这个 Bean 如果在多个同类 Bean 候选时,该 Bean 优先被考虑。「多数据源配置的时候注意,必须要有一个主数据源,用 @Primary 标志该 Bean」
@MapperScan 扫描 Mapper 接口并容器管理,包路径精确到 master,为了和下面 cluster 数据源做到精确区分
@Value 获取全局配置文件 application.properties 的 kv 配置,并自动装配
sqlSessionFactoryRef 表示定义了 key ,表示一个唯一 SqlSessionFactory 实例

同理可得,从数据源 ClusterDataSourceConfig 配置如下:

@Configuration
// 扫描 Mapper 接口并容器管理
@MapperScan(basePackages = ClusterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "clusterSqlSessionFactory")
public class ClusterDataSourceConfig {

    // 精确到 cluster 目录,以便跟其他数据源隔离
    static final String PACKAGE = "org.spring.springboot.dao.cluster";
    static final String MAPPER_LOCATION = "classpath:mapper/cluster/*.xml";

    @Value("${cluster.datasource.url}")
    private String url;

    @Value("${cluster.datasource.username}")
    private String user;

    @Value("${cluster.datasource.password}")
    private String password;

    @Value("${cluster.datasource.driverClassName}")
    private String driverClass;

    @Bean(name = "clusterDataSource")
    public DataSource clusterDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(user);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean(name = "clusterTransactionManager")
    public DataSourceTransactionManager clusterTransactionManager() {
        return new DataSourceTransactionManager(clusterDataSource());
    }

    @Bean(name = "clusterSqlSessionFactory")
    public SqlSessionFactory clusterSqlSessionFactory(@Qualifier("clusterDataSource") DataSource clusterDataSource)
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(clusterDataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(ClusterDataSourceConfig.MAPPER_LOCATION));
        return sessionFactory.getObject();
    }
}

上面数据配置分别扫描 Mapper 接口,org.spring.springboot.dao.master(对应 xml classpath:mapper/master ) 和 org.spring.springboot.dao.cluster(对应 xml classpath:mapper/cluster ) 包中对应的 UserDAO 和 CityDAO 。
都有 @Mapper 标志为 Mybatis 的并通过容器管理的 Bean。Mybatis 内部会使用反射机制运行去解析相应 SQL。

3.业务层 biz
biz 照常注入了两个 DAO,如同以前一样正常工作。不用关心和指定到具体说明数据源。

/**
 * 用户业务实现层
 *
 * Created by bysocket on 07/02/2017.
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao; // 主数据源

    @Autowired
    private CityDao cityDao; // 从数据源

    @Override
    public User findByName(String userName) {
        User user = userDao.findByName(userName);
        City city = cityDao.findByName("温岭市");
        user.setCity(city);
        return user;
    }
}

四、小结

多数据源适合的场景很多。不同的 DataSource ,不同的 SqlSessionFactory 和 不同的 DAO 层,在业务逻辑层做 整合。总结的架构图如下:

同样,如果实战中,大家遇到什么,或者建议《Spring boot 那些事》还需要一起交流的。请点击留言。

推荐书《奥黛丽·赫本》一本讲女神的故事。

欢迎扫一扫我的公众号关注 — 及时得到博客订阅哦!
— http://www.bysocket.com/ —
— https://github.com/JeffLi1993 —

Posted in 清文

听一首《回上海》…

岁月在走
会给你各种留恋式的
惋惜
也会给你先前未知的
答案

听一首先 《回上海
有人说:
“中年以后的男人,时常会觉得孤独,因为他一睁开眼,周围都是要依靠他的人,却没有他依靠的人。”

我好友也说:
“我觉得世界上所有事情都在你没有准备好的时候,就开始;在你准备好的时候,就结束了。— 《年华似水》”

我曾经想,仗势欺人的过着。make mistake,no care。因为老子年轻。错了。
至今记得好友说我,劈头盖脸的说我,不对。我,我不知道三旬后会是怎样,但我知道二十多到三十岁这段时间很重要,灰常重要。就是需要认真对待的意思。

我开始思考为啥我会错过这么多?理想也罢,目标也是,你怎么做我也不能模仿。我只是在各个地方学着,说说,总结给自己看看。

碎片

前段时间,看《人类简史》,没看完。里面说,
“ 现今生活在富裕国家的人都有肥胖的问题,肥胖症几乎像瘟疫一样蔓延,还很快地将魔爪伸向发展中国家。”
生活水平的提高,为健康付的钱也是应该的。至少出身汗,挺适合我的,吃饭也香,心态也好。

也记得很迟开始喜欢读点东西,记得从《超级阿嬷的信》开始喜欢看,那时候是我在读大学。我开始读书的时间很晚,我一直在找到那原来也有我喜欢看的书。看的慢,还是慢。很多书看了几页,就没看了。但有书,有一句话值得你看到,感到,思考到就好。

所以我得慢慢开始,开始少交学费得做事
最近发现了,我的表达很有问题。学着去改,去告诉自己三思而言。还看了 TED 《如何成为一个更好的交谈者?》,里面说:
「 不要三心二意 」
告诉我要专注,看着人家的眼睛,那不会骗人。处在那个环境、那个情景
「 不要好为人师 」
不然听众会觉得无聊 a boring teacher
「 使用开放性的问题 」
』Who、What、When、Where、Why、How。就用初中英语老师教我们的 5 个 W 吧
「 顺其自然 」
「 如果你不知道,你就说你不知道 」

我想更重要的是换位思考。多在他人的角度想事情,说人话。就像最近还在看的传记《奥黛丽赫本》中她妈妈说的:
“ 记得要想想别人”
“ 不要老是谈你自己,你没什么了不起,这世上还有别人”

还有种不说的人,有着无法长大的理想,改变吧。除非你很屌,活在自己世界很累
来吧,一起和我慢慢成为更好的交谈者
还有,那天中午吃饭看了 TED 《被拒100天》。因为害怕批评,所以我们很少向他人寻求反馈。我们把天性压抑,看着人家,听着人家的生活一样,以至于不敢放手去做感兴和惧怕的事。

放空自己思考,寻求反馈,总结自我
就这样

推荐:《 死亡学分一起修吧 》
上一篇:《 Spring Boot HTTP over JSON 的错误码异常处理 》

Posted in Spring, Spring Boot, 技术

Spring Boot HTTP over JSON 的错误码异常处理

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!

“年轻人不要怕表现,要敢于出来表现,但还是那句话,要有正确的度,你的表现是分析问题和解决问题的能力。”

– 《你凭什么做好互联网》

本文提纲
一、异常统一处理的使用场景
二、运行 springboot-validation-over-json 工程
三、springboot-validation-over-json 工程代码详解

一、异常统一处理的使用场景

在前后端分离开发中,经常用 HTTP over JSON 作为服务进行前后端联调对接。这里简单介绍下为啥前后端分离开发?我想到如下:

1.低耦合,责权分离,模块化。前后端之间利用轻量级协议对接耦合。
2.便于敏捷开发:后端给出 api 文档 -> 前端根据文档,mock出数据开发 ;同时,后端实现业务逻辑。
3.微服务尤其适用

这时候 HTTP over JSON 形式中很多涉及到返回码,错误码相关的处理。比如xxx参数不完整,权限不足,用户不存在等。

怎么统一处理认为是异常的场景呢?
利用的是 Spring 4.x 提供的 RestControllerAdvice。这里做下说明,也可以根据 ControllerAdvice 去实现。这里案例是 HTTP over JSON 模式,所以直接利用
RestControllerAdvice ,控制层通知器,这里用于统一拦截异常,进行响应处理。工作模式,如图:

二、运行 springboot-validation-over-json 工程

运行环境:JDK 7 或 8,Maven 3.0+
技术栈:SpringBoot 1.5+(内涵 Spring 4.x)

1.git clone 下载工程 springboot-learning-example
项目地址见 GitHub – https://github.com/JeffLi1993/springboot-learning-example:

git clone git@github.com:JeffLi1993/springboot-learning-example.git

然后,Maven 编译安装这个工程:

cd springboot-learning-example
mvn clean install

2.运行 springboot-validation-over-json 工程
右键运行 springboot-validation-over-json 工程 Application 应用启动类的 main 函数。默认端口 8080

3.访问案例
a. 参数不完整案例:
访问浏览器打开下面链接,可得到以下 JSON 返回
http://localhost:8080/api/city?cityName=

{
    "code": "000001",
    "message": "params no complete",
    "result": null
}

b. 成功案例:
访问浏览器打开下面链接,可得到以下 JSON 返回
http://localhost:8080/api/city?cityName=%E6%B8%A9%E5%B2%AD%E5%B8%82

{
    "code": "0",
    "message": "success",
    "result": {
        "id": 1,
        "provinceId": 2,
        "cityName": "温岭",
        "description": "是我的故乡"
    }
}

三、springboot-validation-over-json 工程代码详解

代码详解提纲:
a.控制层通知器
b.响应码设计

同样,代码共享在我的 GitHub 上:
https://github.com/JeffLi1993/springboot-learning-example/tree/master/springboot-validation-over-json

首先,工程代码目录如下:

├── pom.xml
└── src
    └── main
        └── java
            └── org
                └── spring
                    └── springboot
                        ├── Application.java
                        ├── constant
                        │   └── CityErrorInfoEnum.java
                        ├── result
                        │   ├── ErrorInfoInterface.java
                        │   ├── GlobalErrorInfoEnum.java
                        │   ├── GlobalErrorInfoException.java
                        │   ├── GlobalErrorInfoHandler.java
                        │   └── ResultBody.java
                        └── web
                            ├── City.java
                            └── ErrorJsonController.java

a.控制层通知器
GlobalErrorInfoHandler.java 代码如下:

@RestControllerAdvice
public class GlobalErrorInfoHandler {

@ExceptionHandler(value = GlobalErrorInfoException.class)
public ResultBody errorHandlerOverJson(HttpServletRequest request,
GlobalErrorInfoException exception) {
    ErrorInfoInterface errorInfo = exception.getErrorInfo();
    ResultBody result = new ResultBody(errorInfo);
    return result;
}
}

@ExceptionHandler 注解,标记了使用 errorHandlerOverJson() 方法来处理 GlobalErrorInfoException 异常。
@RestControllerAdvice 是 @ControllerAdvice 和 @ResponseBody 的语义结合。是控制器增强,直接返回对象。这里用于统一拦截异常,然后返回错误码对象体。
@ResponseBody 作用: 该注解用于将 Controller 的方法返回的对象,通过适当的 HttpMessageConverter 转换为指定格式后,写入到 Response 对象的 body 数据区。

b.响应码设计
简单讲讲,这里定义了一个错误码接口,全局错误码枚举和各个业务错误码枚举去实现接口,并用枚举值枚举出错误码及错误码消息列表。如图:

四、小结

如果实战中,大家遇到什么,或者建议《Spring boot 那些事》还需要一起交流的。请点击留言。

推荐书《腾讯传》,其中几章写的很不错。

欢迎扫一扫我的公众号关注 — 及时得到博客订阅哦!
— http://www.bysocket.com/ —
— https://github.com/JeffLi1993 —

Posted in Spring, Spring Boot, 技术

Springboot 整合 Dubbo/ZooKeeper 详解 SOA 案例

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!
“看看星空,会觉得自己很渺小,可能我们在宇宙中从来就是一个偶然。所以,无论什么事情,仔细想一想,都没有什么大不了的。这能帮助自己在遇到挫折时稳定心态,想得更开。”  – 《腾讯传》
本文提纲
一、为啥整合 Dubbo 实现 SOA
二、运行 springboot-dubbo-server 和 springboot-dubbo-client 工程
三、springboot-dubbo-server 和 springboot-dubbo-client 工程配置详解

Spring Boot 系列文章:《Spring Boot 那些事

 

一、为啥整合 Dubbo 实现 SOA

Dubbo 不单单只是高性能的 RPC 调用框架,更是 SOA 服务治理的一种方案。
核心
1. 远程通信,向本地调用一样调用远程方法。
2. 集群容错
3. 服务自动发现和注册,可平滑添加或者删除服务提供者。
我们常常使用 Springboot 暴露 HTTP 服务,并走 JSON 模式。但慢慢量大了,一种 SOA 的治理方案。这样可以暴露出 Dubbo 服务接口,提供给 Dubbo 消费者进行 RPC 调用。下面我们详解下如何集成 Dubbo。

 

二、运行 springboot-dubbo-server 和 springboot-dubbo-client 工程

运行环境:JDK 7 或 8,Maven 3.0+
技术栈:SpringBoot 1.5+、Dubbo 2.5+、ZooKeeper 3.3+

 

1.ZooKeeper 服务注册中心
ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
下载 ZooKeeper ,地址 http://www.apache.org/dyn/closer.cgi/zookeeper
解压 ZooKeeper
tar zxvf zookeeper-3.4.8.tar.gz
在 conf 目录新建 zoo.cfg ,照着该目录的 zoo_sample.cfg 配置如下。
cd zookeeper-3.3.6/conf
vim zoo.cfg
zoo.cfg 代码如下(自己指定 log 文件目录):
tickTime=2000
dataDir=/javaee/zookeeper/data 
dataLogDir=/javaee/zookeeper/log
clientPort=2181
在 bin 目录下,启动 ZooKeeper:
cd zookeeper-3.3.6/bin
./zkServer.sh start

 

2. git clone 下载工程 springboot-learning-example
git clone git@github.com:JeffLi1993/springboot-learning-example.git

然后,Maven 编译安装这个工程:

cd springboot-learning-example
mvn clean install

 

3.运行 springboot-dubbo-server Dubbo 服务提供者工程
右键运行 springboot-dubbo-server 工程 ServerApplication 应用启动类的 main 函数。Console 中出现如下表示项目启动成功:
这里表示 Dubbo 服务已经启动成功,并注册到 ZK (ZooKeeper)中。

 

4.运行 springboot-dubbo-client Dubbo 服务消费者工程

右键运行 springboot-dubbo-client 工程 ClientApplication 应用启动类的 main 函数。Console 中出现如下:

...
2017-03-01 16:31:38.473  INFO 9896 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-03-01 16:31:38.538  INFO 9896 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8081 (http)
2017-03-01 16:31:38.547  INFO 9896 --- [           main] org.spring.springboot.ClientApplication  : Started ClientApplication in 6.055 seconds (JVM running for 7.026)
City{id=1, provinceId=2, cityName='温岭', description='是我的故乡'}
最后打印的城市信息,就是通过 Dubbo 服务接口调用获取的。顺利运行成功,下面详解下各个代码及配置。

 

三、springboot-dubbo-server 和 springboot-dubbo-client 工程配置详解

1.详解 springboot-dubbo-server Dubbo 服务提供者工程
springboot-dubbo-server 工程目录结构
├── pom.xml
└── src
    └── main
        ├── java
        │   └── org
        │       └── spring
        │           └── springboot
        │               ├── ServerApplication.java
        │               ├── domain
        │               │   └── City.java
        │               └── dubbo
        │                   ├── CityDubboService.java
        │                   └── impl
        │                       └── CityDubboServiceImpl.java
        └── resources
            └── application.properties

 

a.pom.xml 配置

pom.xml 中依赖了 spring-boot-starter-dubbo 工程,该项目地址是 https://github.com/teaey/spring-boot-starter-dubbo。pom.xml 配置如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>springboot</groupId>
    <artifactId>springboot-dubbo-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-dubbo 服务端:: 整合 Dubbo/ZooKeeper 详解 SOA 案例</name>

    <!-- Spring Boot 启动父依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>

    <properties>
        <dubbo-spring-boot>1.0.0</dubbo-spring-boot>
    </properties>

    <dependencies>

        <!-- Spring Boot Dubbo 依赖 -->
        <dependency>
            <groupId>io.dubbo.springboot</groupId>
            <artifactId>spring-boot-starter-dubbo</artifactId>
            <version>${dubbo-spring-boot}</version>
        </dependency>

        <!-- Spring Boot Web 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Boot Test 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- Junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>
b.application.properties 配置

## Dubbo 服务提供者配置
spring.dubbo.application.name=provider
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20880
spring.dubbo.scan=org.spring.springboot.dubbo
这里 ZK 配置的地址和端口,就是上面本机搭建的 ZK 。如果有自己的 ZK 可以修改下面的配置。配置解释如下:
spring.dubbo.application.name 应用名称
spring.dubbo.registry.address 注册中心地址
spring.dubbo.protocol.name 协议名称
spring.dubbo.protocol.port 协议端口
spring.dubbo.scan dubbo 服务类包目录
c.CityDubboServiceImpl.java 城市业务 Dubbo 服务层实现层类
// 注册为 Dubbo 服务
@Service(version = "1.0.0")
public class CityDubboServiceImpl implements CityDubboService {

    public City findCityByName(String cityName) {
        return new City(1L,2L,"温岭","是我的故乡");
    }
}
@Service 注解标识为 Dubbo 服务,并通过 version 指定了版本号。
d.City.java 城市实体类
实体类通过 Dubbo 服务之间 RPC 调用,则需要实现序列化接口。最好指定下 serialVersionUID 值。

 

2.详解 springboot-dubbo-client Dubbo 服务消费者工程
springboot-dubbo-client 工程目录结构
├── pom.xml
└── src
    └── main
        ├── java
        │   └── org
        │       └── spring
        │           └── springboot
        │               ├── ClientApplication.java
        │               ├── domain
        │               │   └── City.java
        │               └── dubbo
        │                   ├── CityDubboConsumerService.java
        │                   └── CityDubboService.java
        └── resources
            └── application.properties
pom.xml 、 CityDubboService.java、City.java 没有改动。Dubbo 消费者通过引入接口实现 Dubbo 接口的调用。

 

a.application.properties 配置
## 避免和 server 工程端口冲突
server.port=8081

## Dubbo 服务消费者配置
spring.dubbo.application.name=consumer
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181
spring.dubbo.scan=org.spring.springboot.dubbo
因为 springboot-dubbo-server 工程启动占用了 8080 端口,所以这边设置端口为 8081。

 

b.CityDubboConsumerService.java 城市 Dubbo 服务消费者
@Component
public class CityDubboConsumerService {

    @Reference(version = "1.0.0")
    CityDubboService cityDubboService;

    public void printCity() {
        String cityName="温岭";
        City city = cityDubboService.findCityByName(cityName);
        System.out.println(city.toString());
    }
}
@Reference(version = “1.0.0”) 通过该注解,订阅该接口版本为 1.0.0 的 Dubbo 服务。
这里将 CityDubboConsumerService 注入 Spring 容器,是为了更方便的获取该 Bean,然后验证这个 Dubbo 调用是否成功。

 

c.ClientApplication.java 客户端启动类
@SpringBootApplication
public class ClientApplication {

    public static void main(String[] args) {
        // 程序启动入口
        // 启动嵌入式的 Tomcat 并初始化 Spring 环境及其各 Spring 组件
        ConfigurableApplicationContext run = SpringApplication.run(ClientApplication.class, args);
        CityDubboConsumerService cityService = run.getBean(CityDubboConsumerService.class);
        cityService.printCity();
    }
}
解释下这段逻辑,就是启动后从 Bean 容器中获取城市 Dubbo 服务消费者 Bean。然后调用该 Bean 方法去验证 Dubbo 调用是否成功。

 

四、小结

还有涉及到服务的监控,治理。这本质上和 SpringBoot 无关,所以这边不做一一介绍。感谢阿里 teaey 提供的 starter-dubbo 项目。

 

欢迎扫一扫我的公众号关注 — 及时得到博客订阅哦!
— http://www.bysocket.com/ —
— https://github.com/JeffLi1993 —
Posted in Spring, Spring Boot, 技术

Spring Boot 集成 FreeMarker 详解案例

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!
“年轻就不应该让自己过得太舒服” –  From yong

一、Springboot 那些事

SpringBoot 很方便的集成 FreeMarker ,DAO 数据库操作层依旧用的是 Mybatis,本文将会一步一步到来如何集成 FreeMarker 以及配置的详解:
Springboot 那些事:
系类文章:

二、运行 springboot-freemarker 工程

git clone 下载工程 springboot-learning-example ,项目地址见 GitHub – https://github.com/JeffLi1993/springboot-learning-example。下面开始运行工程步骤(Quick Start):
1.数据库准备

a.创建数据库 springbootdb:

CREATE DATABASE springbootdb;
b.创建表 city :(因为我喜欢徒步)
DROP TABLE IF EXISTS  `city`;
CREATE TABLE `city` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '城市编号',
  `province_id` int(10) unsigned  NOT NULL COMMENT '省份编号',
  `city_name` varchar(25) DEFAULT NULL COMMENT '城市名称',
  `description` varchar(25) DEFAULT NULL COMMENT '描述',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

c.插入数据

INSERT city VALUES (1 ,1,'温岭市','BYSocket 的家在温岭。');
2. 项目结构介绍
项目结构如下图所示:
org.spring.springboot.controller – Controller 层
org.spring.springboot.dao – 数据操作层 DAO
org.spring.springboot.domain – 实体类
org.spring.springboot.service – 业务逻辑层
Application – 应用启动类
resources/application.properties – 应用配置文件,应用启动会自动读取配置
resources/web – *.ftl文件,是 FreeMarker 文件配置路径。在 application.properties 配置
resources/mapper – DAO Maper XML 文件
3.改数据库配置
打开 application.properties 文件, 修改相应的数据源配置,比如数据源地址、账号、密码等。(如果不是用 MySQL,pom 自行添加连接驱动依赖,然后修改驱动名配置。)
4.编译工程
在项目根目录 springboot-learning-example,运行 maven 指令:
mvn clean install
5.运行工程
右键运行 springboot-freemarker 工程 Application 应用启动类的 main 函数,然后在浏览器访问:
获取 ID 编号为 1 的城市信息页面:
localhost:8080/api/city/1
获取城市列表页面:
localhost:8080/api/city
6.补充
运行环境:JDK 7 或 8,Maven 3.0+
技术栈:SpringBoot、Mybatis、FreeMarker

三、 springboot-freemarker 工程配置详解

1.pom.xml 依赖

pom.xml 代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>springboot</groupId>
    <artifactId>springboot-freemarker</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-freemarker :: Spring Boot 集成 FreeMarker 案例</name>

    <!-- Spring Boot 启动父依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>

    <properties>
        <mybatis-spring-boot>1.2.0</mybatis-spring-boot>
        <mysql-connector>5.1.39</mysql-connector>
    </properties>

    <dependencies>
        <!-- Spring Boot Freemarker 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

        <!-- Spring Boot Web 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Boot Test 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- Spring Boot Mybatis 依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis-spring-boot}</version>
        </dependency>

        <!-- MySQL 连接驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector}</version>
        </dependency>

        <!-- Junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>
在 pom.xml 依赖中增加 Spring Boot FreeMarker 依赖。
2.配置 FreeMarker

然后在 application.properties 中加入 FreeMarker 相关的配置:

## Freemarker 配置
## 文件配置路径
spring.freemarker.template-loader-path=classpath:/web/
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=true
spring.freemarker.expose-session-attributes=true
spring.freemarker.request-context-attribute=request
spring.freemarker.suffix=.ftl
这是我这块的配置,如果需要更多的 FreeMarker 配置,可以查看下面的详解:
spring.freemarker.allow-request-override=false # Set whether HttpServletRequest attributes are allowed to override (hide) controller generated model attributes of the same name.
spring.freemarker.allow-session-override=false # Set whether HttpSession attributes are allowed to override (hide) controller generated model attributes of the same name.
spring.freemarker.cache=false # Enable template caching.
spring.freemarker.charset=UTF-8 # Template encoding.
spring.freemarker.check-template-location=true # Check that the templates location exists.
spring.freemarker.content-type=text/html # Content-Type value.
spring.freemarker.enabled=true # Enable MVC view resolution for this technology.
spring.freemarker.expose-request-attributes=false # Set whether all request attributes should be added to the model prior to merging with the template.
spring.freemarker.expose-session-attributes=false # Set whether all HttpSession attributes should be added to the model prior to merging with the template.
spring.freemarker.expose-spring-macro-helpers=true # Set whether to expose a RequestContext for use by Spring's macro library, under the name "springMacroRequestContext".
spring.freemarker.prefer-file-system-access=true # Prefer file system access for template loading. File system access enables hot detection of template changes.
spring.freemarker.prefix= # Prefix that gets prepended to view names when building a URL.
spring.freemarker.request-context-attribute= # Name of the RequestContext attribute for all views.
spring.freemarker.settings.*= # Well-known FreeMarker keys which will be passed to FreeMarker's Configuration.
spring.freemarker.suffix= # Suffix that gets appended to view names when building a URL.
spring.freemarker.template-loader-path=classpath:/templates/ # Comma-separated list of template paths.
spring.freemarker.view-names= # White list of view names that can be resolved.
3.展示层 Controller 详解
/**
 * 城市 Controller 实现 Restful HTTP 服务
 * <p>
 * Created by bysocket on 07/02/2017.
 */
@Controller
public class CityController {

    @Autowired
    private CityService cityService;

    @RequestMapping(value = "/api/city/{id}", method = RequestMethod.GET)
    public String findOneCity(Model model, @PathVariable("id") Long id) {
        model.addAttribute("city", cityService.findCityById(id));
        return "city";
    }

    @RequestMapping(value = "/api/city", method = RequestMethod.GET)
    public String findAllCity(Model model) {
        List<City> cityList = cityService.findAllCity();
        model.addAttribute("cityList",cityList);
        return "cityList";
    }
}
a.这里不是走 HTTP + JSON 模式,使用了 @Controller 而不是先前的 @RestController
b.方法返回值是 String 类型,和 application.properties 配置的 Freemarker 文件配置路径下的各个 *.ftl 文件名一致。这样才会准确地把数据渲染到 ftl 文件里面进行展示。
c.用 Model 类,向 Model 加入数据,并指定在该数据在 Freemarker 取值指定的名称。

四、小结

FreeMarker 是常用的模板引擎,很多开发 Web 的必选。
推荐阅读《Springboot 那些事
欢迎扫一扫我的公众号关注 — 及时得到博客订阅哦!
— http://www.bysocket.com/ —
— https://github.com/JeffLi1993 —
Posted in 清文

由奢入俭的故事

上海一周有感


延误的飞机
新年新打算

黄鹤楼下黄鹤楼烟
桃花一粉又一白

黄鹤楼直对长江大桥
风水宝地

就这样
新年了
决定了

上海
来过好几次
破破的除了陆家嘴

由奢入俭的故事
在这个小城
发生着…

心态


心态所属于自己,并不是别人。就像每一次哭,都要笑着奔跑。声音很渺小,人也很渺小。
stay stupid,在宇宙中,在地球中,在中国,在上海,在… 想着我们很小,每天都是精彩的一天,都是那 24 小时,还得出去睡觉的时间。同样,保持学习,老话都这么说三人行,必有我师。
每天笑一笑,真的有效果。
为每天能挣到比昨天更多的钱而开心,为每天工作同事带来的开心,战斗的开心,为锻炼,为做没有回报但有意义的事儿开心。保持正能量,对自己也是种负责。

锻炼


喜欢跑步,喜欢跑步不带手机。同样喜欢走不同的路,走不同的地方,看不同的风景。跑步那半小时,一小时不带手机,不带手机让自己活得更真实,不带手机让我更有时间去想想自己,想想让自己怎么活自己。

工作


开心第一。干的爽第一。马云说的意思。
dont make me think 。没有猜测,一起做喜欢的事情。这是jobs说的。
还有几句话,我一直记得《超级阿嬷的信》:

★人只要有一项长处就可以。跑得快,会打棒球,性格开朗,不论是什么,只要有一点强过别人就可以。

★不能丢弃梦想。就算不能实现,梦想终归是梦想。如果这个梦想没有实现,可以再树立下一个梦想。这样,人到死都能拥有众多的梦想。人生就是这样的反反复复。
★如果不知道自己想干什么,就先工作。只要工作,就可以得到米、酱、酱油、朋友和信任。可以一边工作,一边寻找真正想干的事,千万不要游手好闲。
好了,洗碗去了。
Posted in Working Skills, 技术

Angular JS 生成动态二维码

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!
“小步快跑,试错迭代” – 《腾讯传》

一、场景

二维码的场景,很多。这里是二维码一种小场景,比如分享一个链接,商品链接,项目链接,优惠券链接…
技术实现,如果用后端实现,需要构造输出一个图片流。或者后端生产二维码图片,给图片地址就好了。弊端,这个二维码就是一个链接,后端的文件 IO 操作,还得考虑存储。太费力。
如果前端实现,这样就很轻松了。这只是个分享二维码,分享出去给人家扫一扫。利用前端的 canvas,这里坐下调研。
jq 封装的 qrcode.js ,文章网上一大堆。
angular js :https://github.com/monospaced/angular-qrcode

二、使用

1.安装 angular-qrcode

git clone https://github.com/monospaced/angular-qrcode.git
cd angular-qrcode
npm install

2.引入 js 文件

<script src="/node_modules/qrcode-generator/js/qrcode.js"></script>
<script src="/node_modules/qrcode-generator/js/qrcode_UTF8.js"></script>
<script src="/node_modules/angular-qrcode/angular-qrcode.js"></script>

并在你 angular 配置中加入对这个模块的依赖:

angular
.module('your-module', [
'monospaced.qrcode',
]);

 

3.使用

在线案例:monospaced.github.io/angular-qrcode

使用元素:

<qrcode data="string"></qrcode>

具体配置参数:

<qrcode data="string" version="2" error-correction-level="Q" size="200" color="#fff" ba kground="#000"></qrcode>

 

作为可下载的图片:

<qrcode data="string" download></qrcode>

 

作为有链接的二维码:

<qrcode data="http://example.com" href="http://example.com"></qrcode>

 

download 和 href 互斥,不能同时使用。具体参数入下:

<qrcode version="{{version}}" error-correction-level="{{level}}" size="{{size}}" data="{{var}}" href="{{var}}" color="{{color}}" background="{{background}}" download></qrcode>

 

三、小结

二维码是个好东西。

推荐:《 Springboot 系列

欢迎扫一扫我的公众号关注 — 及时得到博客订阅哦!
— http://www.bysocket.com/ —
— https://github.com/JeffLi1993 —

Posted in Spring, Spring Boot, 技术

Springboot 实现 Restful 服务,基于 HTTP / JSON 传输

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!
“怎样的人生才是没有遗憾的人生?我的体会是:
(1)拥有健康;
(2)创造“难忘时刻”;
(3)尽力做好自己,不必改变世界;
(4)活在当下。”
– 《向死而生》李开复
Spring Boot 系列文章:《Spring Boot 那些事
基于上一篇《Springboot 整合 Mybatis 的完整 Web 案例》,这边我们着重在 控制层 讲讲。讲讲如何在 Springboot 实现 Restful 服务,基于 HTTP / JSON 传输。

一、运行 springboot-restful 工程

git clone 下载工程 springboot-learning-example ,项目地址见 GitHub – https://github.com/JeffLi1993/springboot-learning-example。下面开始运行工程步骤(Quick Start):

 

1.数据库准备
a.创建数据库 springbootdb:
CREATE DATABASE springbootdb;
b.创建表 city :(因为我喜欢徒步)
DROP TABLE IF EXISTS  `city`;
CREATE TABLE `city` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '城市编号',
  `province_id` int(10) unsigned  NOT NULL COMMENT '省份编号',
  `city_name` varchar(25) DEFAULT NULL COMMENT '城市名称',
  `description` varchar(25) DEFAULT NULL COMMENT '描述',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
c.插入数据
INSERT city VALUES (1 ,1,'温岭市','BYSocket 的家在温岭。');

 

2. springboot-restful 工程项目结构介绍
springboot-restful 工程项目结构如下图所示:
org.spring.springboot.controller – Controller 层
org.spring.springboot.dao – 数据操作层 DAO
org.spring.springboot.domain – 实体类
org.spring.springboot.service – 业务逻辑层
Application – 应用启动类
application.properties – 应用配置文件,应用启动会自动读取配置

 

3.改数据库配置
打开 application.properties 文件, 修改相应的数据源配置,比如数据源地址、账号、密码等。(如果不是用 MySQL,自行添加连接驱动 pom,然后修改驱动名配置。)

 

4.编译工程
在项目根目录 springboot-learning-example,运行 maven 指令:
mvn clean install

 

5.运行工程
右键运行 springboot-restful 工程 Application 应用启动类的 main 函数。
用 postman 工具可以如下操作,
根据 ID,获取城市信息
GET http://127.0.0.1:8080/api/city/1
获取城市列表
GET http://127.0.0.1:8080/api/city
新增城市信息
POST http://127.0.0.1:8080/api/city
更新城市信息
PUT http://127.0.0.1:8080/api/city
删除城市信息
DELETE http://127.0.0.1:8080/api/city/2

 

二、springboot-restful 工程控制层实现详解

1.什么是 REST?
REST 是属于 WEB 自身的一种架构风格,是在 HTTP 1.1 规范下实现的。Representational State Transfer 全称翻译为表现层状态转化。Resource:资源。比如 newsfeed;Representational:表现形式,比如用JSON,富文本等;State Transfer:状态变化。通过HTTP 动作实现。
理解 REST ,要明白五个关键要素:
资源(Resource)
资源的表述(Representation)
状态转移(State Transfer)
统一接口(Uniform Interface)
超文本驱动(Hypertext Driven)

 

6 个主要特性:
面向资源(Resource Oriented)
可寻址(Addressability)
连通性(Connectedness)
无状态(Statelessness)
统一接口(Uniform Interface)
超文本驱动(Hypertext Driven)

 

2.Spring 对 REST 支持实现
CityRestController.java 城市 Controller 实现 Restful HTTP 服务
public class CityRestController {

    @Autowired
    private CityService cityService;

    @RequestMapping(value = "/api/city/{id}", method = RequestMethod.GET)
    public City findOneCity(@PathVariable("id") Long id) {
        return cityService.findCityById(id);
    }

    @RequestMapping(value = "/api/city", method = RequestMethod.GET)
    public List<City> findAllCity() {
        return cityService.findAllCity();
    }

    @RequestMapping(value = "/api/city", method = RequestMethod.POST)
    public void createCity(@RequestBody City city) {
        cityService.saveCity(city);
    }

    @RequestMapping(value = "/api/city", method = RequestMethod.PUT)
    public void modifyCity(@RequestBody City city) {
        cityService.updateCity(city);
    }

    @RequestMapping(value = "/api/city/{id}", method = RequestMethod.DELETE)
    public void modifyCity(@PathVariable("id") Long id) {
        cityService.deleteCity(id);
    }
}

 

代码详解:
@RequestMapping 处理请求地址映射。
method – 指定请求的方法类型:POST/GET/DELETE/PUT 等
value – 指定实际的请求地址
consumes – 指定处理请求的提交内容类型,例如 Content-Type 头部设置application/json, text/html
produces – 指定返回的内容类型
@PathVariable URL 映射时,用于绑定请求参数到方法参数
@RequestBody 这里注解用于读取请求体 boy 的数据,通过 HttpMessageConverter 解析绑定到对象中

 

3.HTTP 知识补充
GET            请求获取Request-URI所标识的资源
POST          在Request-URI所标识的资源后附加新的数据
HEAD         请求获取由Request-URI所标识的资源的响应消息报头
PUT            请求服务器存储一个资源,并用Request-URI作为其标识
DELETE       请求服务器删除Request-URI所标识的资源
TRACE        请求服务器回送收到的请求信息,主要用于测试或诊断
CONNECT  保留将来使用
OPTIONS   请求查询服务器的性能,或者查询与资源相关的选项和需求

 

三、小结

Springboot 实现 Restful 服务,基于 HTTP / JSON 传输,适用于前后端分离。这只是个小demo,没有加入bean validation这种校验。还有各种业务场景。
欢迎扫一扫我的公众号关注 — 及时得到博客订阅哦!
— http://www.bysocket.com/ —
— https://github.com/JeffLi1993 —
Posted in 思考

死亡学分一起修吧

听着赵雷的《理想》,写写看完《向死而生》的摘记与思考,就这样《活着活着就老了》。
【死亡学分篇】书中说:“人在面对疾病、死亡、悲伤等重大失落时,会产生“五个阶段”的心理反应─否认、愤怒、讨价还价、沮丧和接受。”
一般人想着能长生不老就好,往往面对死亡、疾病会更加深有体会。但却在努力为了钱,不开心压抑着活着的努力,渴望有些回报。往往回报不大,还要为讨价还价操劳过度。身体小事,都是大事。注意休息,注意心理生理健康。


书中说:“怎样的人生才是没有遗憾的人生?我的体会是:(1)拥有健康;(2)创造“难忘时刻”;(3)尽力做好自己,不必改变世界;(4)活在当下。”
第一点,爱运动不是第一,饮食作息才是通病。吃点素的,早点关机睡觉。第二点,放下面子脸皮,厚脸皮是好事。第三点,做自己,也要听去别人爸妈建议。但听心声,总错不了。

第四点,废话咯。乔布斯曾说过:“记住你即将死去。”


【人生规划篇】书中说:“我一直笃信“付出总有回报”,出差的时候吩咐秘书尽量选夜间航班,下了飞机可以立即洽谈公事。”
想起友人tam说过的:我也相信努力,相信努力总比不努力好我相信命的意思是,我相信 有些东西不是努力可以解决的

甚至不是你可以解决的

困难在于,你无法知道哪些东西努力可以解决,哪些不可以

所以,为了不错过那些努力可以解决的东西,你最好还是努力点


书中说:“做自己感兴趣的工作是最重要的。”
人生规划,免不了职业。职业和事业的区别就不说了。如果在职业中,一定要保持小小的事业心。你努力职业中获取,股权期权分红等,慢慢的升职成为了事业。所以当不了老板,低风险低收益的保持事业心的干事,会有属于你的舞台。


书中说:“不要因为年轻,只顾事业而忘了家庭”
这是真的,你现在有个家。你结婚了有了新家。你老了你儿子有了个家。你在上一个家,到下一个家,不要忘记家。他们理解你,他们也需要你。他们不需要你多少时间全部的关心,但他们需要重视。所以抽10分钟,认真视频,认真电话。回到家陪他们下厨,聊天吃饭,放下手机吧。


书中说:“饮食、运动、睡眠、减压,结果就是好吃、好玩、好睡、好轻松。”
这是生活,说白了就是心态。自私点,对自己好点。

最后

你若盛开,蝴蝶自来;你若精彩,天自安排。

最好的赞赏

就是你的关注

Posted in Spring, Spring Boot, 技术

Springboot 整合 Mybatis 的完整 Web 案例

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!
推荐一本书《腾讯传》。
Spring Boot 系列文章:《Spring Boot 那些事
新年第一篇 Springboot 技术文诞生。泥瓦匠准备写写 Springboot 相关最佳实践。一方面总结下一些 Springboot 相关,一方面和大家交流交流 Springboot 框架。
现在业界互联网流行的数据操作层框架 Mybatis,下面详解下 Springboot 如何整合 Mybatis ,这边没有使用 Mybatis Annotation 这种,是使用 xml 配置 SQL。因为我觉得 SQL 和业务代码应该隔离,方便和 DBA 校对 SQL。二者 XML 对较长的 SQL 比较清晰。

一、运行 springboot-mybatis 工程

git clone 下载工程 springboot-learning-example ,项目地址见 GitHub。下面开始运行工程步骤(Quick Start):
1.数据库准备
a.创建数据库 springbootdb:
CREATE DATABASE springbootdb;
b.创建表 city :(因为我喜欢徒步)
DROP TABLE IF EXISTS  `city`;
CREATE TABLE `city` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '城市编号',
  `province_id` int(10) unsigned  NOT NULL COMMENT '省份编号',
  `city_name` varchar(25) DEFAULT NULL COMMENT '城市名称',
  `description` varchar(25) DEFAULT NULL COMMENT '描述',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
c.插入数据
INSERT city VALUES (1 ,1,'温岭市','BYSocket 的家在温岭。');
2. 项目结构介绍
项目结构如下图所示:
org.spring.springboot.controller – Controller 层
org.spring.springboot.dao – 数据操作层 DAO
org.spring.springboot.domain – 实体类
org.spring.springboot.service – 业务逻辑层
Application – 应用启动类
application.properties – 应用配置文件,应用启动会自动读取配置
3.改数据库配置
打开 application.properties 文件, 修改相应的数据源配置,比如数据源地址、账号、密码等。(如果不是用 MySQL,自行添加连接驱动 pom,然后修改驱动名配置。)
4.编译工程
在项目根目录 springboot-learning-example,运行 maven 指令:
mvn clean install
5.运行工程

右键运行 Application 应用启动类的 main 函数,然后在浏览器访问:

http://localhost:8080/api/city?cityName=温岭市
可以看到返回的 JSON 结果:
{
    "id": 1,
    "provinceId": 1,
    "cityName": "温岭市",
    "description": "我的家在温岭。"
}
如图:

二、springboot-mybatis 工程配置详解

1.pom 添加 Mybatis 依赖
<!-- Spring Boot Mybatis 依赖 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>${mybatis-spring-boot}</version>
</dependency>
mybatis-spring-boot-starter 工程依赖如图:
整个工程的 pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>springboot</groupId>
    <artifactId>springboot-mybatis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-mybatis :: 整合 Mybatis Demo</name>

    <!-- Spring Boot 启动父依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>

    <properties>
        <mybatis-spring-boot>1.2.0</mybatis-spring-boot>
        <mysql-connector>5.1.39</mysql-connector>
    </properties>

    <dependencies>

        <!-- Spring Boot Web 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Boot Test 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- Spring Boot Mybatis 依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis-spring-boot}</version>
        </dependency>

        <!-- MySQL 连接驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector}</version>
        </dependency>

        <!-- Junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>
2.在 application.properties 应用配置文件,增加 Mybatis 相关配置
## Mybatis 配置
mybatis.typeAliasesPackage=org.spring.springboot.domain
mybatis.mapperLocations=classpath:mapper/*.xml
mybatis.typeAliasesPackage 配置为 org.spring.springboot.domain,指向实体类包路径。mybatis.mapperLocations 配置为 classpath 路径下 mapper 包下,* 代表会扫描所有 xml 文件。
mybatis 其他配置相关详解如下:
mybatis.config = mybatis 配置文件名称
mybatis.mapperLocations = mapper xml 文件地址
mybatis.typeAliasesPackage = 实体类包路径
mybatis.typeHandlersPackage = type handlers 处理器包路径
mybatis.check-config-location = 检查 mybatis 配置是否存在,一般命名为 mybatis-config.xml
mybatis.executorType = 执行模式。默认是 SIMPLE
3.在 Application 应用启动类添加注解 MapperScan
Application.java 代码如下:
/**
 * Spring Boot 应用启动类
 *
 * Created by bysocket on 16/4/26.
 */
// Spring Boot 应用的标识
@SpringBootApplication
// mapper 接口类扫描包配置
@MapperScan("org.spring.springboot.dao")
public class Application {

    public static void main(String[] args) {
        // 程序启动入口
        // 启动嵌入式的 Tomcat 并初始化 Spring 环境及其各 Spring 组件
        SpringApplication.run(Application.class,args);
    }
}
mapper 接口类扫描包配置注解 MapperScan :用这个注解可以注册 Mybatis mapper 接口类。
4.添加相应的 City domain类、CityDao mapper接口类

City.java:

/**
 * 城市实体类
 *
 * Created by bysocket on 07/02/2017.
 */
public class City {

    /**
     * 城市编号
     */
    private Long id;

    /**
     * 省份编号
     */
    private Long provinceId;

    /**
     * 城市名称
     */
    private String cityName;

    /**
     * 描述
     */
    private String description;

    public Long getId() {
        return id;
    }

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

    public Long getProvinceId() {
        return provinceId;
    }

    public void setProvinceId(Long provinceId) {
        this.provinceId = provinceId;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        this.cityName = cityName;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}
CityDao.java:
/**
 * 城市 DAO 接口类
 *
 * Created by bysocket on 07/02/2017.
 */
public interface CityDao {

    /**
     * 根据城市名称,查询城市信息
     *
     * @param cityName 城市名
     */
    City findByName(@Param("cityName") String cityName);
}
其他不明白的,可以 git clone 下载工程 springboot-learning-example ,工程代码注解很详细。 https://github.com/JeffLi1993/springboot-learning-example

三、其他

利用 Mybatis-generator自动生成代码 http://www.cnblogs.com/yjmyzz/p/4210554.html
Mybatis 通用 Mapper3 https://github.com/abel533/Mapper
Mybatis 分页插件 PageHelper https://github.com/pagehelper/Mybatis-PageHelper
最后,推荐阅读:《 Spring Boot 之 HelloWorld 详解
欢迎扫一扫我的公众号关注 — 及时得到博客订阅哦!
— http://www.bysocket.com/ —
— https://github.com/JeffLi1993 —