在應用中,當我們進行集成測試或者單元測試的時候,有些中間件因爲涉及到了外部請求,所以想把測試拉起來,顯得比較困難,但是由於spring中,我們可以對bean進行替換,所以這個事兒變得簡單了。
我們需要基於BeanDefinitionRegistryPostProcessor 和 PriorityOrdered兩個類,來對bean進行處理:
package org.tiny.upgrade.plugin.mock.mockbiz.mockbiz;//package com.jd.migration.service.replace; import com.alibaba.fastjson.JSON; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; import org.springframework.stereotype.Component; //@Component public class BeanReplace implements BeanDefinitionRegistryPostProcessor, PriorityOrdered { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { System.out.println(JSON.toJSONString("xxxx"+beanDefinitionRegistry)); for (String beanDefinitionName : beanDefinitionRegistry.getBeanDefinitionNames()) { String beanName = "redisClient"; if(beanDefinitionName.equals(beanName)){ //移除原有bean beanDefinitionRegistry.removeBeanDefinition(beanName); System.out.println("註冊新的mock beanName:"+beanName); RootBeanDefinition beanDefinition = (RootBeanDefinition) BeanDefinitionBuilder.rootBeanDefinition(BeanFactory.class) .setFactoryMethod("mock") .addConstructorArgValue(loadClass("org.tiny.cli.Cluster", beanDefinitionRegistry)) .setLazyInit(false) .setScope(BeanDefinition.SCOPE_SINGLETON) .getBeanDefinition(); beanDefinition.setTargetType(loadClass("org.tiny.cli.Cluster", beanDefinitionRegistry)); //注入mock的bean beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition); } } } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE - 10000; } public Class loadClass(String className, BeanDefinitionRegistry beanDefinitionRegistry) throws RuntimeException { ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanDefinitionRegistry; Class clazz = tryLoadClass(cbf.getBeanClassLoader(), className); if (clazz != null) { return clazz; } return tryLoadClass(cbf.getTempClassLoader(), className); } private Class tryLoadClass(ClassLoader classLoader, String interfaceClassName) { try { return classLoader.loadClass(interfaceClassName); } catch (ClassNotFoundException e) { // 忽略 } return null; } }
從上面流程中,我們可以看到,我們先是移除了已經註冊好的bean,之後創建了一個新的mock bean,並註冊到spring的bean容器中。
由於這裏面,我們用到了工廠方法:.setFactoryMethod("mock"), 所以我們需要定義一個工廠類:
public class BeanFactory { public static <T> T mock(Class<T> clazz) { System.out.println("創建mock的clazz:" + clazz.getName()); return Mockito.mock(clazz); } public static Object original(Object obj) { System.out.println("創建mock的對象:" + obj.getClass().getName()); return obj; } }
跑起項目,可以看到bean被替換了。