210807-Jackson 实用姿势小结

使用json进行数据交互可以说是非常常见的常见,在java侧,常用的json解析框架也不少,比如gson, fastjson以及spring mvc中默认使用的jackson;本文将主要介绍一下jackson的基本使用姿势,比如常见的

  • 普通对象转json字符串
  • json字符串转POJO,转Map/List
  • 泛型支持
  • 驼峰/下划线互转,自定义映射关系

210723-Arthas 获取SpringContext访问应用行为记录

使用arthas进行应用排查定位,这里主要记录借助arthas,获取SpringContext,然后就可以通过SpringContext来访问应用内存数据,调用bean方法等操作

基本操作

1
2
3
4
5
6
7
8
9
10
11
12
# 下载arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar
# 启动
java -jar arthas-boot.jar
# 选择需要挂在的jar进程
1

# 监控,获取SpringContext
tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod

# 然后通过http访问一下应用,接下来就可以使用下面的获取到SpringContext
tt -i 1000 -w 'target.getApplicationContext()'

210715-Json序列化框架对比与最佳实践推荐

Java 生态中,最最常见的json序列化工具有三个jackson, gson, fastsjon,当然我们常用的也就是这几个

https://mvnrepository.com/open-source/json-libraries

json协议虽然是一致的,但是不同的框架对json的序列化支持却不尽相同,那么在项目中如何使用这些框架,怎样的使用才算优雅呢?

210629-QlExpress使用姿势四:Operator与Micro扩展

本文将介绍QlExpress中自定义操作符Operator + Micro的使用姿势,通过扩展Operator,可以为规则脚本赋能,提供更友好的使用姿势

210628-QlExpress使用姿势三:Function与方法绑定

QlExpress绑定方法,实现能力扩展;支持自定Function,再脚本中定义函数,实现复用

210624-QlExpress使用姿势二:基本语法

上一篇博文简单的介绍了一下QlExpress,以及一个最基础的使用demo,接下来我们看一下QlExpress的语法,重点关注一下它与Java不同的地方

210509-spring boot启动类启动 错误: 找不到或无法加载主类 xxx.xxxx.Application 的解决方法

SpringBoot项目启动,忽然提示找不到或者无法加载主类,记录两种常用的方式

方法一:清空idea缓存

idea清理缓存:

  • file -> Invalidate Cache & Restart IDEA

方法二:maven重新install

执行命令

1
mvn clean install -DskipTests=true

210331-ElastchSearch 基本使用姿势

ElastchSearch 基本使用姿势,如常见的

  • 添加文档
  • 常见的查询姿势
  • 修改/删除文档

210329-Elastic & Kibana安装与基本使用

本文主要介绍es & kibana的安装和基本使用,更多es的相关用法后面逐一补上

210128-dubbo接口测试小技巧

记录一下借助telnate进行简单的dubbo接口测试

  • 获取dubbo端口号
  • telnate ip port
  • ls 列出所有服务
  • invoke com.xxx.Service.sayHello("xxx")

201016-rabbitmq延时插件安装

源码: https://github.com/rabbitmq/rabbitmq-delayed-message-exchange

下载二进制的插件,如3.8.0下载地址: https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/tag/v3.8.0

将下载的ez包,放在插件目录下,一般centos的查检目录放在/usr/lib/rabbitmq/lib/rabbitmq_server-xxx/plugins

如果不知道具体在什么地方,可以通过进程查看

拷贝完毕之后,启用插件

1
rabbitmq-plugins enable rabbitmq_delayed_message_exchange

接着重启一下rabbit

1
service rabbit-server restart

200826-Gson 简单使用姿势小结

关于Json序列化的框架可以说比较多了,比如Spring默认的Jackson,国内互联网用的比较多的FastJson,本文则主要介绍一下Gson的简单使用姿势,并不会涉及到不同的json框架的性能对比

本文主要内容来源于官方教程: https://github.com/google/gson/blob/master/UserGuide.md

200214-Guava BloomFilter 使用手册

在jdk中有一个数据结构BitSet,可以用来执行位操作,比如我们常见的统计网站在线人数、pv/uv等;但是当数据样本分布不均,可能导致较大的空间浪费;其次它更适用于正整数类型的判定,针对其他的业务场景,有点薄弱

本文将介绍BloomFilter(布隆过滤器)的相关知识点,以及Guava中BloomFilter的使用姿势

200115-Redis配置参数在线修改(热修改)

redis的配置除了直接修改配置文件之后,重启进程之外,还支持在线修改,下面记录一下使用姿势

191217-Ognl之内部类与静态成员属性修改使用姿势

191204-Ognl 使用实例手册 中,当时遇到一个问题,静态成员属性直接赋值时,会抛出异常;那么这个问题真的无解么?

此外之前的实例手册中,漏了一个内部类的使用姿势,本文也一并补上

191204-Ognl 使用实例手册

上一篇博文介绍了ongl的基础语法,接下来进入实际的使用篇,我们将结合一些实际的case,来演示ognl究竟可以支撑到什么地步

在看本文之前,强烈建议先熟悉一下什么是ognl,以及其语法特点,减少阅读障碍,五分钟入门系列: 191129-Ognl 语法基础教程

191129-Ognl 语法基础教程

本文将力求用最简单的语言是和示例,介绍一下OGNL的语法规则,文章主要内容参考自官方文档http://commons.apache.org/proper/commons-ognl/language-guide.html

  • 本篇主要是语法介绍篇,实战放在一下篇

190401-SpringBoot远程debug设置

记录下SpringBoot jar启动方式,开启远程debug的命令

1
java -server -Xms512m -Xmx512m -XX:AutoBoxCacheMax=20480 -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=9999,suspend=n -jar web.jar

190117-Rabbitmq启动失败的问题

rabbitmq启动,死活起不来,一直报错,记录下解决方式

1
2
Redirecting to /bin/systemctl start rabbitmq-server.service
Job for rabbitmq-server.service failed because the control process exited with error code. See "systemctl status rabbitmq-server.service" and "journalctl -xe" for details.

181119-Redis性能监控之Redis-Stat

redis性能监控工具

使用 redis-stat 进行redis的性能监控数据收集

181112-springboot应用下线配置

本篇记录SpringBoot 2.x借助Actuator实现优雅的下线的配置方式

181011-SpringCloud之DiscoverClient无法获取Service记录

最终解决问题之后,才发现是自己走进盲角了,花了不少时间,特此记录

I. 问题说明

在测试使用SpringCloud全家桶时,服务向注册中心注册,然后client就可以相互之间实现RPC调用(其实还是http访问)

如果我想看看当前注册中心获取了哪些服务,可以怎么办?

一个简单的方法就是借助DiscoverClient来做,然后问题就来了

1
2
3
4
public Application(DiscoveryClient discoveryClient) {
List<String> list = discoveryClient.getServices();
System.out.println(list);
}

上面的代码执行之后,发现返回是空数组;蛋疼的是换另外一个工程,同样的方式,却可以拿到注册的服务名

查了半天,最好才发现拿不到是因为在配置中添加了

1
eureka.client.fetchRegistry=false

上面就表示不会去主动获取注册的服务,因此也就拿不到服务了;改成true之后就ok了

II. 其他

1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰blog

QrCode

知识星球

goals

180928-hexo使用本地图片的一种方式

hexo搭建的个人博客中,想引入图片可以怎么搞?

整个过程中,尝试过一些方法,下面记录一下,并给出现在正使用的方式

180905-Spring返回Json对象

Spring之返回Json串

使用Spring搭建的web后端服务,与前端通过Json串进行交互,特此记录下使用姿势

1. 直接返回JsonString

这个可以说是最简单和最常见的一种使用姿势了,直接返回String,如下

1
2
3
4
5
@ResponseBody
@RequestMapping(value = "/body")
public String body(@RequestBody Req req) {
return JSON.toJSONString(req);
}

2. 使用JsonInclude注解

直接返回的是一个对象,然后交给框架来将对象转换为String丢给前端

1
2
3
4
5
@RequestMapping(path = {"/index", "/"})
public ResponseWrapper<List<PoetryDTO>> index() {
List<PoetryDTO> ans = poetryReadService.getIndex();
return ResponseWrapper.successReturn(ans);
}

直接用上面的方式时,可能会抛出,提示没有对应的HttpMessageConverter来转换ResponseWrapper对象

一个简单的使用姿势就是直接使用注解 com.fasterxml.jackson.annotation.JsonInclude

如下即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
JsonInclude
@Data
@NoArgsConstructor
public class ResponseWrapper<T> {
private int code;
private String msg;
private T data;

public ResponseWrapper(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}

public static <T> ResponseWrapper<T> buildSuccess(T data) {
return new ResponseWrapper<>(200, "success", data);
}

public static <T> ResponseWrapper<T> buildError(int code, String msg, T data) {
return new ResponseWrapper<>(code, msg, data);
}
}

因此关键就是HttpMessageConverter在起作用了,下一篇重点关注下这个是什么东西,干嘛用,以及如何自己实现一个特定需求的转换器

II. 其他

1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰blog

QrCode

知识星球

goals

180830-SpringBoot之获取application.yml配置参数

SpringBoot之获取Application.yml配置参数

需要获取配置文件中的配置参数的场景挺多的,常见的一种方式就是直接从Enironment对象中获取,或者使用 @Value 注解的方式注入,但是这都有一个前提,需要确切的知道配置的name

如果某些场景下,我需要遍历配置参数可以怎么办?

180815-Spring之RestTemplate使用小结二:中级使用篇

Spring之RestTemplate中级使用篇

前面一篇介绍了如何使用RestTemplate发起post和get请求,然而也只能满足一些基本的场景,对于一些特殊的如需要设置请求头,添加认证信息等场景,却没有提及可以怎么做,这一篇则相当于进阶版,将主要介绍

  • get/post请求如何携带 header
  • post传文件可以怎么玩, post提交json串怎么处理
  • exchange方法的使用姿势

180813-Spring之RestTemplate使用小结一

Spring之RestTemplate初级使用篇

作为一个Java后端,需要通过HTTP请求其他的网络资源可以说是一个比较常见的case了;一般怎么做呢?

可能大部分的小伙伴直接捞起Apache的HttpClient开始做,或者用其他的一些知名的开源库如OkHttp, 当然原生的HttpURLConnection也是没问题的

本篇博文则主要关注点放在Spring的生态下,利用RestTemplate来发起Http请求的使用姿势

180804-Spring之动态注册bean

Spring之动态注册bean

什么场景下,需要主动向Spring容器注册bean呢?

如我之前做个的一个支持扫表的基础平台,使用者只需要添加基础配置 + Groovy任务,就可以丢到这个平台上面来运行了,而这个基础平台是一直都在运行的,所以在新来任务时,最直观需要注册的就是 DataSource 数据源这个bean了,那么可以怎么玩?

I. 主动注册Bean支持

借助BeanDefinition来实现bean的定义,从最终的使用来看,代码比较少,几行而已

1
2
3
4
5
6
7
8
9
10
11
12
13
public <T> T registerBean(String name, Class<T> clazz, Object... args) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
if (args.length > 0) {
for (Object arg : args) {
beanDefinitionBuilder.addConstructorArgValue(arg);
}
}
BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();

BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
beanFactory.registerBeanDefinition(name, beanDefinition);
return applicationContext.getBean(name, clazz);
}

测试如下

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
import com.github.hui.story.quickstory.server.VisitService;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class BeanHolder {
private final ConfigurableApplicationContext applicationContext;

public BeanHolder(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
initSer();
}

public void initSer() {
InrSer ser = registerBean("test", InrSer.class);
ser.name = "一灰";
ser.uid = 22;
System.out.println(ser);

InrSer ser2 = registerBean("test2", InrSer.class, "一灰灰Blog", 20);
System.out.println(ser2);
}

@ToString
public static class InrSer {
private String name;
private Integer uid;

@Autowired
private VisitService visitService;

public InrSer() {
}

public InrSer(String name, Integer uid) {
this.name = name;
this.uid = uid;
}
}

private <T> T registerBean(String name, Class<T> clazz, Object... args) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
if (args.length > 0) {
for (Object arg : args) {
beanDefinitionBuilder.addConstructorArgValue(arg);
}
}
BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();

BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
beanFactory.registerBeanDefinition(name, beanDefinition);
return applicationContext.getBean(name, clazz);
}
}

输出如下

show

II. 其他

1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰blog

QrCode

知识星球

goals

180803-Spring定时任务高级使用篇

logo

前面一篇博文 《Spring之定时任务基本使用篇》 介绍了Spring环境下,定时任务的简单使用姿势,也留了一些问题,这一篇则希望能针对这些问题给个答案

I. 定时任务进阶篇

1. 问题小结

前面一篇博文,抛出了下面的几个问题,接下来则围绕问题进行分析

  • 一个项目中有多个定时任务时,他们是并行执行的还是串行执行的?
  • 如果默认是串行的
    • 那么有相同的crond表达式的定时任务之间,有先后顺序么?
    • 某个任务的阻塞是否会影响后面的任务?
    • 如果需要他们并行执行,可以怎么做?
  • 如果是并发执行的
    • 是新创建线程还是采用线程池来复用呢?
    • 在并发执行时,假设有个每秒执行一次的任务,但是它执行一次消耗的时间大于1s时,这个任务的表现时怎样的呢?不断地新增线程来执行还是等执行完毕之后再执行下一次的呢?

180801-Spring之定时任务基本使用篇

image.png

Spring之定时任务基本使用篇

spring-boot项目中,想添加一个定时任务,可以怎么办?

  • 不管什么项目,都是可以直接用JDK原生的定时任务来实现
  • 借助@Scheduled注解来使用

本篇博文则主要集中在在SpringBoot项目中,怎么使用定时任务

180730-Spring之RequestBody的使用姿势小结

logo

Spring之RequestBody的使用姿势小结

SpringMVC中处理请求参数有好几种不同的方式,如我们常见的下面几种

  • 根据 HttpServletRequest 对象获取
  • 根据 @PathVariable 注解获取url参数
  • 根据 @RequestParam 注解获取请求参数
  • 根据Bean的方式获取请求参数
  • 根据 @ModelAttribute 注解获取请求参数

对上面几种方式有兴趣的可以看一下这篇博文: SpringMVC之请求参数的获取方式

除了上面的几种方式之外,还有一种 @RequestBody 的使用方式,本文则主要介绍这种传参的使用姿势和相关注意事项

180713-Spring之借助Redis设计访问计数器之扩展篇

logo

之前写了一篇博文,简单的介绍了下如何利用Redis配合Spring搭建一个web的访问计数器,之前的内容比较初级,现在考虑对其进行扩展,新增访问者记录

  • 记录当前站点的总访问人数(根据Ip或则设备号)
  • 记录当前访问者在总访问人数中的排名
  • 记录每个子页面的访问计数,记录站点的总访问计数

180626-Spring之借助Redis设计一个简单访问计数器

logo

Spring之借助Redis设计一个简单访问计数器

为什么要做一个访问计数?之前的个人博客用得是卜算子做站点访问计数,用起来挺好,但出现较多次的响应很慢,再其次就是个人博客实在是访问太少,数据不好看😢…

前面一篇博文简单介绍了Spring中的RedisTemplate的配置与使用,那么这篇算是一个简单的应用case了,主要基于Redis的计数器来实现统计

180623-SpringBoot之logback配置文件

SpringBoot配置logback

项目的日志配置属于比较常见的case了,之前接触和使用的都是Spring结合xml的方式,引入几个依赖,然后写个 logback.xml 配置文件即可,那么在SpringBoot中可以怎么做?

180619-Yaml文件语法及读写小结

Yaml文件小结

Yaml文件有自己独立的语法,常用作配置文件使用,相比较于xml和json而言,减少很多不必要的标签或者括号,阅读也更加清晰简单;本篇主要介绍下YAML文件的基本语法,以及如何在Java中实现读写逻辑

180613-GuavaCache返回Null的注意事项

GuavaCache返回Null的注意事项

Guava在实际的Java后端项目中应用的场景还是比较多的,比如限流,缓存,容器操作之类的,有挺多实用的工具类,这里记录一下,在使用GuavaCache,返回null的一个问题

I. 常见使用姿势

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testGuava() {
LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
if ("hello".equals(key)) {
return "word";
}
return null;
}
});

String word = cache.getUnchecked("hello");
System.out.println(word);

System.out.println(cache.getUnchecked("word"));
}

上面是一个非常简单的测试case,需要注意的是,cache.get("word") 的执行,并不如逾期的返回的是null,而是会抛一个异常出来

1
2
3
4
word
com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader returned null for key word.
at com.google.common.cache.LocalCache$Segment.getAndRecordStats(LocalCache.java:2287)
...

从异常描述能看出,不允许返回null,这一块之前倒是没怎么注意,因此对于null的情况,要么定义一个标记表示不存在,要么在load()方法中主动抛一个异常出来,在使用的时候注意下,通过异常的使用方式,可以如下

1
2
3
4
5
6
7
8
9
10
public class NoVlaInGauvaException extends Exception {
public NoVlaInGauvaException(String msg) {
super(msg);
}

@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}

说明:为什么重写fillInStackTrace方法

  • 对于这种缓存未命中的情况下,一般而言是不需要关注完整的堆栈信息的,没有数据而已,可以节省一点点性能(当然除非是在高频率的抛出时,才会有表现症状)

其次就是getgetUnchecked的区别了

  • get要求显示处理exception状况
  • getUnchecked 一般是可确认不会有问题的场景,直接调用

II. 其他

1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

QrCode

180612-Spring之Yml配置文件加载问题

Yml配置文件加载问题

在resource目录下有一个application.yml文件,希望是通过@PropertySource注解,将配置文件数据读取到Environment中,然而调试发现数据始终读取不到,google之后,记录下解决方法

在测试用例中,指定初始化方式 @ContextConfiguration(classes = RedisConf.class, initializers = ConfigFileApplicationContextInitializer.class)

1
2
3
4
5
6
7
8
9
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RedisConf.class, initializers = ConfigFileApplicationContextInitializer.class)
public class RedisTest {
@Test
public void testRedis() {
String ans = JedisClient.getStr("hello");
System.out.println(ans);
}
}

对应的配置类

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
@Configuration
@PropertySource(value = "classpath:application.yml")
public class RedisConf {

@Autowired
private Environment environment;

@Autowired
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);

DefaultStrSerializer serializer = new DefaultStrSerializer();
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashValueSerializer(serializer);
redisTemplate.setKeySerializer(serializer);
redisTemplate.setHashKeySerializer(serializer);

redisTemplate.afterPropertiesSet();

JedisClient.register(redisTemplate);
return redisTemplate;
}

@Bean
public RedisConnectionFactory redisConnectionFactory() {
LettuceConnectionFactory fac = new LettuceConnectionFactory();
fac.getStandaloneConfiguration().setHostName(environment.getProperty("spring.redis.host"));
fac.getStandaloneConfiguration().setPort(Integer.parseInt(environment.getProperty("spring.redis.port")));
fac.getStandaloneConfiguration()
.setPassword(RedisPassword.of(environment.getProperty("spring.redis.password")));
fac.afterPropertiesSet();
return fac;
}
}

II. 其他

1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

QrCode

180611-Spring之RedisTemplate配置与使用

Spring之RedisTemplate配置与使用

Spring针对Redis的使用,封装了一个比较强大的Template以方便使用;之前在Spring的生态圈中也使用过redis,但直接使用Jedis进行相应的交互操作,现在正好来看一下RedisTemplate是怎么实现的,以及使用起来是否更加便利

180609-Spring之事件驱动机制的简单使用

Spring之事件驱动机制的简单使用

关于事件的发起与相应,在客户端的交互中可算是非常频繁的事情了,关于事件的发布订阅,在Java生态中,EventBus可谓是非常有名了,而Spring也提供了事件机制,本文则主要介绍后端如何在Spring的环境中,使用事件机制

RabbitMQ基础教程之基于配置的消费者实现

RabbitMQ基础教程之基于配置的消费者实现

相关博文,推荐查看:

  1. RabbitMq基础教程之安装与测试
  2. RabbitMq基础教程之基本概念
  3. RabbitMQ基础教程之基本使用篇
  4. RabbitMQ基础教程之使用进阶篇
  5. RabbitMQ基础教程之Spring&JavaConfig使用篇
  6. RabbitMQ基础教程之Spring-JavaConfig-FactoryBean使用姿势

前面一篇介绍了使用工厂方式创建消费者,其中一个不太友好的地方就在配置都是硬编码的方式,不太灵活,那么是否可以结合前一篇的FactoryBean来实现从配置中来灵活的创建消费者呢?

RabbitMQ基础教程之Spring&JavaConfig&FactoryBean使用姿势

RabbitMQ基础教程之Spring使用篇

相关博文,推荐查看:

  1. RabbitMq基础教程之安装与测试
  2. RabbitMq基础教程之基本概念
  3. RabbitMQ基础教程之基本使用篇
  4. RabbitMQ基础教程之使用进阶篇
  5. RabbitMQ基础教程之Spring&JavaConfig使用篇

在前面的一篇演示了如何使用Spring来进行RabbitMQ的消息投递和消费,虽然可以实现基本的需求场景,但是使用起来却并不是特别顺手,首先是不同的消费者,得添加好多不同的配置项,加上有较多的配置(QueueName, ExchangeName, RoutingKey, autoAck…)

那么有没有可能借助工厂方式,来简化消费者这边的大多数配置呢?

RabbitMQ基础教程之Spring&JavaConfig使用篇

RabbitMQ基础教程之Spring使用篇

相关博文,推荐查看:

  1. RabbitMq基础教程之安装与测试
  2. RabbitMq基础教程之基本概念
  3. RabbitMQ基础教程之基本使用篇
  4. RabbitMQ基础教程之使用进阶篇

在实际的应用场景中,将RabbitMQ和Spring结合起来使用的时候可能更加频繁,网上关于Spring结合的博文中,大多都是xml的方式,这篇博文,则主要介绍下利用JavaConfig的结合,又会是怎样的

180531-Spring中JavaConfig知识小结

Sring中JavaConfig使用姿势

去掉xml的配置方式,改成用Java来配置,最常见的就是将xml中的 bean定义, scanner包扫描,属性文件的配置信息读取等

I. 几个基本注解

1. Configuration注解

在javaConfig中注解@Configuration用来代替一个xml文件,可以简单的理解他们的作用是相等的,一般bean的定义也都是放在被这个注解修饰的类中

如一个基本的配置文件如下

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
@Configuration
@ComponentScan("com.git.hui.rabbit.spring")
public class SpringConfig {
private Environment environment;

@Autowired
public void setEnvironment(Environment environment) {
this.environment = environment;
System.out.println("then env: " + environment);
}

@Bean(name="connectionFactory")
public ConnectionFactory connectionFactory() {
CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("/");
return factory;
}

@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
}

2. Bean 注解

上面的例子中,在方法上添加了@Bean注解,这个就相当于传统的

1
<bean name="rabbitAdmin" class="org.springframework.amqp.rabbit.core.RabbitAdmin"/>

因此在需要引入rabbitAdmin实例的地方,可以如下使用

a. 属性字段上添加 @Autowired注解

1
2
3
4
public class RConsumer {
@Autowired
private RabbitAdmin rabbitAdmin;
}

b. 设置方法上添加 @Autowired注解

1
2
3
4
5
6
7
8
public class RConsumer {
private RabbitAdmin rabbitAdmin;

@Autowired
public void setRabbitAdmin(RabbitAdmin rabbitAdmin) {
this.rabbitAdmin = rabbitAdmin;
}
}

c. 使用构造器的方式

1
2
3
4
5
6
public class RConsumer {
private RabbitAdmin rabbitAdmin;
public RConsumer(RabbitAdmin rabbitAdmin) {
this.rabbitAdmin = rabbitAdmin;
}
}

上面就是Spring容器支持的几种典型的IoC方式

3. ComponentScan

这个类似于xml中的 <context:component-scan"/> 标签

1
2
3
@ComponentScan("com.git.hui.rabbit.spring")
public class SpringConfig {
}

上面的这个配置,表示自动扫描包 com.git.hui.rabbit.spring 下面的bean (要求类上添加了 @Component, @Repository, @Service)

那么一个问题来了,如果一个类既被自动扫描加载,又显示定义了bean,会怎样?

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.git.hui.rabbit.spring;

import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class TestBean {
private static AtomicInteger count = new AtomicInteger(1);

public TestBean() {
System.out.println("testBean count: " + count.getAndAdd(1));
}
}

对应的JavaConfig

1
2
3
4
5
6
7
8
@Configuration
@ComponentScan("com.git.hui.rabbit.spring")
public class SpringConfig {
@Bean
public TestBean testBean() {
return new TestBean();
}
}

实际测试,发现这个bean只会有一个实例,即输出计数只会有一条,实际查看ApplicationContext中的内容,TestBean的实例,也确实只有一个,如果改成下面这种场景呢

1
2
3
4
@Bean(name="testBean2")
public TestBean testBean() {
return new TestBean();
}

会有两条记录输出,实际查看容器中的Bean对象,会有两个实例如下

image1

这和我们的预期也是一样的,因为一个类我可能需要多个不同的Bean实例来干一些事情

那么出现这种JavaConfig定义的beanName与自动扫描的冲突的情况会怎样呢?

新增一个NewBean对象,

1
2
3
4
5
6
7
public class NewBean {
private static AtomicInteger count = new AtomicInteger(1);

public NewBean() {
System.out.println(" newbean count: " + count.getAndAdd(1));
}
}

在JavaConfig中新加一个bean定义,但是BeanName与自动扫描的TestBean重复了

1
2
3
4
@Bean(name="testBean")
public NewBean newBean() {
return new NewBean();
}

此时发现有意思的事情了,从Spring容器中,将查不到TestBean的实例,但是可以查到NewBean的实例

image1

这个的表现是:

  • 当beanName出现冲突时,JavaConfig的优先级会高于自动加载的,导致自动加载的Bean不会被加载到容器内

那么跟着来的一个问题就是如果JavaConfig中定义了两个相同的BeanName的bean呢?

1
2
3
4
5
6
7
8
9
@Bean(name = "testBean2")
public NewBean newBean() {
return new NewBean();
}

@Bean(name = "testBean2")
public TestBean testBean() {
return new TestBean();
}

因为我们TestBean上加了@Component注解,因此容器中至少有一个,但是否会有testBean2这个实例呢? 通过实际查看是没有的,testBean2这个名被 NewBean 占领了

image1

so,表现上看,加上实测,将上面的定义换个位置,得出下面的结论

  • 当出现beanName重名时,先定义的Bean占优

然后就是最后一个问题了,当自动扫描时,两个类包不同,但是类名相同,会怎样?

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.git.hui.rabbit.spring.demo;

import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class TestBean {
private static AtomicInteger count = new AtomicInteger(1);

public TestBean() {
System.out.println(" demo.TestBean count: " + count.getAndAdd(1));
}
}

实测,会抛出一个异常,在使用xml的配置方式时,经常见到的一个BeanName冲突的异常

1
org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'testBean' for bean class [com.git.hui.rabbit.spring.demo.TestBean] conflicts with existing, non-compatible bean definition of same name and class [com.git.hui.rabbit.spring.TestBean]

小结:

  • JavaConfig 定义的BeanName与自动扫描的BeanName冲突时,JavaConfig的定义的会被实例化
  • JavaConfig 中定义了BeanName相同的Bean时,优先定义的有效(这里不抛异常不太能理解)
  • 自动扫描的Bean,不支持类名相同,但是包路径不同的场景(会抛异常)

4. Import

在xml配置中,另一个常见的case就是引入另一个xml配置,在JavaConfig中代替的就是Import注解

1
2
3
4
5
@Configuration
@ComponentScan("com.git.hui.rabbit.spring")
@Import({DirectConsumerConfig.class, FanoutConsumerConfig.class, TopicConsumerConfig.class})
public class SpringConfig {
}

这个就等同于xml中常见的:

1
<import resource="service.xml" />

II. 实例测试

1. xml单测姿势

上面说了用JavaConfig代替xml配置的方式,另一个关键的地方就是测试用例的写法了,对于之前的xml,有两种常见的使用姿势

case1: 注解方式

1
2
3
4
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:*.xml")
public class BeanTest {
}

case2: 主动加载容器方式

1
2
3
4
5
6
7
private ServiceA serviceA;

@Before
public void init() {
ApplicationContext apc = new ClassPathXmlApplicationContext("classpath:*.xml");
serviceA = (ServiceA) apc.getBean("serviceA");
}

2. JavaConfig单测使用姿势

那么替换成JavaConfig的用法,也有两种

case1: 注解方式,指定内部classes值

1
2
3
4
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class SprintUnit {
}

case2: 主动加载容器,改为AnnotationConfigApplicationContext

1
2
3
4
5
6
@Test
public void testServiceA() {
ApplicationContext context = new AnnotationConfigApplicationContext(BeansConfiguration.class);
ServiceA serviceA = (ServiceA) context.getBean("serviceA");
serviceA.print();
}

III. 小结

1. 注解映射关系

JavaConfig方式基本上采用的是替换的思路来取代xml,即原xml中的一些东西,可以直接通过注解来代替,如

  • @Configuration 修饰类,与传统的xml文件作用相同
  • @Bean注解,修饰方法,表示声明一个Bean,与原来的xml中的 <bean> 标签作用相同
  • @ComponentScan注解,自动扫描包,类似xml中的 <context:component-scan>
  • @Import注解,与xml中的<import>标签类似,引入其他的配置信息

2. BeanName重名规则

在实际使用中,有一点需要额外注意,对于beanName相同的情况,通过测试的规则如下(没有看源码,不保证完全准确,仅为测试后得出的依据):

  • JavaConfig 定义的BeanName与自动扫描的BeanName冲突时,JavaConfig的定义的会被实例化
  • JavaConfig 中定义了BeanName相同的Bean时,优先定义的有效(这里不抛异常不太能理解)
  • 自动扫描的Bean,不支持类名相同,但是包路径不同的场景(会抛异常)

3. 测试姿势

最简单的就是修改原来的注解@ContextConfiguration中的值

1
@ContextConfiguration(classes = SpringConfig.class)

II. 其他

一灰灰Bloghttps://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

扫描关注

QrCode

RabbitMQ基础教程之使用进阶篇

RabbitMQ基础教程之使用进阶篇

相关博文,推荐查看:

  1. RabbitMq基础教程之安装与测试
  2. RabbitMq基础教程之基本概念
  3. RabbitMQ基础教程之基本使用篇

I. 背景

前一篇基本使用篇的博文中,介绍了rabbitmq的三种使用姿势,可以知道如何向RabbitMQ发送消息以及如何消费,但遗留下几个疑问,本篇则主要希望弄清楚这几点

  • Exchange声明的问题(是否必须声明,如果不声明会怎样)
  • Exchange声明的几个参数(durable, autoDelete)有啥区别
  • 当没有队列和Exchange绑定时,直接往队列中塞数据,好像不会有数据增加(即先塞数据,然后创建queue,建立绑定,从控制台上看这个queue里面也不会有数据)
  • 消息消费的两种姿势(一个主动去拿数据,一个是rabbit推数据)对比

RabbitMQ基础教程之基本使用篇

RabbitMQ基础教程之基本使用篇

最近因为工作原因使用到RabbitMQ,之前也接触过其他的mq消息中间件,从实际使用感觉来看,却不太一样,正好趁着周末,可以好好看一下RabbitMQ的相关知识点;希望可以通过一些学习,可以搞清楚以下几点

  • 基础环境搭建
  • 可以怎么使用
  • 实现原理是怎样的
  • 实际工程中的使用(比如结合SpringBoot可以怎么玩)
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×