一灰灰blog 一灰灰blog
首页
  • InfluxDB
  • MongoDB
  • MySql
  • 基础系列
  • DB系列
  • 搜索系列
  • MQ系列
  • WEB系列
  • 中间件
  • 运维
  • SpringSecurity
  • SpringCloud
  • QuickAlarm
  • QuickCrawer
  • QuickFix
  • QuickMedia
  • QuickSpi
  • QuickTask
  • 高可用
  • 分类
  • 标签
  • 归档
  • 收藏
  • 关于
GitHub (opens new window)

一灰灰blog

资深搬运工
首页
  • InfluxDB
  • MongoDB
  • MySql
  • 基础系列
  • DB系列
  • 搜索系列
  • MQ系列
  • WEB系列
  • 中间件
  • 运维
  • SpringSecurity
  • SpringCloud
  • QuickAlarm
  • QuickCrawer
  • QuickFix
  • QuickMedia
  • QuickSpi
  • QuickTask
  • 高可用
  • 分类
  • 标签
  • 归档
  • 收藏
  • 关于
GitHub (opens new window)
  • 基础系列

    • 配置

    • AOP

    • Bean

      • 【基础系列】Bean之基本定义与使用
      • 【基础系列】Bean之自动加载
      • 【基础系列】Bean之动态注册
      • 【基础系列】Bean之注销与动态注册实现服务mock(应用篇)
        • I. 应用说明
          • 1. 背景
          • 2. 方案
          • 3. 实现
          • 4. 扩展
        • II. 其他
          • 0. 项目
          • 1. 一灰灰Blog
          • 2. 声明
          • 3. 扫描关注
      • 【基础系列】Bean之条件注入@Condition使用姿势
      • 【基础系列】Bean之@ConditionalOnBean与@ConditionalOnClass
      • 【基础系列】Bean之条件注入@ConditionalOnExpression
      • 【基础系列】Bean之条件注入@ConditionalOnProperty
      • 【基础系列】Bean之多实例选择
      • 【基础系列】FactoryBean及代理实现SPI机制的实例(应用篇)
      • 【配置系列】Bean加载顺序之错误使用姿势辟谣
      • 【基础系列】指定Bean初始化顺序的若干姿势
      • 【基础系列】从0到1实现一个自定义Bean注册器(应用篇)
      • 【基础系列】自动配置选择生效
      • 【基础系列-实战】如何指定bean最先加载(应用篇)
      • 【基础系列】实现一个自定义的@Autowired(应用篇)
      • 【基础系列】SpringContext.getBean()方法调用导致NPE?
    • SpEL

    • 事件

    • 国际化

    • 定时器

    • 日志

  • DB系列

  • 搜索系列

  • MQ系列

  • WEB系列

  • 中间件

  • 运维

  • SpringSecurity

  • SpringCloud

  • Spring系列
  • 基础系列
  • Bean
一灰灰
2018-10-17

【基础系列】Bean之注销与动态注册实现服务mock(应用篇)

前面一篇博文介绍了动态注册Bean的姿势,看完之后难免会有个疑问,在我n年的业务开发中,还真没遇到过需要自己来注册bean的场景(常年的if-else, curd还真不可能遇到)那么这个东西到底有什么用,或者可以给我们打开哪些思路呢?

本篇博文将以应用的角度,简单的演示一下可以怎么用

# I. 应用说明

# 1. 背景

在实际的业务开发中,一个需求来了,我需要依赖第三方提供的接口,但实际的情况可能是对方还没开发好,接口没法提供,这个时候我要测试自己的功能可以怎么做?

  • 在依赖的接口上做特殊处理,不直接调用接口,直接返回mock的结果
  • 测试用例中可以使用MockService来替换某些服务

上面两个可以说是比较常见的使用手段了,再把上面的case进行扩展下,假设我现在提供的一个web服务,正常访问接口是要求用户登录的;但是我希望在本地测试环境下,不登录也可以访问(即给一个默认的登录账号)

针对这个场景进行分析,一是要求本地正常启动服务;二是登录服务默认返回true

# 2. 方案

对上面的场景进行简单化,实例说明

即我有一个web服务,每次访问,都依赖了UserService根据用户名获取用户ID;

要求在本地环境下测试时,使用mock的UserService返回用户id,模拟已经登录的情况

在非本地环境,则通过rpc调用用户服务来走具体的业务流程
1
2
3
4
5

对于上面的这个case可以怎么实现呢?

结合主题,判断当前环境,如果是本地,则删除Spring容器中的UserService的Bean,然后将自己创建的模拟UserService类注册到Bean中,使其他对UserService的引用,替换为mock的UserService

# 3. 实现

根据上面的实现,首先是定义一个UserService的接口类

public interface IUserService {
    Integer getUserId(String uname);
}
1
2
3

给它一个默认实现,表示在正常环境中,实际调用的都是 UserServiceImpl

@Service("userService")
public class UserServiceImpl implements IUserService {
    @Override
    public Integer getUserId(String uname) {
        return 1;
    }
}
1
2
3
4
5
6
7

给一个测试的服务

@RestController
@RequestMapping(path = "mock")
public class MockRest {
    @Autowired
    private IUserService userService;

    @GetMapping(path = "id")
    public String getId(@RequestParam String name) {
        return userService.getUserId(name).toString();
    }
}
1
2
3
4
5
6
7
8
9
10
11

正常情况下,上面的rest服务访问时,每次都应该返回1,即调用的是默认的UserServiceImpl

现在我们就需要加上一个逻辑,如果是本地环境时,使用自己创建的UserService来替换,也就是说这里涉及到了一个bean的注销和手动注册Bean,借助前面的知识也比较好实现了

@Configuration
public class UserServiceMockConfig implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory factory)
            throws BeansException {
        // 先删除容器中的Bean定义
        ((DefaultListableBeanFactory) factory).removeBeanDefinition("userService");

        // 创建mock的Bean,并注册到Spring容器
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(IUserService.class, () -> uname -> {
            Random random = new Random();
            return random.nextInt(1024);
        });

        BeanDefinition beanDefinition = builder.getRawBeanDefinition();
        ((DefaultListableBeanFactory) factory).registerBeanDefinition("userService", beanDefinition);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

上面手动注册的一个生成的匿名UserService类,内部返回的随机的userId, 因此在本地环境启用时,每次调用前面的rest服务时,返回随机的userId,而不是固定的1

演示图

# 4. 扩展

上面只是给出了一个简单的应用场景和实现,在实际的工程中有没有这样的case呢?

在使用SprigCloud的Feign时,就感觉到了这种思路,Feign封装了SpringCloud的RPC调用方式,定义一个接口,对于使用者而言,可以注入这个接口,然后像调用本地方法一样调用执行rpc调用

这里面必然就涉及到接口的代理类生成与注册的问题,而这个过程肯定不会是Spring框架来完成的,也就只有可能是FeignClient来包装的,目前还没有看Feign的源码,所以也不好下结论,也就只能直观的分析,这里面应该少不了Bean的动态注册手段了;关于底层是否如预期这般,静候后续源码分析

# II. 其他

# 0. 项目

  • 工程:spring-boot-demo (opens new window)
  • module: 006-dynamicbean (opens new window)

# 1. 一灰灰Blog

  • 一灰灰Blog个人博客 https://blog.hhui.top (opens new window)
  • 一灰灰Blog-Spring专题博客 http://spring.hhui.top (opens new window)

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

# 2. 声明

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

  • 微博地址: 小灰灰Blog (opens new window)
  • QQ: 一灰灰/3302797840

# 3. 扫描关注

一灰灰blog

QrCode

知识星球

goals

编辑 (opens new window)
#Bean#应用
上次更新: 2021/10/15, 19:56:22
【基础系列】Bean之动态注册
【基础系列】Bean之条件注入@Condition使用姿势

← 【基础系列】Bean之动态注册 【基础系列】Bean之条件注入@Condition使用姿势→

最近更新
01
【基础系列】基于maven多环境配置
04-25
02
【WEB系列】内嵌Tomcat配置Accesslog日志文件生成位置源码探索
04-24
03
【搜索系列】ES查询常用实例演示
04-18
更多文章>
Theme by Vdoing | Copyright © 2017-2022 一灰灰Blog
MIT License | 鄂ICP备18017282号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×