本文将介绍Spring Boot整合第三方技术的配置、原理与示例。
- DataSourceAutoConfiguration:数据源自动配置类
- MybatisAutoConfiguration:MyBatis 自动配置类(第三方)
- RedisAutoConfiguration:Redis 自动配置类
- RabbitAutoConfiguration:RabbitMQ 自动配置类
整合 Druid 数据源
导入 JDBC 场景
- 在Maven中导入JDBC场景
spring-boot-starter-data-jdbc
:
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency>
|
导入该场景后,将出现数据源Hikari、JDBC和事务等依赖:
- 导入数据库 MySQL 驱动的依赖:
1 2 3 4 5 6
| <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency>
|
MySQL 官方推荐使用的驱动版本是 8.0,其向下兼容 5.6, 5.7, 8.0 版本的 MySQL。
Spring Boot2 提供的 MySQL 驱动的默认版本:<mysql.version>8.0.22</mysql.version>
。若想要修改版本,可以:
- 直接依赖引入具体版本(maven的就近依赖原则)
- 重新声明版本(maven的属性的就近优先原则)
1 2 3 4 5 6 7 8 9 10 11
| <properties> <java.version>1.8</java.version> <mysql.version>5.1.49</mysql.version> </properties>
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency>
|
- 修改数据源的配置项:
1 2 3 4 5 6 7 8
| spring: datasource: url: jdbc:mysql://localhost:3306/tableName?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: zhaoyuyun driver-class-name: com.mysql.cj.jdbc.Driver
|
数据源自动配置原理
DataSourceAutoConfiguration: 数据源的自动配置类
- 修改数据源相关的配置前缀:
"spring.datasource"
- 数据库连接池的配置,是容器中没有自定义的DataSource时才自动配置的
- 底层自动配置的数据源是:HikariDataSource
其他数据库相关的自动配置类:
- DataSourceTransactionManagerAutoConfiguration: 事务管理器的自动配置
- JdbcTemplateAutoConfiguration: JdbcTemplate的自动配置,可以来对数据库进行crud。容器中有JdbcTemplate这个组件,可以修改配置前缀
"spring.jdbc"
来修改JdbcTemplate的配置。
- JndiDataSourceAutoConfiguration: jndi的自动配置
- XADataSourceAutoConfiguration: 分布式事务相关的
Druid 数据源
Druid官方github地址:https://github.com/alibaba/druid
- 引入Druid官方提供的
starter
场景依赖:
1 2 3 4 5
| <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.17</version> </dependency>
|
其向容器中添加了一个 Druid 数据源自动配置类 DruidDataSourceAutoConfigure:
- 该配置器在Spring Boot自带的数据源自动配置器DataSourceAutoConfiguration之前配置,因此不再注册Spring Boot默认的数据源HikariDataSource。
- 该配置器绑定了DataSourceProperties和DruidStatProperties资源配置类,分别对应资源路径
"spring.datasource"
和"spring.datasource.druid"
- 该配置器导入了其他相关的配置类,用于开启配置页、防火墙、Web监控等功能
导入的其他相关配置类如下:
- DruidSpringAopConfiguration.class(
spring.datasource.druid.aop-patterns
):监控Spring Bean
- DruidStatViewServletConfiguration.class(
spring.datasource.druid.stat-view-servlet
):配置监控页:
- DruidWebStatFilterConfiguration.class(
spring.datasource.druid.web-stat-filter
):Web监控配置
- DruidFilterConfiguration.class:配置Druid的所有Filters:
1 2 3 4 5 6 7 8
| private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat"; private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config"; private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding"; private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j"; private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j"; private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2"; private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log"; private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filter.wall";
|
配置示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| spring: datasource: url: jdbc:mysql://localhost:3306/tableName?useUnicode=true&characterEncoding=utf8&useSSL=true username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver druid: aop-patterns: com.zhao.admin.* filters: stat,wall stat-view-servlet: enabled: true login-username: admin login-password: admin resetEnable: false
web-stat-filter: enabled: true urlPattern: /* exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
filter: stat: slow-sql-millis: 1000 logSlowSql: true enabled: true wall: enabled: true config: drop-table-allow: false
|
SpringBoot配置示例:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
配置项列表:https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8
整合 MyBatis
MyBatis官方链接:https://github.com/mybatis
原理
- 导入 MyBatis 的
starter
场景依赖:
1 2 3 4 5 6 7 8
| <dependencies> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> </dependencies>
|
其导入了如下包:
其中,MyBatis的自动配置器MybatisAutoConfiguration会在Spring Boot启动时注册到容器中:
该类绑定了MybatisProperties,对应Spring Boot的配置文件中以"mybatis"
为前缀的属性:
- MybatisAutoConfiguration向容器中注册了sqlSessionFactory,其使用容器中存在的数据源,并且从配置资源类MybatisProperties中获取MyBatis的配置属性值:
- MybatisAutoConfiguration向容器中注册了SqlSessionTemplate,其可以执行批量的SqlSession:
- MybatisAutoConfiguration向容器中注册了AutoConfiguredMapperScannerRegistrar,其用于扫描容器中带有 @Mapper 注解的组件:
使用 MyBatis
开启 MyBatis 流程:
- 导入MyBatis官方starter场景:
mybatis-spring-boot-starter
- 编写
xxxMapper
接口,并在其上使用 @Mapper 注解(也可以使用 @MapperScan() 简化)
- 编写sql映射文件
xxxMapper.xml
(放置在classpath:mapper/*.xml
下)并绑定xxxMapper
接口
- 在
application.yaml
中指定mapper配置文件的位置mapper-locations
,以及指定全局配置文件的信息
具体步骤如下:
- 导入 MyBatis 的 starter 场景:
mybatis-spring-boot-starter
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> </dependencies>
|
- 编写
UserMapper
接口,并在其上使用 @Mapper 注解(也可以使用 @MapperScan(“com.zhao.mapper”) 简化)
1 2 3 4 5 6 7 8 9
| @Mapper public interface UserMapper {
@Select("select * from user where id = #{id}") User selectUser(Long id);
void deleteUser(Long id); }
|
- 编写sql映射文件
userMapper.xml
(放置在classpath:mapper/*.xml
下)
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zhao.admin.mapper.UserMapper"> <select id="selectUser" resultType="com.zhao.admin.bean.User"> select * from user where id = #{id} </select>
<delete id="deleteUser" parameterType="long"> delete from user where id = #{id} </delete> </mapper>
|
- 在
application.yaml
中配置MyBatis:
1 2 3 4 5 6 7
| mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true
|
项目结构:
整合 MyBatis Plus
- 导入MyBatis-Plus的
starter
场景:mybatis-plus-boot-starter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version> </dependency> </dependencies>
|
其会向容器中导入MybatisPlusAutoConfiguration:
其对应的配置前缀为"mybatis-plus"
,其会默认扫描"classpath*:/mapper/**/*.xml"
,即当前模块类路径(包括引用其他jar包的类路径,classpath:/mapper...
则只会包含自己的类路径)下mapper目录下的所有.xml
文件都会被作为MyBatis的.xml
进行扫描(开发人员将sql映射文件放置在该目录下即可):
注意:"classpath*:/mapper/**/*.xml"
所指的类路径不仅包含当前模块的类路径,而且还包含了当前模块引用的其他包的类路径。若想只引用自己模块的类路径,则可以写:"classpath:/mapper/**/*.xml"
- 配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| spring: application: name: yunmall-product datasource: username: root password: xxx url: jdbc:mysql://yuyunzhao.cn:3306/yunmall_pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus: mapperLocations: classpath:mapper/**/*.xml global-config: db-config: id-type: auto logic-delete-value: 1 logic-not-delete-value: 0 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
- 使用时,自定义的Mapper接口继承
BaseMapper<User>
接口即可自动实现简单功能的CRUD:
1 2 3 4
| @Mapper public interface UserMapper extends BaseMapper<User> {
}
|
BaseMapper<User>
接口中默认实现了简单CRUD的方法:
使用 MyBatis Plus 提供的IService
,ServiceImpl
,减轻Service层开发工作。
1 2 3 4 5 6 7 8 9 10 11
| import com.zhao.hellomybatisplus.model.User; import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface UserService extends IService<User> { }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| import com.zhao.hellomybatisplus.model.User; import com.zhao.hellomybatisplus.mapper.UserMapper; import com.zhao.hellomybatisplus.service.UserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
import java.util.List;
@Service public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService { }
|
- 配置 MyBatis-Plus 分页插件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Configuration @EnableTransactionManagement @MapperScan("com.zhao.yunmall.product.dao") public class MyBatisConfig {
@Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); paginationInterceptor.setOverflow(true); paginationInterceptor.setLimit(500); paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); return paginationInterceptor; } }
|
整合 Redis
- 导入 Redis 的
starter
场景依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> </dependencies>
|
Redis 相关的组件都由 RedisAutoConfiguration 完成配置和注入:
其绑定了 spring.redis
属性,并且向容器中注入了:
RedisTemplate<Object, Object>
:可以指定任意泛型
StringRedisTemplate
:常用。其 key-value 都是 String 类型
提供了两种 Redis 客户端:
- Lettuce:默认,基于 Netty 框架的客户端,线程安全,性能较好(旧版本存在 BUG:可能产生堆外内存溢出问题
OutOfDirectMemoryError
,这是因为其一些计数逻辑没有处理好,导致 Netty 一直在计数没有减数,从而超出堆外内存)。
- Jedis:原生 API
二者都是底层的客户端,RedisTemplate
是 Spring 在二者基础上进行的二次封装,更方便操作 Redis。
- 在配置文件中设置 redis 信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| spring: redis: host: 192.168.1.203 port: 6379 password: database: 0 client-type: lettuce timeout: 1800000 lettuce: pool: max-active: 10 max-wait: -1 min-idle: 5 min-idle: 0
|
- 向容器中注入 Redis 配置类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| @EnableCaching @Configuration public class RedisConfig extends CachingConfigurerSupport {
@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setConnectionFactory(factory); template.setKeySerializer(redisSerializer); template.setValueSerializer(jackson2JsonRedisSerializer); template.setHashValueSerializer(jackson2JsonRedisSerializer); return template; }
@Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(600)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); return cacheManager; } }
|
- 测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| @RestController @RequestMapping("/redisTest") public class RedisTestController { @Autowired private RedisTemplate redisTemplate; @Autowired private StringRedisTemplate redisTemplate; @Autowired private RedisConnectionFactory redisConnectionFactory;
@GetMapping public String testRedis01() { redisTemplate.opsForValue().set("name","lucy");
String name = (String) redisTemplate.opsForValue().get("name"); return name; } @GetMapping void testRedis02(){ ValueOperations<String, String> operations = redisTemplate.opsForValue(); operations.set("hello","world"); String hello = operations.get("hello"); System.out.println(hello); System.out.println(redisConnectionFactory.getClass()); } }
|
应用:统计 URL 访问次数
- 自定义拦截器统计 URL 的访问次数
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Component public class RedisUrlCountInterceptor implements HandlerInterceptor { @Autowired StringRedisTemplate redisTemplate;
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); redisTemplate.opsForValue().increment(uri); return true; } }
|
- 注册 URL 统计拦截器:
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Configuration public class AdminWebConfig implements WebMvcConfigurer{ @Autowired RedisUrlCountInterceptor redisUrlCountInterceptor;
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(redisUrlCountInterceptor) .addPathPatterns("/**") .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**", "/js/**","/aa/**"); } }
|
- 查看 Redis 内的统计数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Slf4j @Controller public class IndexController { @Autowired StringRedisTemplate redisTemplate;
@GetMapping("/main.html") public String mainPage(HttpSession session,Model model){ log.info("当前方法是:{}","mainPage");
ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
String s = opsForValue.get("/main.html"); String s1 = opsForValue.get("/sql");
model.addAttribute("mainCount",s); model.addAttribute("sqlCount",s1);
return "main"; } }
|
整合 Redisson
- 导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.12.0</version> </dependency>
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.12.0</version> </dependency>
|
- 程序化配置:Redisson程序化的配置方法是通过构建
Config
对象实例来实现的,使用Config
对象创建出RedissonClient对象,后续所有对Redisson的使用都借助于RedissonClient对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Configuration public static class MyRedissonConfig { @Bean(destroyMethod="shutdown") public RedissonClient redisson() throws IOException { Config config = new Config(); config.useClusterServers() .setPassword("zhaoyuyun") .addNodeAddress("redis://127.0.0.1:7004", "redis://127.0.0.1:7001"); return Redisson.create(config); }
@Bean(destroyMethod="shutdown") public RedissonClient redisson() throws IOException { Config config = new Config(); config.useSingleServer() .setPassword("zhaoyuyun") .setAddress("redis://myredisserver:6379"); return Redisson.create(config); } }
|
后续使用 RedissonClient 创建锁:RLock lock = redissonClient.getLock("catalogJson-lock");
整合 ElasticSearch
Java 中的 ES 客户端有多种选择,具体见官方文档: https://www.elastic.co/guide/en/elasticsearch/client/index.html。本章将介绍 Java REST Client 的配置和使用。与 Spring Data 的整合配置与使用见博客:https://blog.csdn.net/u011863024/article/details/115721328
- 导入依赖:
1 2 3 4 5 6
| <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.4.2</version> </dependency>
|
- 配置类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @NoArgsConstructor @Data @Configuration public class MallElasticSearchConfig {
public static final RequestOptions COMMON_OPTIONS; static { RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder(); COMMON_OPTIONS = builder.build(); }
@Bean public RestHighLevelClient esRestClient() { RestClientBuilder builder = RestClient.builder( new HttpHost("localhost", 9200, "http")); RestHighLevelClient client = new RestHighLevelClient(builder); return client; } }
|
- 注入
RestHighLevelClient
:
1 2
| @Autowired public RestHighLevelClient restHighLevelClient;
|
操作索引
- 创建索引
1 2 3 4 5 6 7 8 9
| @Test public void testCreateIndex() throws IOException { CreateIndexRequest request = new CreateIndexRequest("liuyou_index"); CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT); System.out.println(response.isAcknowledged()); System.out.println(response); restHighLevelClient.close(); }
|
- 获取索引:
1 2 3 4 5 6 7 8
| @Test public void testIndexIsExists() throws IOException { GetIndexRequest request = new GetIndexRequest("index"); boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT); System.out.println(exists); restHighLevelClient.close(); }
|
- 删除索引:
1 2 3 4 5 6 7 8
| @Test public void testDeleteIndex() throws IOException { DeleteIndexRequest request = new DeleteIndexRequest("liuyou_index"); AcknowledgedResponse response = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT); System.out.println(response.isAcknowledged()); restHighLevelClient.close(); }
|
整合 RabbitMQ
- 导入 Maven 依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
|
该场景启动器将导入 RabbitMQ 的自动配置类 RabbitAutoConfiguration
:
其中,RabbitProperties 类绑定了配置文件中的 spring.rabbitmq
前缀:
- 在配置文件中配置 RabbitMQ
1 2 3 4 5 6 7 8 9 10 11
| rabbitmq: host: yuyunzhao.cn port: 5672 virtual-host: / publisher-confirm-type: correlated publisher-returns: true template: mandatory: true listener: simple: acknowledge-mode: manual
|
注意:rabbitmq
的属性需要配置在 spring.rabbitmq
下,否则会走默认的本地端口
- 在主启动类上添加
@EnableRabbit
注解:
1 2 3 4 5 6 7
| @EnableRabbit @SpringBootApplication public class MallOrderApplication { public static void main(String[] args) { SpringApplication.run(MallOrderApplication.class, args); } }
|
- 在配置类中注入
RabbitTemplate
,定制化其消息转换器为 JSON 格式转换器,并设置确认回调和失败返回回调方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| @Configuration public class MyRabbitConfig {
private RabbitTemplate rabbitTemplate;
@Bean public MessageConverter messageConverter() { return new Jackson2JsonMessageConverter(); }
@Primary @Bean public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); this.rabbitTemplate = rabbitTemplate; rabbitTemplate.setMessageConverter(messageConverter()); initRabbitTemplate(); return rabbitTemplate; }
public void initRabbitTemplate() {
rabbitTemplate.setConfirmCallback((correlationData,ack,cause) -> { System.out.println("confirm...correlationData["+correlationData+"]==>ack:["+ack+"]==>cause:["+cause+"]"); });
rabbitTemplate.setReturnCallback((message,replyCode,replyText,exchange,routingKey) -> { System.out.println("Fail Message["+message+"]==>replyCode["+replyCode+"]" + "==>replyText["+replyText+"]==>exchange["+exchange+"]==>routingKey["+routingKey+"]"); }); } }
|
整合 ZooKeeper
ZooKeeper是一个分布式协调工具,可以实现注册中心功能,Spring Cloud中整合ZooKeeper时创建的服务节点是临时节点。ZooKeeper详细介绍参考文章 【ZooKeeper】ZooKeeper。
支付服务
- 新建名为
cloud-provider-payment8004
的支付服务Maven工程,占用端口8004。
- 引入 Maven 依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> <exclusions> <exclusion> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> </exclusion> </exclusions> </dependency>
<dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.9</version> </dependency>
|
需要注意,spring-cloud-starter-zookeeper-discovery
场景启动器自带一个ZooKeeper依赖,其版本可能与自己的ZooKeeper版本不兼容,从而无法启动Spring Boot项目,因此在pom文件中首先排除掉spring-cloud-starter-zookeeper-discovery
中的ZooKeeper依赖,再自己添加符合自己版本的ZooKeeper依赖。(也可能会出现log4j依赖的冲突,解决方案相同)
- 修改配置文件
1 2 3 4 5 6 7 8 9 10 11
| server: port: 8004
spring: application: name: cloud-provider-payment cloud: zookeeper: connect-string: 127.0.0.1:2181
|
- 创建主启动类
1 2 3 4 5 6 7 8 9 10 11
| import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication @EnableDiscoveryClient public class PaymentMain8004 { public static void main(String[] args) { SpringApplication.run(PaymentMain8004.class, args); } }
|
- 创建 Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController @Slf4j public class PaymentController { @Value("${server.port}") private String serverPort;
@RequestMapping(value = "/payment/zk") public String paymentzk() { return "springcloud with zookeeper: " + serverPort + "\t" + UUID.randomUUID().toString(); } }
|
- 测试
启动支付服务8004注册进ZooKeeper(要先启动zookeeper的server)
1 2 3 4 5 6 7 8 9 10 11
| [zk: localhost:2181(CONNECTED) 0] ls / [services, zookeeper] [zk: localhost:2181(CONNECTED) 1] ls /services/cloud-provider-payment [a4567f50-6ad9-47a3-9fbb-7391f41a9f3d] [zk: localhost:2181(CONNECTED) 2] get /services/cloud-provider-payment/a4567f50-6ad9-47a3-9fbb-7391f41a9f3d {"name":"cloud-provider-payment","id":"a4567f50-6ad9-47a3-9fbb-7391f41a9f3d","address":"192.168.199.218","port":8004,"ss lPort":null,"payload":{"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance","id":"application-1"," name":"cloud-provider-payment","metadata":{}},"registrationTimeUTC":1612811116918,"serviceType":"DYNAMIC","uriSpec":{"pa rts":[{"value":"scheme","variable":true},{"value":"://","variable":false},{"value":"address","variable":true},{"value":" :","variable":false},{"value":"port","variable":true}]}} [zk: localhost:2181(CONNECTED) 3]
|
json格式化get /services/cloud-provider-payment/a4567f50-6ad9-47a3-9fbb-7391f41a9f3d
的结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| { "name": "cloud-provider-payment", "id": "a4567f50-6ad9-47a3-9fbb-7391f41a9f3d", "address": "192.168.199.218", "port": 8004, "sslPort": null, "payload": { "@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance", "id": "application-1", "name": "cloud-provider-payment", "metadata": { } }, "registrationTimeUTC": 1612811116918, "serviceType": "DYNAMIC", "uriSpec": { "parts": [ { "value": "scheme", "variable": true }, { "value": "://", "variable": false }, { "value": "address", "variable": true }, { "value": ":", "variable": false }, { "value": "port", "variable": true } ] } }
|
订单服务
- 新建名为
cloud-consumerzk-order80
的订单服务Maven工程,端口号为80。
- 引入 Maven 依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> <exclusions> <exclusion> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> </exclusion> </exclusions> </dependency>
<dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.9</version> </dependency>
|
需要注意,spring-cloud-starter-zookeeper-discovery
场景启动器自带一个ZooKeeper依赖,其版本可能与自己的ZooKeeper版本不兼容,从而无法启动Spring Boot项目,因此在pom文件中首先排除掉spring-cloud-starter-zookeeper-discovery
中的ZooKeeper依赖,再自己添加符合自己版本的ZooKeeper依赖。(也可能会出现log4j依赖的冲突,解决方案相同)
- 修改配置文件
1 2 3 4 5 6 7 8 9 10
| server: port: 80
spring: application: name: cloud-consumer-order cloud: zookeeper: connect-string: 127.0.0.1:2181
|
- 创建主启动类
1 2 3 4 5 6 7 8 9 10 11
| import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication @EnableDiscoveryClient public class OrderZKMain80 { public static void main(String[] args) { SpringApplication.run(OrderZKMain80.class, args); } }
|
- 业务类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate;
@Configuration public class ApplicationContextConfig { @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import javax.annotation.Resource;
@RestController @Slf4j public class OrderZKController { public static final String INVOKE_URL = "http://cloud-provider-payment";
@Resource private RestTemplate restTemplate;
@GetMapping(value = "/consumer/payment/zk") public String paymentInfo() { String result = restTemplate.getForObject(INVOKE_URL+"/payment/zk",String.class); return result; } }
|
- 测试
先后运行ZooKeeper服务端,cloud-consumerzk-order80
,cloud-provider-payment8004
。
打开ZooKeeper客户端:
1 2 3 4 5
| [zk: localhost:2181(CONNECTED) 0] ls / [services, zookeeper] [zk: localhost:2181(CONNECTED) 1] ls /services [cloud-consumer-order, cloud-provider-payment] [zk: localhost:2181(CONNECTED) 2]
|
访问测试地址:http://localhost/consumer/payment/zk
原理
80客户端将从ZooKeeper中订阅8004服务端的URL信息,从而利用RestTemplate对象调用该URL对应的Rest请求,从而实现远程调用的效果