Spirng boot笔记

简介

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

Spring boot的特点

1. 创建独立的Spring应用程序

2. 嵌入的Tomcat,无需部署WAR文件

3. 简化Maven配置

4. 自动配置Spring

5. 提供生产就绪型功能,如指标,健康检查和外部配置

6. 绝对没有代码生成和对XML没有要求配置

Spring boot的优点

spring boot 可以支持你快速的开发出 restful 风格的微服务架构

自动化确实方便,做微服务再合适不过了,单一jar包部署和管理都非常方便。只要系统架构设计合理,大型项目也能用,加上nginx负载均衡,轻松实现横向扩展

spring boot 要解决的问题, 精简配置是一方面, 另外一方面是如何方便的让spring生态圈和其他工具链整合(比如redis, email, elasticsearch)

入门

环境搭建

创建maven项目

添加依赖

创建目录和配置文件

创建 src/main/resources 源文件目录,并在该目录下创建 application.properties 文件、static 和 templates 的文件夹。

application.properties:用于配置项目运行所需的配置数据。

static:用于存放静态资源,如:css、js、图片等。

templates:用于存放模板文件。

创建启动类

案例演示

创建 com.guige.springbootdemo.controller 包,在该包下创建一个 Controller 类,如下:

在 SpringbootApplication 文件中右键 Run as -> Java Application。当看到 "Tomcat started on port(s): 8080 (http)" 字样说明启动成功。

打开浏览器访问 http://localhost:8080/helloworld,结果如下:

热部署

当我们修改文件和创建文件时,都需要重新启动项目。这样频繁的操作很浪费时间,配置热部署可以让项目自动加载变化的文件,省去的手动操作。

在 pom.xml 文件中添加如下配置:

配置好 pom.xml 文件后,我们启动项目,随便创建/修改一个文件并保存,会发现控制台打印 springboot 重新加载文件的信息。

附加:

如果使用tk/mybatis 在resources目录创建META-INF文件夹

创建spring-devtools.properties文件

增加restart.include.companycommonlibs=tk/mybatis.*

多环境切换

application.properties 是 springboot 在运行中所需要的配置信息。

当我们在开发阶段,使用自己的机器开发,测试的时候需要用的测试服务器测试,上线时使用正式环境的服务器。

这三种环境需要的配置信息都不一样,当我们切换环境运行项目时,需要手动的修改多出配置信息,非常容易出错。

为了解决上述问题,springboot 提供多环境配置的机制,让开发者非常容易的根据需求而切换不同的配置环境。

在 src/main/resources 目录下创建三个配置文件:

application-dev.properties:用于开发环境

application-test.properties:用于测试环境

application-prod.properties:用于生产环境

我们可以在这个三个配置文件中设置不同的信息,application.properties 配置公共的信息。

在 application.properties 中配置:

spring.profiles.active=dev

表示激活 application-dev.properties 文件配置, springboot 会加载使用 application.properties 和 application-dev.properties 配置文件的信息。

同理,可将 spring.profiles.active 的值修改成 test 或 prod 达到切换环境的目的。

配置日志

配置 logback(官方推荐使用)

spring boot 默认会加载 classpath:logback-spring.xml 或者 classpath:logback-spring.groovy。

如需要自定义文件名称,在 application.properties 中配置 logging.config 选项即可。

在 src/main/resources 下创建 logback-spring.xml 文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

<!-- 文件输出格式 -->

<property name="PATTERN" value="%-12(%d{yyyy-MM-dd HH:mm:ss.SSS}) |-%-5level [%thread] %c [%L] -| %msg%n" />

<!-- test文件路径 -->

<property name="TEST_FILE_PATH" value="d:/test.log" />

<!-- pro文件路径 -->

<property name="PRO_FILE_PATH" value="/opt/test/log" />

<!-- 开发环境 -->

<springProfile name="dev">

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">

<encoder>

<pattern>${PATTERN}</pattern>

</encoder>

</appender>

<logger name="com.guige" level="debug" />

<root level="info">

<appender-ref ref="CONSOLE" />

</root>

</springProfile>

<!-- 测试环境 -->

<springProfile name="test">

<!-- 每天产生一个文件 -->

<appender name="TEST-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">

<!-- 文件路径 -->

<file>${TEST_FILE_PATH}</file>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<!-- 文件名称 -->

<fileNamePattern>${TEST_FILE_PATH}/info.%d{yyyy-MM-dd}.log</fileNamePattern>

<!-- 文件最大保存历史数量 -->

<MaxHistory>100</MaxHistory>

</rollingPolicy>

<layout class="ch.qos.logback.classic.PatternLayout">

<pattern>${PATTERN}</pattern>

</layout>

</appender>

<root level="info">

<appender-ref ref="TEST-FILE" />

</root>

</springProfile>

<!-- 生产环境 -->

<springProfile name="prod">

<appender name="PROD_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">

<file>${PRO_FILE_PATH}</file>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<fileNamePattern>${PRO_FILE_PATH}/warn.%d{yyyy-MM-dd}.log</fileNamePattern>

<MaxHistory>100</MaxHistory>

</rollingPolicy>

<layout class="ch.qos.logback.classic.PatternLayout">

<pattern>${PATTERN}</pattern>

</layout>

</appender>

<root level="warn">

<appender-ref ref="PROD_FILE" />

</root>

</springProfile>

</configuration>

其中,springProfile 标签的 name 属性对应 application.properties 中的 spring.profiles.active 的配置。

即 spring.profiles.active 的值可以看作是日志配置文件中对应的 springProfile 是否生效的开关。

配置 log4j2

添加依赖

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-log4j2</artifactId>

</dependency>

配置日志文件

spring boot 默认会加载 classpath:log4j2.xml 或者 classpath:log4j2-spring.xml。

如需要自定义文件名称,在 application.properties 中配置 logging.config 选项即可。

log4j2.xml 文件内容如下:

<?xml version="1.0" encoding="utf-8"?>

<configuration>

<properties>

<!-- 文件输出格式 -->

<property name="PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%thread] %c [%L] -| %msg%n</property>

</properties>

<appenders>

<Console name="CONSOLE" target="system_out">

<PatternLayout pattern="${PATTERN}" />

</Console>

</appenders>

<loggers>

<logger name="com.light.springboot" level="debug" />

<root level="info">

<appenderref ref="CONSOLE" />

</root>

</loggers>

</configuration>

log4j2 不能像 logback 那样在一个文件中设置多个环境的配置数据,只能命名 3 个不同名的日志文件,分别在 application-dev,application-test 和 application-prod 中配置 logging.config 选项。

除了在日志配置文件中设置参数之外,还可以在 application-*.properties 中设置,日志相关的配置:

logging.config # 日志配置文件路径,如 classpath:logback-spring.xml

logging.exception-conversion-word # 记录异常时使用的转换词

logging.file # 记录日志的文件名称,如:test.log

logging.level.* # 日志映射,如:logging.level.root=WARN,logging.level.org.springframework.web=DEBUG

logging.path # 记录日志的文件路径,如:d:/

logging.pattern.console # 向控制台输出的日志格式,只支持默认的 logback 设置。

logging.pattern.file # 向记录日志文件输出的日志格式,只支持默认的 logback 设置。

logging.pattern.level # 用于呈现日志级别的格式,只支持默认的 logback 设置。

logging.register-shutdown-hook # 初始化时为日志系统注册一个关闭钩子

打包运行

打包的形式有两种:jar 和 war。

默认情况下,通过 maven 执行 package 命令后,会生成 jar 包,且该 jar 包会内置了 tomcat 容器,因此我们可以通过 java -jar 就可以运行项目

打包成部署的war

打包成功后,将 war 包部署到 tomcat 容器中运行即可。

Web配置

整合Freemarker

由于 jsp 不被 SpringBoot 推荐使用,所以模板引擎主要介绍 Freemarker 和 Thymeleaf。

添加 Freemarker 依赖

<!-- 添加 Freemarker 依赖 -->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-freemarker</artifactId>

</dependency>

添加 Freemarker 模板配置

在 application.properties 中添加如下内容:

spring.freemarker.allow-request-override=false

spring.freemarker.cache=true

spring.freemarker.check-template-location=true

spring.freemarker.charset=UTF-8

spring.freemarker.content-type=text/html

spring.freemarker.expose-request-attributes=false

spring.freemarker.expose-session-attributes=false

spring.freemarker.expose-spring-macro-helpers=false

spring.freemarker.prefix=

spring.freemarker.suffix=.ftl

上述配置都是默认值。

Freemarker 案例演示

在 controller 包中创建 FreemarkerController:

在 templates 目录中创建名为 hello.ftl 文件,内容如下:

结果如下:

整合Thymeleaf

添加 Thymeleaf 依赖

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-thymeleaf</artifactId>

</dependency>

在 application.properties 中添加如下内容:

spring.thymeleaf.cache=true

spring.thymeleaf.prefix=classpath:/templates/

spring.thymeleaf.suffix=.html

spring.thymeleaf.mode=HTML5

spring.thymeleaf.encoding=UTF-8

spring.thymeleaf.content-type=text/html

上述配置都是默认值。

Thymeleaf 案例演示

在 controller 包中创建 ThymeleafController:

@Controller

@RequestMapping("thymeleaf")

public class ThymeleafController {

@RequestMapping("hello")

public String hello(Map<String,Object> map) {

map.put("msg", "Hello Thymeleaf");

return "hello";

}

}

在 template 目录下创建名为 hello.html 的文件,内容如下:

<!DOCTYPE html>

<html lang="zh">

<head>

<meta charset="UTF-8">

<title>Document</title>

</head>

<body>

<div class="container">

<h2 th:text="${msg}"></h2>

</div>

</body>

</html>

结果如下:


整合jsp

src/main/下创建web资源文件夹,webapp,并设置为资源文件。 

application.properties文件设置如下

#jsp 支持

spring.mvc.view.suffix=.jsp

spring.mvc.view.prefix=/WEB-INF/jsp/

#关闭默认模板引擎

spring.thymeleaf.cache=false

spring.thymeleaf.enabled=false

maven配置jsp相关依赖。

<!--jsp支持-->

<!-- servlet 依赖. -->

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>javax.servlet-api</artifactId>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>jstl</artifactId>

</dependency>

<!-- tomcat 的支持.-->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-tomcat</artifactId>

</dependency>

<dependency>

<groupId>org.apache.tomcat.embed</groupId>

<artifactId>tomcat-embed-jasper</artifactId>

<scope>provided</scope>

</dependency>

创建ServletInitalizer集成SpringbootServletInitalizer,绑定自己添加了@SpringbootApplication类。

创建JspController

创建hello.jsp

效果如下

整合 Fastjson

添加依赖

<!-- fastjson -->

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>fastjson</artifactId>

<version>1.2.35</version>

</dependency>

创建一个配置管理类 WebConfig ,如下:

演示案例:

创建一个实体类 User:

创建控制器类 FastjsonController :

打开浏览器,访问 http://localhost:8080/fastjson/test,结果如下图:

此时,还不能看出 Fastjson 是否正常工作,我们在 User 类中使用 Fastjson 的注解,如下内容:

@JSONField(format="yyyy-MM-dd")

private Date birthday;

再次访问 http://localhost:8080/fastjson/test,结果如下图:

日期格式与我们修改的内容格式一致,说明 Fastjson 整合成功。

自定义 Servlet

编写 Servlet

将 Servelt 注册成 Bean。在上文创建的 WebConfig 类中添加如下代码:

结果如下:

自定义过滤器/第三方过滤器

编写过滤器

注册过滤器

要是该过滤器生效,有两种方式:

1) 使用 @Component 注解

2) 添加到过滤器链中,此方式适用于使用第三方的过滤器。将过滤器写到 WebConfig 类中,如下:

结果如下:

自定义监听器

编写监听器

注册监听器

注册监听器为 Bean,在 WebConfig 配置类中添加如下代码:


针对自定义 Servlet、Filter 和 Listener 的配置,还有另一种方式:




自定义拦截器

编写拦截器

使用 @Component 让 Spring 管理其生命周期:

注册拦截器

编写拦截器后,我们还需要将其注册到拦截器链中,如下配置:

请求一个 controller ,结果如下:

配置 AOP 切面

添加依赖

<!-- aop -->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-aop</artifactId>

</dependency>

编写切面类

请求 FastJsonController 控制器的方法,结果如下:

错误处理

友好页面

先演示非友好页面,修改 FastJsonController 类中的 test 方法:

当系统报错时,返回到页面的内容通常是一些杂乱的代码段,这种显示对用户来说不友好,因此我们需要自定义一个友好的提示系统异常的页面。

在 src/main/resources 下创建 /public/error,在该目录下再创建一个名为 5xx.html 文件,该页面的内容就是当系统报错时返回给用户浏览的内容:

<!DOCTYPE html>

<html lang="zh">

<head>

<meta charset="UTF-8">

<title>系统错误</title>

<link href="/css/index.css" rel="stylesheet"/>

</head>

<body>

<div class="container">

<h2>系统内部错误</h2>

</div>

</body>

</html>

路径时固定的,Spring Boot 会在系统报错时将返回视图指向该目录下的文件。

如下图:

上边处理的 5xx 状态码的问题,接下来解决 404 状态码的问题。

当出现 404 的情况时,用户浏览的页面也不够友好,因此我们也需要自定义一个友好的页面给用户展示。

在 /public/error 目录下再创建一个名为 404.html 的文件:

<!DOCTYPE html>

<html lang="zh">

<head>

<meta charset="UTF-8">

<title>访问异常</title>

<link href="/css/index.css" rel="stylesheet"/>

</head>

<body>

<div class="container">

<h2>找不到页面</h2>

</div>

</body>

</html>

我们请求一个不存在的资源,如:http://localhost:8080/fastjson/test2,结果如下图:

全局异常捕获

如果项目前后端是通过 JSON 进行数据通信,则当出现异常时可以常用如下方式处理异常信息。

编写一个类充当全局异常的处理类,需要使用 @ControllerAdvice 和 @ExceptionHandler 注解:

其中,方法名为任意名,入参一般使用 Exception 异常类,方法返回值可自定义。

启动项目,访问 http://localhost:8080/fastjson/test,结果如下图:

我们还可以自定义异常,在全局异常的处理类中捕获和判断,从而对不同的异常做出不同的处理。

文件上传和下载

添加依赖

<!-- 工具 -->

<dependency>

<groupId>commons-io</groupId>

<artifactId>commons-io</artifactId>

<version>2.4</version>

</dependency>

实现

public class FileInfo {

private String path;

public FileInfo(String path) {

this.path = path;

}

public String getPath() {

return path;

}

public void setPath(String path) {

this.path = path;

}

}

基本上都是在学习 javaweb 时用到的 API。

文件上传测试结果如下图:

CORS 支持

前端页面:

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<title>跨域测试</title>

</head>

<body>

<button id="test">测试</button>

<script type="text/javascript" src="jquery-1.12.3.min.js"></script>

<script type="text/javascript">

$(function() {

$("#test").on("click", function() {

$.ajax({

"url": "http://localhost:8080/fastjson/test",

"type": "get",

"dataType": "json",

"success": function(data) {

console.log(data);

}

})

});

});

</script>

</body>

</html>

通过 http 容器启动前端页面代码,笔者使用 Sublime Text 的插件启动的,测试结果如下:

从图中可知,前端服务器启动端口为 8088 与后端服务器 8080 不同源,因此出现跨域的问题。

现在开始解决跨域问题,可以两种维度控制客户端请求。

粗粒度控制:

方式一

@Configuration

public class WebConfig {

@Bean

public WebMvcConfigurer corsConfigurer() {

return new WebMvcConfigurerAdapter() {

@Override

public void addCorsMappings(CorsRegistry registry) {

registry.addMapping("/fastjson/**")

.allowedOrigins("http://localhost:8088");// 允许 8088 端口访问

}

};

}

}

方式二

@Configuration

public class WebConfig extends WebMvcConfigurerAdapter{

@Override

public void addCorsMappings(CorsRegistry registry) {

registry.addMapping("/fastjson/**")

.allowedOrigins("http://localhost:8088");// 允许 8088 端口访问

}

}

配置后,重新发送请求,结果如下:

细粒度控制:

在 FastJsonController 类中的方法上添加 @CrossOrigin(origins="xx") 注解:

在使用该注解时,需要注意 @RequestMapping 使用的请求方式类型,即 GET 或 POST。

整合 Swagger2

添加依赖

<dependency>

<groupId>io.springfox</groupId>

<artifactId>springfox-swagger2</artifactId>

<version>2.7.0</version>

</dependency>

<dependency>

<groupId>io.springfox</groupId>

<artifactId>springfox-swagger-ui</artifactId>

<version>2.7.0</version>

</dependency>

重新创建一个配置类,如下:

为了能更好的说明接口信息,我们还可以在 Controller 类上使用 Swagger2 相关注解说明信息。

我们以 FastJsonController 为例:

@Api(value = "FastJson测试", tags = { "测试接口" })

@RestController

@RequestMapping("fastjson")

public class FastJsonController {

@ApiOperation("获取用户信息")

@ApiImplicitParam(name = "name", value = "用户名", dataType = "string", paramType = "query")

@GetMapping("/test/{name}")

public User test(@PathVariable("name") String name) {

User user = new User();

user.setId(1);

user.setUsername(name);

user.setPassword("jack123");

user.setBirthday(new Date());

return user;

}

}

注意,上边的方法是用 @GetMapping 注解,如果只是使用 @RequestMapping 注解,不配置 method 属性,那么 API 文档会生成 7 种请求方式。

启动项目,打开浏览器访问 http://localhost:8080/swagger-ui.html。结果如下图:

持久层配置

整合 JdbcTemplate

添加依赖

<!-- jdbc依赖 -->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-jdbc</artifactId>

</dependency>

<!-- mysql 驱动包 -->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

</dependency>

配置数据库连接

在 application-dev.properties 中添加:

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/springbootdemo?useUnicode=true&characterEncoding=utf8

spring.datasource.username=root

spring.datasource.password=root

其中,可以不指定 driver-class-name,因为 spring boot 会自动识别 url。

测试

在 MySQL 中创建名为 springboot 的数据库,在该库中创建 user 表:

CREATE TABLE `user` (

`id` INT(11) NOT NULL AUTO_INCREMENT,

`username` VARCHAR(50) NOT NULL,

`password` VARCHAR(64) NOT NULL,

`birthday` DATE NOT NULL,

PRIMARY KEY (`id`)

)

COLLATE='utf8_general_ci'

ENGINE=InnoDB

AUTO_INCREMENT=3

;

建实体类

public class User implements Serializable{

private static final long serialVersionUID = -6249397911566315813L;

private Integer id;

private String username;

private String password;

private Date birthday;

}

setter 和 getter 方法此处省略。

dao 接口

接口和实现类如下:

public interface UserDao {

public int insert(User user);

public int deleteById(Integer id);

public int update(User user);

public User getById(Integer id);

}

@Repository

public class UserDaoImpl implements UserDao {

@Autowired

private JdbcTemplate jdbcTemplate;

@Override

public int insert(User user) {

String sql = "insert into user(id,username,password,birthday) values(?,?,?,?)";

return this.jdbcTemplate.update(

sql,

user.getId(),

user.getUsername(),

user.getPassword(),

user.getBirthday()

);

}

@Override

public int deleteById(Integer id) {

String sql = "delete from user where id = ?";

return this.jdbcTemplate.update(sql,id);

}

@Override

public int update(User user) {

String sql = "update user set password = ? where id = ?";

return this.jdbcTemplate.update(

sql,

user.getPassword(),

user.getId()

);

}

@Override

public User getById(Integer id) {

String sql = "select * from user where id = ?";

return this.jdbcTemplate.queryForObject(sql, new RowMapper<User>() {

@Override

public User mapRow(ResultSet rs, int rowNum) throws SQLException {

User user = new User();

user.setId(rs.getInt("id"));

user.setUsername(rs.getString("username"));

user.setPassword(rs.getString("password"));

user.setBirthday(rs.getDate("birthday"));

return user;

}

},id);

}

}

测试类

@RunWith(SpringRunner.class)

@SpringBootTest

public class UserDaoTest {

@Autowired

private UserDao userDao;

@Test

public void testInsert() {

User user = new User();

user.setId(1);

user.setUsername("张三");

user.setPassword("zhangsan");

user.setBirthday(new Date());

int result = this.userDao.insert(user);

System.out.println(result);

}

@Test

public void testGetById() {

User user = this.userDao.getById(1);

System.out.println(user.getUsername());

}

@Test

public void testUpdate() {

User user = new User();

user.setId(1);

user.setPassword("zhangsan123");

this.userDao.update(user);

}

@Test

public void testDeleteById() {

int result = this.userDao.deleteById(1);

System.out.println(result);

}

}

测试结果省略...

如需打印日志,在日志配置文件中添加如下配置:

<logger name="org.springframework.jdbc.core.JdbcTemplate" level="debug"/>

整合 Spring-data-jpa

添加依赖

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

</dependency>

配置数据库连接

####jdbc

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/springbootdemo?useUnicode=true&characterEncoding=utf8

spring.datasource.username=root

spring.datasource.password=root

# JPA

spring.jpa.hibernate.ddl-auto=update

spring.jpa.show-sql=true

建表

CREATE TABLE `role` (

`id` INT(11) NOT NULL AUTO_INCREMENT,

`name` VARCHAR(10) NOT NULL,

`descr` VARCHAR(100) NULL DEFAULT NULL,

PRIMARY KEY (`id`)

)

COLLATE='utf8_general_ci'

ENGINE=InnoDB

;

注意,主键 ID 为 AUTO_INCREMENT 自增。

建实体类

添加相应的注解

@Entity

public class Role implements Serializable{

private static final long serialVersionUID = 3926276668667517847L;

@Id

@GeneratedValue

private Integer id;

@Column

private String name;

@Column

private String descr;

}

setter 和 getter 方法此处省略。

Repository 接口

public interface RoleRepository extends JpaRepository<Role, Integer>{

}

测试类

@RunWith(SpringRunner.class)

@SpringBootTest

public class RoleRepositoryTest {

@Autowired

private RoleRepository roleRepository;

@Test

public void testInsert() {

Role role = new Role();

role.setName("管理员");

role.setDescr("测试");

Role result = this.roleRepository.save(role);

System.out.println(result);

}

@Test

public void testFindOne() {

Role role = this.roleRepository.findOne(1);

System.out.println(role);

}

@Test

public void testUpdate() {

Role role = new Role();

role.setId(1);

role.setName("管理员");

role.setDescr("控制权限");

Role result = this.roleRepository.save(role);

System.out.println(result);

}

@Test

public void testDelete() {

this.roleRepository.delete(1);

}

}

测试结果省略...

整合 Mybatis

整合 MyBatis 有两种方式:

1) 使用 mybatis 官方提供的 Spring Boot 整合包实现。

2) 使用 mybatis-spring 整合的方式,也就是传统的方式(推荐,此方式容易控制 MyBatis 的配置)。

配置依赖

方式一:

添加依赖:

<!-- mybatis -->

<dependency>

<groupId>org.mybatis.spring.boot</groupId>

<artifactId>mybatis-spring-boot-starter</artifactId>

<version>1.3.0</version>

</dependency>

<!-- mysql 驱动包 -->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

</dependency>

配置数据库连接:

在 application.properties 中添加:

# 数据源配置

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3380/springboot?useUnicode=true&characterEncoding=utf8

spring.datasource.username=root

spring.datasource.password=tiger

# mybatis 配置

mybatis.config-location=classpath:mybatis/mybatis-config.xml

mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

方式二:

添加依赖:

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis</artifactId>

<version>3.4.4</version>

</dependency>

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis-spring</artifactId>

<version>1.3.1</version>

</dependency>

创建配置类:

@Configuration

public class MyBatisConfiguration {

@Bean

@ConditionalOnMissingBean // 当容器里没有指定的 Bean 的情况下创建该对象

public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {

SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();

// 设置数据源

sqlSessionFactoryBean.setDataSource(dataSource);

// 设置mybatis的主配置文件

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

Resource mybatisConfigXml = resolver.getResource("classpath:mybatis/mybatis-config.xml");

sqlSessionFactoryBean.setConfigLocation(mybatisConfigXml);

// 设置mapper映射文件

Resource[] mapperXml;

try {

mapperXml = resolver.getResources("classpath:mybatis/mapper/*.xml");

sqlSessionFactoryBean.setMapperLocations(mapperXml);

} catch (IOException e) {

e.printStackTrace();

}

// 设置别名包

//sqlSessionFactoryBean.setTypeAliasesPackage("com.guige");

return sqlSessionFactoryBean;

}

@Bean

@ConditionalOnBean(SqlSessionFactoryBean.class) // 当 SqlSessionFactoryBean 实例存在时创建对象

public MapperScannerConfigurer mapperScannerConfigurer() {

MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();

mapperScannerConfigurer.setBasePackage("com.guige.springbootdemo.mapper");

return mapperScannerConfigurer;

}

}

此方式也需要在 applicaton.properties 配置数据库连接,当不需要在文件中配置 mybatis 相关参数。

以上便是两种方式的配置的不同之处。

在 src/main/resources 下创建 mybatis 文件夹,并在 mybatis 文件夹中创建 "mybatis-config.xml" 配置文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE configuration

PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

<typeAliases>

</typeAliases>

</configuration>

mybatis 文件夹下再创建一个 "mapper" 文件夹,里边存放 Mpper 接口对应的 mapper 映射文件。

测试

建表

在 MySQL 中创建名为 springboot 的数据库,在该库中创建 department表:

CREATE TABLE `department` (

`id` INT(11) NOT NULL,

`name` VARCHAR(10) NOT NULL,

`descr` VARCHAR(50) NULL DEFAULT NULL,

PRIMARY KEY (`id`)

)

ENGINE=InnoDB

;

实体类

public class Department implements Serializable{

private static final long serialVersionUID = 6067283535977178571L;

private Integer id;

private String name;

private String descr;

}

setet 和 getter 方法省略。

Mapper 接口

@Mapper

public interface DepartmentMapper {

public void insert(Department department);

public Department getById(Integer id);

public void update(Department department);

public void deleteById(Integer id);

}

mybatis/mapper/departmentMapper.xml :

<?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.guige.springbootdemo.mapper.DepartmentMapper">

<insert id="insert" parameterType="com.guige.springbootdemo.entity.Department">

insert into department(id,name,descr) values(#{id},#{name},#{descr})

</insert>

<select id="getById" parameterType="java.lang.Integer" resultType="com.guige.springbootdemo.entity.Department">

select id,name,descr from department where id = #{id}

</select>

<update id="update" parameterType="com.guige.springbootdemo.entity.Department">

update department set descr = #{descr} where id = #{id}

</update>

<delete id="deleteById" parameterType="java.lang.Integer">

delete from department where id = #{id}

</delete>

</mapper>

测试类

@RunWith(SpringRunner.class)

@SpringBootTest

public class DepartmentMapperTest {

@Autowired

private DepartmentMapper departmentMapper;

@Test

public void testInsert() {

Department department = new Department();

department.setId(1);

department.setName("研发部");

department.setDescr("开发产品");

this.departmentMapper.insert(department);

}

@Test

public void testGetById() {

Department department = this.departmentMapper.getById(1);

System.out.println(department);

}

@Test

public void testUpdate() {

Department department = new Department();

department.setId(1);

department.setDescr("开发高级产品");

this.departmentMapper.update(department);

}

@Test

public void testDeleteById() {

this.departmentMapper.deleteById(1);

}

}

Mybatis生成代码

在pom中build的插件中添加生成插件

<plugin>

<groupId>org.mybatis.generator</groupId>

<artifactId>mybatis-generator-maven-plugin</artifactId>

<version>1.3.2</version>

<configuration>

<configurationFile>src/main/resources/generator/generatorConfig.xml</configurationFile>

<overwrite>true</overwrite>

<verbose>true</verbose>

</configuration>

</plugin>

在resources下创建 generator文件夹

配置config.properties

datasource.url=jdbc:oracle:thin:@dbServer254:1521:guigedb1

datasource.username=mis

datasource.password=mis

datasource.driverClassName=oracle.jdbc.OracleDriver

##项目目录下执行命令:mvn mybatis-generator:generate

targetJavaProject=src/main/java

targetMapperBasePackage=com.guige.springbootdemo

targetModelPackage=com.guige.springbootdemo.entity

targetMapperPackage=com.guige.springbootdemo.mapper

targetResourcesProject=src/main/resources

targetXMLPackage=mybatis/mapper

创建generatorConfig.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE generatorConfiguration

PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"

"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

<properties resource="generator/config.properties"/>

<classPathEntry location="D:\work4\tomcat2\apache-tomcat-6.0.532\webapps\MIS\WEB-INF\lib\ojdbc6.jar"/>

<!-- 一个数据库一个context -->

<context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">

<property name="javaFileEncoding" value="UTF-8"/>

<property name="beginningDelimiter" value="`"/>

<property name="endingDelimiter" value="`"/>

<!-- 为了防止生成的代码中有很多注释,比较难看,加入下面的配置控制 -->

<commentGenerator>

<property name="suppressAllComments" value="true" />

<property name="suppressDate" value="true" />

</commentGenerator>

<!-- jdbc连接 -->

<jdbcConnection driverClass="${datasource.driverClassName}"

connectionURL="${datasource.url}"

userId="${datasource.username}"

password="${datasource.password}">

</jdbcConnection>

<!-- 生成实体类地址 -->

<javaModelGenerator targetPackage="${targetModelPackage}" targetProject="${targetJavaProject}">

</javaModelGenerator>

<!-- 生成mapxml文件 -->

<sqlMapGenerator targetPackage="${targetXMLPackage}" targetProject="${targetResourcesProject}">

</sqlMapGenerator>

<!-- 生成mapxml对应client,也就是接口dao|mapper -->

<javaClientGenerator targetPackage="${targetMapperPackage}" targetProject="${targetJavaProject}"

type="XMLMAPPER"/>

<table tableName="conf_parser" domainObjectName="ConfParser" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">

<generatedKey column="id" sqlStatement="select SEQ_CONF_PARSER.NEXTVAL from dual" identity="false" />

</table>

</context>

</generatorConfiguration>

##项目目录下执行命令:mvn mybatis-generator:generate

配置 Druid 数据源

添加依赖

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>druid-spring-boot-starter</artifactId>

<version>1.1.5</version>

</dependency>

添加配置

在 application.properties 中添加:

# 修改数据源

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

spring.datasource.druid.initial-size=5

spring.datasource.druid.min-idle=5

spring.datasource.druid.max-active=20

spring.datasource.druid.max-wait=60000

spring.datasource.druid.time-between-eviction-runs-millis=60000

spring.datasource.druid.min-evictable-idle-time-millis=300000

spring.datasource.druid.validation-query=SELECT 1 FROM DUAL

spring.datasource.druid.test-while-idle=true

spring.datasource.druid.test-on-borrow=false

spring.datasource.druid.test-on-return=false

spring.datasource.druid.pool-prepared-statements=true

spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20

spring.datasource.druid.filters=stat,wall,log4j

通过上文 MyBatis 的测试代码,运行结果如下:

项目已经使用了 Druid 数据源了。

配置 Druid 监控

默认情况下,Druid 的监控统计功能和页面是开启的。

我们启动项目,访问 http://localhost:8080/druid/index.html,如下图:

为了保证访问的安全性,我们可以如下配置:

在 application.properties 中添加:

## druid 监控

spring.datasource.druid.web-stat-filter.enabled=true

spring.datasource.druid.web-stat-filter.url-pattern=/*

spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*

## druid 监控页面

spring.datasource.druid.stat-view-servlet.enabled=true

spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*

spring.datasource.druid.stat-view-servlet.login-username=druid

spring.datasource.druid.stat-view-servlet.login-password=druid123

重启项目,再次访问 http://localhost:8080/druid/index.html 地址时需要身份验证:

参考资料

https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter Druid 相关

缓存配置

当系统的访问量增大时,相应的数据库的性能就逐渐下降。但是,大多数请求都是在重复的获取相同的数据,如果使用缓存,将结果数据放入其中可以很大程度上减轻数据库的负担,提升系统的响应速度。

本篇将介绍 Spring Boot 中缓存和 NoSQL 的使用。

整合缓存

Spring Boot 针对不同的缓存技术实现了不同的封装,本篇主要介绍 EhCache 和 Redis 缓存。

Spring Boot 提供了以下几个注解实现声明式缓存:

注解

说明

@EnableCaching

开启缓存功能,放在配置类或启动类上

@CacheConfig

缓存配置,设置缓存名称

@Cacheable

执行方法前先查询缓存是否有数据。有则直接返回缓存数据;否则查询数据再将数据放入缓存

@CachePut

执行新增或更新方法后,将数据放入缓存中

@CacheEvict

清除缓存

@Caching

将多个缓存操作重新组合到一个方法中

EhCache 缓存

添加依赖

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-cache</artifactId>

</dependency>

<dependency>

<groupId>net.sf.ehcache</groupId>

<artifactId>ehcache</artifactId>

</dependency>

在 src/main/resources 目录下创建 ehcache.xml 文件,内容如下:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="ehcache.xsd">

<cache name="department"

eternal="false"

maxEntriesLocalHeap="0"

timeToIdleSeconds="50">

</cache>

</ehcache>

application.properties :

spring.cache.type=ehcache

spring.cache.ehcache.config=classpath:ehcache.xml

# 打印日志,查看 sql

logging.level.com.light.springboot=DEBUG

在持久层篇的基础上,结合 Mybatis 测试:

Service 层:

@CacheConfig(cacheNames = "department")

@Service

public class DepartmentService {

@Autowired

private DepartmentMapper departmentMapper;

@CachePut(key = "#department.id")

public Department save(Department department) {

System.out.println("保存 id=" + department.getId() + " 的数据");

this.departmentMapper.insert(department);

return department;

}

@CachePut(key = "#department.id")

public Department update(Department department) {

System.out.println("修改 id=" + department.getId() + " 的数据");

this.departmentMapper.update(department);

return department;

}

@Cacheable(key = "#id")

public Department getDepartmentById(Integer id) {

System.out.println("获取 id=" + id + " 的数据");

Department department = this.departmentMapper.getById(id);

return department;

}

@CacheEvict(key = "#id")

public void delete(Integer id) {

System.out.println("删除 id=" + id + " 的数据");

this.departmentMapper.deleteById(id);

}

}

控制层:

@Controller

@RequestMapping("department")

@ResponseBody

public class DepartmentController {

@Autowired

private DepartmentService departmentService;

@RequestMapping("save")

public Map<String,Object> save(Department department) {

this.departmentService.save(department);

Map<String,Object> map = new HashMap<String,Object>();

map.put("code", "200");

map.put("msg", "保存成功");

return map;

}

@RequestMapping("get/{id}")

public Map<String,Object> get(@PathVariable("id") Integer id) {

Department department = this.departmentService.getDepartmentById(id);

Map<String,Object> map = new HashMap<String,Object>();

map.put("code", "200");

map.put("msg", "获取成功");

map.put("data", department);

return map;

}

@RequestMapping("update")

public Map<String,Object> update(Department department) {

this.departmentService.update(department);

Map<String,Object> map = new HashMap<String,Object>();

map.put("code", "200");

map.put("msg", "修改成功");

return map;

}

@RequestMapping("delete/{id}")

public Map<String,Object> delete(@PathVariable("id") Integer id) {

this.departmentService.delete(id);

Map<String,Object> map = new HashMap<String,Object>();

map.put("code", "200");

map.put("msg", "删除成功");

return map;

}

}

启动类:

添加 @EnableCaching 注解,开启缓存功能。

@EnableCaching

@SpringBootApplication

public class SpringbootNosqlApplication {

public static void main(String[] args) {

SpringApplication.run(SpringbootNosqlApplication.class, args);

}

}

测试说明

发送保存接口


发送查询接口


再次发送查询接口


说明数据是从缓存中获取。

发起修改请求:

发送查询接口

没有sql日志打印,但返回修改后的对象数据,说明缓存中的数据已经同步。

发起删除请求:

删除成功后,立刻发起查询请求,控制台打印 sql 语句,说明缓存数据被删除,需要查询数据库。

Redis 缓存

添加依赖

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

添加配置

application.properties :

spring.redis.host=192.168.1.254

spring.redis.port=6379

spring.redis.password=123456

spring.redis.database=6

spring.redis.pool.max-active=8

spring.redis.pool.max-idle=8

spring.redis.pool.max-wait=-1

spring.redis.pool.min-idle=0

spring.redis.timeout=0

注意:spring.cache.type=redis,缓存类型设置成 redis。

完成上边 2 个步骤后,其他步骤与测试 Ehcache 时的步骤一致。

测试结果也一致,此处省略。

整合 Redis

在添加 redis 依赖包启动项目后,Spring Boot 会自动配置 RedisCacheManger 和 RedisTemplate 的 Bean。如果开发者不想使用 Spring Boot 写好的 Redis 缓存,而是想使用其 API 自己实现缓存功能、消息队列或分布式锁之类的需求时,可以继续往下浏览。

Spring Data Redis 为我们提供 RedisTemplate 和 StringRedisTemplate 两个模板进行数据操作,它们主要 的访问方法如下:

方法

说明

opsForValue()

操作简单属性的数据

opsForList()

操作含有 list 的数据

opsForSet()

操作含有 set 的数据

opsForZSet()

操作含有 zset 的数据

opsForHash()

操作含有 hash 的数据

添加依赖

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

配置连接

spring.redis.host=192.168.1.254

spring.redis.port=6379

spring.redis.password=123456

spring.redis.database=6

spring.redis.pool.max-active=8

spring.redis.pool.max-idle=8

spring.redis.pool.max-wait=-1

spring.redis.pool.min-idle=0

spring.redis.timeout=0

编码

@Component

public class RedisDao {

@Autowired

private StringRedisTemplate stringRedisTemplate;

public void set(String key, String value) {

this.stringRedisTemplate.opsForValue().set(key, value);

}

public String get(String key) {

return this.stringRedisTemplate.opsForValue().get(key);

}

public void delete(String key) {

this.stringRedisTemplate.delete(key);

}

}

测试

@RunWith(SpringRunner.class)

@SpringBootTest

public class RedisDaoTest {

@Autowired

private RedisDao redisDao;

@Test

public void testSet() {

String key = "name";

String value = "zhangsan";

this.redisDao.set(key, value);

}

@Test

public void testGet() {

String key = "name";

String value = this.redisDao.get(key);

System.out.println(value);

}

@Test

public void testDelete() {

String key = "name";

this.redisDao.delete(key);

}

}

测试结果

整合 MongoDB

Spring Data MongoDB 提供了 MongoTemplate 模板 和 Repository 让开发者进行数据访问。

添加依赖

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-mongodb</artifactId>

</dependency>

配置连接

spring.data.mongodb.host=192.168.2.25

spring.data.mongodb.port=27017

spring.data.mongodb.database=test

编码

使用 MongoTemplate

@Component

public class MongodbDao {

@Autowired

private MongoTemplate mongoTemplate;

public void insert(User user) {

this.mongoTemplate.insert(user);

}

public void deleteById(int id) {

Criteria criteria = Criteria.where("id").is(id);

Query query = new Query(criteria);

this.mongoTemplate.remove(query, User.class);

}

public void update(User User) {

Criteria criteria = Criteria.where("id").is(User.getId());

Query query = new Query(criteria);

Update update = new Update();

update.set("password", User.getPassword());

this.mongoTemplate.updateMulti(query, update, User.class);

}

public User getById(int id) {

Criteria criteria = Criteria.where("id").is(id);

Query query = new Query(criteria);

return this.mongoTemplate.findOne(query, User.class);

}

public List<User> getAll() {

List<User> userList = this.mongoTemplate.findAll(User.class);

return userList;

}

}

使用 Repository

public interface UserRepository extends MongoRepository<User, Integer> {

}

测试方式与 Redis 测试大同小异,测试结果省略...

参考资料

https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html 官方文档

消息中间件

在消息中间件中有 2 个重要的概念:消息代理和目的地。当消息发送者发送消息后,消息就被消息代理接管,消息代理保证消息传递到指定目的地。

我们常用的消息代理有 JMS 和 AMQP 规范。对应地,它们常见的实现分别是 ActiveMQ 和 RabbitMQ。

整合 ActiveMQ

添加依赖

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-activemq</artifactId>

</dependency>

<!-- 如果需要配置连接池,添加如下依赖 -->

<dependency>

<groupId>org.apache.activemq</groupId>

<artifactId>activemq-pool</artifactId>

</dependency>

添加配置

# activemq 配置

spring.activemq.broker-url=tcp://192.168.2.12:61616

spring.activemq.user=admin

spring.activemq.password=admin

spring.activemq.pool.enabled=false

spring.activemq.pool.max-connections=50

# 使用发布/订阅模式时,下边配置需要设置成 true

spring.jms.pub-sub-domain=false

此处 spring.activemq.pool.enabled=false,表示关闭连接池。

编码

配置类:

@Configuration

public class JmsConfirguration {

public static final String QUEUE_NAME = "activemq_queue";

public static final String TOPIC_NAME = "activemq_topic";

@Bean

public Queue queue() {

return new ActiveMQQueue(QUEUE_NAME);

}

@Bean

public Topic topic() {

return new ActiveMQTopic(TOPIC_NAME);

}

}

负责创建队列和主题。

消息生产者:

@Component

public class JmsSender {

@Autowired

private Queue queue;

@Autowired

private Topic topic;

@Autowired

private JmsMessagingTemplate jmsTemplate;

public void sendByQueue(String message) {

this.jmsTemplate.convertAndSend(queue, message);

}

public void sendByTopic(String message) {

this.jmsTemplate.convertAndSend(topic, message);

}

}

消息消费者:

@Component

public class JmsReceiver {

@JmsListener(destination = JmsConfirguration.QUEUE_NAME)

public void receiveByQueue(String message) {

System.out.println("接收队列消息:" + message);

}

@JmsListener(destination = JmsConfirguration.TOPIC_NAME)

public void receiveByTopic(String message) {

System.out.println("接收主题消息:" + message);

}

}

消息消费者使用 @JmsListener 注解监听消息。

测试

@RunWith(SpringRunner.class)

@SpringBootTest

public class JmsTest {

@Autowired

private JmsSender sender;

@Test

public void testSendByQueue() {

for (int i = 1; i < 6; i++) {

this.sender.sendByQueue("hello activemq queue " + i);

}

}

@Test

public void testSendByTopic() {

for (int i = 1; i < 6; i++) {

this.sender.sendByTopic("hello activemq topic " + i);

}

}

}

打印结果:

接收队列消息:hello activemq queue 1

接收队列消息:hello activemq queue 2

接收队列消息:hello activemq queue 3

接收队列消息:hello activemq queue 4

接收队列消息:hello activemq queue 5

测试发布/订阅模式时,设置 spring.jms.pub-sub-domain=true

接收主题消息:hello activemq topic 1

接收主题消息:hello activemq topic 2

接收主题消息:hello activemq topic 3

接收主题消息:hello activemq topic 4

接收主题消息:hello activemq topic 5

整合 RabbitMQ

添加依赖

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-amqp</artifactId>

</dependency>

添加配置

spring.rabbitmq.host=192.168.2.30

spring.rabbitmq.port=5672

spring.rabbitmq.username=light

spring.rabbitmq.password=light

spring.rabbitmq.virtual-host=/test

编码

配置类:

@Configuration

public class AmqpConfirguration {

//=============简单、工作队列模式===============

public static final String SIMPLE_QUEUE = "simple_queue";

@Bean

public Queue queue() {

return new Queue(SIMPLE_QUEUE, true);

}

//===============发布/订阅模式============

public static final String PS_QUEUE_1 = "ps_queue_1";

public static final String PS_QUEUE_2 = "ps_queue_2";

public static final String FANOUT_EXCHANGE = "fanout_exchange";

@Bean

public Queue psQueue1() {

return new Queue(PS_QUEUE_1, true);

}

@Bean

public Queue psQueue2() {

return new Queue(PS_QUEUE_2, true);

}

@Bean

public FanoutExchange fanoutExchange() {

return new FanoutExchange(FANOUT_EXCHANGE);

}

@Bean

public Binding fanoutBinding1() {

return BindingBuilder.bind(psQueue1()).to(fanoutExchange());

}

@Bean

public Binding fanoutBinding2() {

return BindingBuilder.bind(psQueue2()).to(fanoutExchange());

}

//===============路由模式============

public static final String ROUTING_QUEUE_1 = "routing_queue_1";

public static final String ROUTING_QUEUE_2 = "routing_queue_2";

public static final String DIRECT_EXCHANGE = "direct_exchange";

@Bean

public Queue routingQueue1() {

return new Queue(ROUTING_QUEUE_1, true);

}

@Bean

public Queue routingQueue2() {

return new Queue(ROUTING_QUEUE_2, true);

}

@Bean

public DirectExchange directExchange() {

return new DirectExchange(DIRECT_EXCHANGE);

}

@Bean

public Binding directBinding1() {

return BindingBuilder.bind(routingQueue1()).to(directExchange()).with("user");

}

@Bean

public Binding directBinding2() {

return BindingBuilder.bind(routingQueue2()).to(directExchange()).with("order");

}

//===============主题模式============

public static final String TOPIC_QUEUE_1 = "topic_queue_1";

public static final String TOPIC_QUEUE_2 = "topic_queue_2";

public static final String TOPIC_EXCHANGE = "topic_exchange";

@Bean

public Queue topicQueue1() {

return new Queue(TOPIC_QUEUE_1, true);

}

@Bean

public Queue topicQueue2() {

return new Queue(TOPIC_QUEUE_2, true);

}

@Bean

public TopicExchange topicExchange() {

return new TopicExchange(TOPIC_EXCHANGE);

}

@Bean

public Binding topicBinding1() {

return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with("user.add");

}

@Bean

public Binding topicBinding2() {

return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("user.#");

}

}

RabbitMQ 有多种工作模式,因此配置比较多。想了解相关内容的读者可以查看本站的《RabbitMQ 工作模式介绍》或者自行百度相关资料。

消息生产者:

@Component

public class AmqpSender {

@Autowired

private AmqpTemplate amqpTemplate;

/**

* 简单模式发送

*

* @param message

*/

public void simpleSend(String message) {

this.amqpTemplate.convertAndSend(AmqpConfirguration.SIMPLE_QUEUE, message);

}

/**

* 发布/订阅模式发送

*

* @param message

*/

public void psSend(String message) {

this.amqpTemplate.convertAndSend(AmqpConfirguration.FANOUT_EXCHANGE, "", message);

}

/**

* 路由模式发送

*

* @param message

*/

public void routingSend(String routingKey, String message) {

this.amqpTemplate.convertAndSend(AmqpConfirguration.DIRECT_EXCHANGE, routingKey, message);

}

/**

* 主题模式发送

*

* @param routingKey

* @param message

*/

public void topicSend(String routingKey, String message) {

this.amqpTemplate.convertAndSend(AmqpConfirguration.TOPIC_EXCHANGE, routingKey, message);

}

}

消息消费者:

@Component

public class AmqpReceiver {

/**

* 简单模式接收

*

* @param message

*/

@RabbitListener(queues = AmqpConfirguration.SIMPLE_QUEUE)

public void simpleReceive(String message) {

System.out.println("接收消息:" + message);

}

/**

* 发布/订阅模式接收

*

* @param message

*/

@RabbitListener(queues = AmqpConfirguration.PS_QUEUE_1)

public void psReceive1(String message) {

System.out.println(AmqpConfirguration.PS_QUEUE_1 + "接收消息:" + message);

}

@RabbitListener(queues = AmqpConfirguration.PS_QUEUE_2)

public void psReceive2(String message) {

System.out.println(AmqpConfirguration.PS_QUEUE_2 + "接收消息:" + message);

}

/**

* 路由模式接收

*

* @param message

*/

@RabbitListener(queues = AmqpConfirguration.ROUTING_QUEUE_1)

public void routingReceive1(String message) {

System.out.println(AmqpConfirguration.ROUTING_QUEUE_1 + "接收消息:" + message);

}

@RabbitListener(queues = AmqpConfirguration.ROUTING_QUEUE_2)

public void routingReceive2(String message) {

System.out.println(AmqpConfirguration.ROUTING_QUEUE_2 + "接收消息:" + message);

}

/**

* 主题模式接收

*

* @param message

*/

@RabbitListener(queues = AmqpConfirguration.TOPIC_QUEUE_1)

public void topicReceive1(String message) {

System.out.println(AmqpConfirguration.TOPIC_QUEUE_1 + "接收消息:" + message);

}

@RabbitListener(queues = AmqpConfirguration.TOPIC_QUEUE_2)

public void topicReceive2(String message) {

System.out.println(AmqpConfirguration.TOPIC_QUEUE_2 + "接收消息:" + message);

}

}

测试

@RunWith(SpringRunner.class)

@SpringBootTest

public class AmqpTest {

@Autowired

private AmqpSender sender;

@Test

public void testSimpleSend() {

for (int i = 1; i < 6; i++) {

this.sender.simpleSend("test simpleSend " + i);

}

}

@Test

public void testPsSend() {

for (int i = 1; i < 6; i++) {

this.sender.psSend("test psSend " + i);

}

}

@Test

public void testRoutingSend() {

for (int i = 1; i < 6; i++) {

this.sender.routingSend("order", "test routingSend " + i);

}

}

@Test

public void testTopicSend() {

for (int i = 1; i < 6; i++) {

this.sender.topicSend("user.add", "test topicSend " + i);

}

}

}

测试结果略过。。。

踩坑提醒1:ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN

解决方案:

1) 请确保用户名和密码是否正确,需要注意的是用户名和密码的值是否包含空格或制表符(笔者测试时就是因为密码多了一个制表符导致认证失败)。

2) 如果测试账户使用的是 guest,需要修改 rabbitmq.conf 文件。在该文件中添加 "loopback_users = none" 配置。

踩坑提醒2:Cannot prepare queue for listener. Either the queue doesn't exist or the broker will not allow us to use it

解决方案:

我们可以登陆 RabbitMQ 的管理界面,在 Queue 选项中手动添加对应的队列。

参考资料

消息中间件简单介绍

Spring Boot 官方文档

Rabbit MQ 访问控制相关

Lombok 简单入门

Lombok 是一个 Java 库,它作为插件安装至编辑器中,其作用是通过简单注解来精简代码,以此达到消除冗长代码的目的。

优点

1) 通过注解自动生成成员变量的 getter、setter 等方法,使代码简洁

2) 修改类变量名时,无需关注其 getter、setter 等方法

缺点

降低源码文件的可读性。

原理

从 Java 6 开始,javac 就支持 JSR 269 API 规范,而 Lombok 实现 JSR 269 Pluggable Annation Processing API 规范。

当我们编写代码并保存后,编辑器会自动编译源码文件,在这个过程中,源码先被转化为 AST。

然后,Lombok 插件解析 AST 是否存在 Lombok 的注解。如果存在则修改 AST ,使其生成注解对应的代码。

最终将修改的 AST 解析并生成字节码文件。

安装插件

为编辑器安装 Lombok 插件。

IDEA 安装

在 IDEA 界面点击 "File"->"Settings" 弹出设置框,选择左侧 "Plugins",通过 "Browse repositories" 搜索 lombok 关键字安装即可。

Eclipse 安装

点击 Lombok.jar,下载该 jar 包。

双击 jar 包会弹出一个安装界面,点击界面的"Specify location..." 安装选择 Eclipse 的安装路径(精确到 eclipse.exe)。

使用

使用 Lombok 的方法非常简单,就是在类上或者成员变量上添加注解即可。

为了能使用注解,我们还需要在项目中引入 lombok 的 jar 包。

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

</dependency>

注解介绍

Lombok 常用的注解如下:

注解名

作用描述

@Getter

作用在类上或成员变量上,生成对应的 getter 方法

@Setter

作用在类上或成员变量上,生成对应的 setter 方法

@NoArgsConstructor

作用在类上,生成对应的无参构造方法

@AllArgsConstructor

作用在类上,生成对应的有参构造方法

@ToString

作用在类上,生成对应的 toString 方法

@EqualsAndHashCode

作用在类上,生成对应的 equals 和 hashCode 方法

@Data

作用在类上,效果等同于上述 5 个注解,排除 @AllArgsConstructor 功能

@Log4j/@Slf4j

作用在类上,生成对应的 Logger 对象,变量名为 log

本次测试使用 Ecplise 编辑器。

@Data

public class User {

private int id;

private String name;

private String password;

private Date birthday;

}

当添加注解保存文件后,Ecplise 编辑器的 Outline 视图结果如下:

我们还可以使用 jd-gui 等反编译工具查看源码,结果如下:

参考资料

http://jnb.ociweb.com/jnb/jnbJan2010.html 官方文档

Spring Boot 快速入门笔记的更多相关文章

  1. Spring Boot 快速入门

    Spring Boot 快速入门 http://blog.csdn.net/xiaoyu411502/article/details/47864969 今天给大家介绍一下Spring Boot MVC ...

  2. Spring Boot快速入门(二):http请求

    原文地址:https://lierabbit.cn/articles/4 一.准备 postman:一个接口测试工具 创建一个新工程 选择web 不会的请看Spring Boot快速入门(一):Hel ...

  3. 笔记61 Spring Boot快速入门(一)

    IDEA+Spring Boot快速搭建 一.IDEA创建项目 略 项目创建成功后在resources包下,属性文件application.properties中,把数据库连接属性加上,同时可以设置服 ...

  4. spring boot入门教程——Spring Boot快速入门指南

    Spring Boot已成为当今最流行的微服务开发框架,本文是如何使用Spring Boot快速开始Web微服务开发的指南,我们将使创建一个可运行的包含内嵌Web容器(默认使用的是Tomcat)的可运 ...

  5. Spring Boot 快速入门 史上最简单

    1.Spring Boot 概述 Spring Boot 是所有基于 Spring 开发的项目的起点.Spring Boot 的设计是为了让你尽可能快的跑起来 Spring 应用程序并且尽可能减少你的 ...

  6. Spring Boot 快速入门(IDEA)

    从字面理解,Boot是引导的意思,因此SpringBoot帮助开发者快速搭建Spring框架:SpringBoot帮助开发者快速启动一个Web容器:SpringBoot继承了原有Spring框架的优秀 ...

  7. 笔记65 Spring Boot快速入门(五)

    SpringBoot+JPA 一.什么是JPA? JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期 ...

  8. 笔记63 Spring Boot快速入门(三)

    SpringBoot中使用JSP Springboot的默认视图支持是Thymeleaf,但是Thymeleaf还没开始学,熟悉的还是jsp,所以要让Springboot支持 jsp. 一.在pom. ...

  9. Spring Boot快速入门(最新)

    本章通过完成Spring Boot基础项目的构建并实现一个简单的Http请求处理,让大家对Spring Boot有一个初步的了解,并体验其结构简单.开发快速的特性.预计阅读及演练过程将花费约5分钟. ...

随机推荐

  1. Shell脚本的调试技术

    编程中必不可少的一点就是调试,Shell脚本以其强大的功能令人向往,当然,它的强大之处不只是体现在语言的实现功能上,更强大的是它的调试功能,下面,我将以实例讲解Shell脚本的调试技术. 下面是我所用 ...

  2. 如何设计一个web容器

    开发一个web容器涉及很多不同方面不同层面的技术,例如通信层的知识,程序语言层面的知识等等,且一个可用的web容器是一个比较庞大的系统,要说清楚需要很长的篇幅,本文旨在介绍如何设计一个web容器,只探 ...

  3. 《java入门第一季》之面向对象(匿名内部类)

    1.认识匿名内部类 /* 匿名内部类 就是内部类的简化写法. 前提:存在一个类或者接口 这里的类可以是具体类也可以是抽象类. 匿名内部类的格式: new 类名或者接口名(){ 重写方法; }:这代表的 ...

  4. 【Linux 操作系统】Ubuntu 基础操作 基础命令 热键 man手册使用 关机 重启等命令使用

    . : 关机, 如果将Linux默认运行等级设置为0, 系统将无法启动; -- : 多用户模式, 允许使用网络文件系统, 一般不使用图形界面登陆就是这种模式; -- : 多用户图形界面模式, 该模式下 ...

  5. 漫谈程序员(十一)老鸟程序员知道而新手不知道的小技巧之Web 前端篇

    老鸟程序员知道而新手不知道的小技巧 Web 前端篇 常充电!程序员只有一种死法:土死的. 函数不要超过50行. 不要一次性写太多来不及测的代码,而是要写一段调试一段. UI和编码要同步做. 多写注释方 ...

  6. mysql进阶(九)多表查询

    MySQL多表查询 一 使用SELECT子句进行多表查询 SELECT 字段名 FROM 表1,表2 - WHERE 表1.字段 = 表2.字段 AND 其它查询条件 SELECT a.id,a.na ...

  7. Leetcode_49_Anagrams

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/42744709 Given an array of stri ...

  8. Android应用---基于NDK的samples例程hello-jni学习NDK开发

    Android应用---基于NDK的samples例程hello-jni学习NDK开发 NDK下载地址:http://developer.android.com/tools/sdk/ndk/index ...

  9. LeetCode之“链表”:Remove Nth Node From End of List

    题目链接 题目要求: Given a linked list, remove the nth node from the end of list and return its head. For ex ...

  10. HBase replication

    Hbase Replication 介绍 现状 Hbase 的replication目前在业界使用并不多见,原因有很多方面,比如说HDFS目前已经有多份备份在某种程度上帮助HBASE底层数据的安全性, ...