欢迎点击头像进入主页查看更多内容....
当业务场景中出现类似支付的不同支付方式,登录的不同策略等场景时,业务代码根据选项的不同选择不同的实现类实例进行调用,即动态绑定。
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 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已经存放了实体类映射关系。