Java设计模式实战

举报
赵KK日常技术记录 发表于 2023/06/30 15:24:29 2023/06/30
【摘要】 当业务场景中出现类似支付的不同支付方式,登录的不同策略等场景时,业务代码根据选项的不同选择不同的实现类实例进行调用,即动态绑定。SpringFactoriesLoaderSpringFactoriesLoader类的主要作用是通过类路径下的META-INF/spring.factories文件获取工厂类接口的实现类。源码:public final class SpringFactoriesL...

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

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

源码:
public final class SpringFactoriesLoader {

/**

  • The location to look for factories.
  • 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 Iroman
I i’m SpiderMan

Dubbo SPI

SPI即service provider interface,是在运行动态替换发现的机制,标识接口为@SPI
@SPI
public 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.Iroman
SpiderMan=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
@AllArgsConstructor
public 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)
@Documented
public @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已经存放了实体类映射关系。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: [email protected]
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。