在spring容器中對中間件bean進行替換

在應用中,當我們進行集成測試或者單元測試的時候,有些中間件因爲涉及到了外部請求,所以想把測試拉起來,顯得比較困難,但是由於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被替換了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章