炫技?No.

原创
06/06 21:27
阅读数 0

当业务场景中出现类似支付的不同支付方式,登录的不同策略等场景时,业务代码根据选项的不同选择不同的实现类实例进行调用,即动态绑定。


SpringFactoriesLoader

SpringFactoriesLoader类的主要作用是通过类路径下的META-INF/spring.factories文件获取工厂类接口的实现类。


源码:
public final class SpringFactoriesLoader {
/** * The location to look for factories. * <p>Can be present in multiple JAR files. */ public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();

private SpringFactoriesLoader() { } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; }
try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }


书写接口:

public interface DynamicBind {        void  sayHello();}


书写不同实现类:

public class Iroman implements DynamicBind {    @Override    public void sayHello() {        System.out.println("I i'm  Iroman");    }}public class SpiderMan implements DynamicBind {    @Override    public void sayHello() {        System.out.println("I i'm  SpiderMan");    }}

在spring.factories配置实现类,k是接口的全限定类名,v是实现类
com.springcloud.service.DynamicBind=com.springcloud.service.impl.Iroman,com.atguigu.springcloud.service.impl.SpiderMan


测试类:

public static void main(String[] args) {        List<DynamicBind> dynamicBinds = SpringFactoriesLoader.loadFactories(DynamicBind.class, ClassUtils.getDefaultClassLoader());        for (DynamicBind dynamicBind : dynamicBinds) {            dynamicBind.sayHello();        }    }


输出:

I i'm  IromanI i'm  SpiderMan



Dubbo SPI

SPI即service provider interface,是在运行动态替换发现的机制, 标识接口为@SPI
@SPIpublic interface DynamicBindService {    void  sayHello();}
public class SpiderMan implements DynamicBindService { @Override public void sayHello() { System.out.println("Dubbo I I'm SpiderMan"); }}public class Iroman implements DynamicBindService { @Override public void sayHello() { System.out.println("Dubbo I I'm Iroman"); }}


仍要在配置文件中指定:

Iroman=org.apache.dubbo.demo.provider.Service.Impl.IromanSpiderMan=org.apache.dubbo.demo.provider.Service.Impl.SpiderMan


测试类:

 public static void main(String[] args) {        ExtensionLoader<DynamicBindService> extensionLoader =                ExtensionLoader.getExtensionLoader(DynamicBindService.class);        DynamicBindService iroman = extensionLoader.getExtension("Iroman");        DynamicBindService spiderMan = extensionLoader.getExtension("SpiderMan");        iroman.sayHello();        spiderMan.sayHello();    }

以上两种方法全部需要在配置文件中配置,如果需要添加新的还要改动配置文件,且SPI局限于Dubbo框架。

枚举动态绑定

根据参数或者URL提前绑定与实现类的关系,从而决定不同参数调用不同实现类。
@Getter@AllArgsConstructorpublic enum DynamicStrategyEnum {
蜘蛛侠(DynamicEnum.蜘蛛侠.getCode(), Iroman.class),
钢铁侠(DynamicEnum.钢铁侠.getCode(), SpiderMan.class),
美队(DynamicEnum.美队.getCode(), CaptainAmerica.class);
private String whoImI;
private Class<? extends DynamicBind> clazz;
public static Class<? extends DynamicBind> getClazz(String param) { if (StringUtils.isEmpty(param)) { throw new NullPointerException("param is null"); } else { for (DynamicStrategyEnum strategyEnum : DynamicStrategyEnum.values()) { if (strategyEnum.getWhoImI().equals(param)) { return strategyEnum.getClazz(); } } throw new NullPointerException(String.format("param is null is %s", param)); } }
}


接口实现类:

public interface DynamicBind {
void sayHello();}public class CaptainAmerica implements DynamicBind { @Override public void sayHello() { System.out.println("美国翘臀"); }}public class Iroman implements DynamicBind { @Override public void sayHello() { System.out.println("I i'm Iroman"); }}public class SpiderMan implements DynamicBind { @Override public void sayHello() {        System.out.println("I i'm  SpiderMan"); }}


调用时:

   DynamicBind bind = ctx.getBean(DynamicStrategyEnum.getClazz(DynamicEnum.蜘蛛侠.getCode()));   bind.sayHello();


Autowire+枚举+注解

此时的实体关系类是由枚举维护的,如果新加实现需要增加枚举维护,将实现类由注解维护,新实现类通过注解即可标识。


自定义注解与枚举维护:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface AutoHandlerDynamicType {
DynamicStrategyEnum handlerModel();
}


实现类标识不同注解:

@AutoHandlerDynamicType (handlerModel = DynamicStrategyEnum.钢铁侠)public class IronMan implements DynamicBind {    @Override    public void sayHello() {        System.out.println("I   i'm  Iron man");    }}


仍然由枚举维护获取哪个实现类的:

private Map<DynamicStrategyEnum, DynamicBind> autoDynamiceHandlerMap;    @Autowired    public void setAutoDynamicHandlerMap(List<DynamicBind> autoDynamicHandlerList) {        autoDynamiceHandlerMap = autoDynamiceHandlerList.stream()                .collect(Collectors.toMap(                        autoDynamicHandler -> AnnotationUtils.findAnnotation(DynamicBind.getClass(), DynamicStrategyEnum.class).handlerModel(),                        autoDynamicHandler -> autoDynamicHandler));    }


Autowire作用于方法时Spring会先实例化所有Bean,然后根据配置进行扫描,当检测到@Autowired后进行注入,注入时调用这个方法,也就是当启动完容器,map已经存放了实体类映射关系。


这几种方法能够在不同场景下实现动态绑定,视情况所需来使用。 如有其他方法可在下面页面留言,PC端谷歌浏览器打开,但不要攻击~

http://47.105.116.133:8089/#/room

本文分享自微信公众号 - 赵KK日常技术记录(gh_cc4c9f1a9521)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部