181017-SpringBoot应用篇Bean之注销与动态注册实现服务mock

文章目录
  1. I. 应用说明
    1. 1. 背景
    2. 2. 方案
    3. 3. 实现
    4. 4. 扩展
  2. II. 其他
    1. 0. 项目
    2. 1. 一灰灰Blog
    3. 2. 声明
    4. 3. 扫描关注

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

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

I. 应用说明

1. 背景

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

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

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

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

2. 方案

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

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

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

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

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

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

3. 实现

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

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

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

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

给一个测试的服务

1
2
3
4
5
6
7
8
9
10
11
@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();
}
}

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

现在我们就需要加上一个逻辑,如果是本地环境时,使用自己创建的UserService来替换,也就是说这里涉及到了一个bean的注销和手动注册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
@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);
}
}

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

演示图

4. 扩展

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

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

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

II. 其他

0. 项目

1. 一灰灰Blog

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

2. 声明

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

3. 扫描关注

一灰灰blog

QrCode

知识星球

goals


打赏 如果觉得我的文章对您有帮助,请随意打赏。
分享到