Spring使用“注解”(自定義實現(xiàn)依賴注入)實現(xiàn)動態(tài)切換Bean

場景:在灰度上線某些功能時,我們一方面不希望侵入太多的業(yè)務邏輯,通過注解的方式可以動態(tài)的切換到不同的子類上。

原理:類似于Spring依賴注入,在加載Bean過程中,找到具有特定注解Field,然后通過反射new出一個新的對象。

源碼實現(xiàn)

自定義注解:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RouteAnno {

    String master() default "";

    String gray() default "";
}

Bean的后置處理器


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class RouteBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        ReflectionUtils.doWithFields(bean.getClass(), field -> {
            //類似于Sprig的依賴注入,在加載bean的過程中,依賴注入一個代理類。
            RouteAnno annotation = field.getAnnotation(RouteAnno.class);
            if (annotation != null) {
                Route.buildRoute(annotation.master(), annotation.gray());
                try {
                    field.setAccessible(true);
                    field.set(bean, Route.buildRoute(annotation.master(), annotation.gray()));
                } catch (Exception e) {
                    log.error("", e);
                }
            }
        });
        return bean;
    }
}

動態(tài)路由工程

@Component
public class Route<T> {

    private Map<String, String> cache = new ConcurrentHashMap<>();

    public static final ThreadLocal<String> bsTL = new ThreadLocal<>();

    public static <T> Route<T> buildRoute(String master, String gray) {
        Route<T> route = new Route<>();
        route.put("master", master);
        route.put("gray", gray);
        return route;
    }

    private void put(String name, String bean) {
        cache.put(name, bean);
    }

    public T getBean() {
        //通過參數(shù)來控制例如此處
        if (bsTL.get().equals("gray")) {
            return (T) SpringUtil.getBean(cache.get("gray"));
        }
        return (T) SpringUtil.getBean(cache.get("master"));
    }

}

使用方式

@Service
@Slf4j
public class ARouteService {

    @RouteAnno(master = "baseServiceImpl", gray = "baseServiceMockImpl")
    private Route<BaseService> baseServiceRoute;

    public String test() {

        return baseServiceRoute.getBean().doCheck();
    }
}

調(diào)用者

@Slf4j
@RestController
public class RouteController {

    @Autowired
    private ARouteService aRouteService;

    @GetMapping(value = "/route/t1")
    public ResponseObject<String> t1(@RequestParam("bs") String bs) {
        bsTL.set(bs);
        return ResponseObject.success(aRouteService.test());
    }
}

請求:http://localhost:8081/route/t1?bs=master

注:上述是MVP版本實現(xiàn)。在Route類中,還可以很多操作,例如打點、功能上報、根據(jù)配置動態(tài)切換不同子類等等。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容