場(chǎng)景:在灰度上線某些功能時(shí)盐捷,我們一方面不希望侵入太多的業(yè)務(wù)邏輯,通過注解的方式可以動(dòng)態(tài)的切換到不同的子類上倒堕。
原理:類似于Spring依賴注入爆价,在加載Bean過程中铭段,找到具有特定注解Field,然后通過反射new出一個(gè)新的對(duì)象憔披。
源碼實(shí)現(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的過程中锰霜,依賴注入一個(gè)代理類癣缅。
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;
}
}
動(dòng)態(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());
}
}
請(qǐng)求:http://localhost:8081/route/t1?bs=master
注:上述是MVP版本實(shí)現(xiàn)。在
Route
類中屡立,還可以很多操作万哪,例如打點(diǎn)奕巍、功能上報(bào)、根據(jù)配置動(dòng)態(tài)切換不同子類等等檩坚。