• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

Spring Boot 应用篇–Bean(1):注销与动态注册实现服务mock

Spring Boot winrains 来源:一灰灰Blog 12个月前 (11-08) 78次浏览

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

I. 应用说明

1. 背景

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

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

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

2. 方案

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

即我有一个web服务,每次访问,都依赖了UserService根据用户名获取用户ID;
要求在本地环境下测试时,使用mock的UserService返回用户id,模拟已经登录的情况
在非本地环境,则通过rpc调用用户服务来走具体的业务流程

对于上面的这个case可以怎么实现呢?
结合主题,判断当前环境,如果是本地,则删除Spring容器中的UserService的Bean,然后将自己创建的模拟UserService类注册到Bean中,使其他对UserService的引用,替换为mock的UserService

3. 实现

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

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

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

@Service("userService")
public class UserServiceImpl implements IUserService {
    @Override
    public Integer getUserId(String uname) {
        return 1;
    }
}

给一个测试的服务

@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,借助前面的知识也比较好实现了

@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的动态注册手段了;关于底层是否如预期这般,静候后续源码分析

作者:一灰灰Blog

来源:http://spring.hhui.top/spring-blog/2018/10/17/181017-SpringBoot%E5%BA%94%E7%94%A8%E7%AF%87Bean%E4%B9%8B%E6%B3%A8%E9%94%80%E4%B8%8E%E5%8A%A8%E6%80%81%E6%B3%A8%E5%86%8C%E5%AE%9E%E7%8E%B0%E6%9C%8D%E5%8A%A1mock/


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (0)