底层api调用上层代码

267 阅读3分钟

底层api调用上层代码

假如有两个java普通项目java-api, java-api-impl,后者是前者的实现并依赖前者, 我们要在java-api调用java-api-impl的具体实现怎么设计呢?

  • 反射
  • java spi(本质是反射+文件)
  • 反射+注解
  • 反射+文件(spring.factroy)

反射-工厂模式

//java-api
public interface Say {
    void say();
}
​
public class SayFactory {
    public static <T implements Say> T create(Class<T> type) {
        try {
            T t = type.newInstance();
            return t;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
}
​
//java-api-impl
public class ZhanSanSay implements Say {
    @Override
    public void say() {
        System.out.println("zhansan say");
    }
}
​
//test 
Say say = SayFactory.create(ZhanSanSay.class);
say.say();

反射-driver模式

//java-api
public interface Say {
    void say();
}
​
public class SayManage {
    public final static List<Say> sayList = new ArrayList<>();
​
    public static void registerSay(Say say){
        if(say != null){
            sayList.add(say);
        }
    }
​
    public static Say getSay(){
        if(sayList.size()>0){
            return sayList.get(0);
        }
        throw new RuntimeException("Not find Say!");
    }
}
​
//java-api-impl
public class ZhanSanSay implements Say {
​
    static {
        SayManage.registerSay(new ZhanSanSay());
    }
​
    @Override
    public void say() {
        System.out.println("zhansan say");
    }
}
​
//test
Class.forName("com.example.ZhanSanSay");
​
Say say = SayManage.getSay();
say.say();

反射-slf4j模式(slf4j 1.8版本以前)

//java-api
public interface Say {
    void say();
}
//java-api
// 这里会报出StaticSayBinder找不到红色错误,我们编译时在java-api项目中自定义StaticSayBinder一个实现,打包时用maven插件删除它
public class SayFactory {
    private static volatile boolean init = false;
​
    public static Say getSay() {
        if(!init){
            bind();
        }
        return StaticSayBinder.getSingleton().getSay();
    }
​
    private final static void bind() {
        try {
            StaticLoggerBinder.getSingleton();
            init = true;
        } catch (NoClassDefFoundError ncde) {
            throw new RuntimeException("Not find Say !");
        } catch (Exception e) {
            throw new RuntimeException("Unexpected initialization failure", e);
        }
    }
}
​
//java-api-impl
public class StaticSayBinder {
​
    private static StaticSayBinder SINGLETON = new StaticSayBinder();
​
    public static StaticSayBinder getSingleton() {
        return SINGLETON;
    }
​
    public Say getSay(){
        return new ZhanSanSay();
    }
}
public class ZhanSanSay implements Say {
    @Override
    public void say() {
        System.out.println("zhansan say");
    }
}
​
//test
Say say = SayFactory.getSay();
say.say();

java spi(slf4j 1.8及以后)

ServiceLoader是jdk6里面引进的一个特性。它用来实现SPI(Service Provider Interface),一种服务发现机制,很多框架用它来做来做服务的扩展发现。

首先定义一个SPIService接口
//java-api
public interface Say {
    void say();
}
再定义两个实现类
//java-api-impl
public class ZhanSanSay implements Say {
    @Override
    public void say() {
        System.out.println("zhansan say");
    }
}
​
public class LiSiSay implements Say {
    @Override
    public void say() {
        System.out.println("lisi say");
    }
}

在java-api-impl项目ClassPath路径META-INF/services/下添加配置文件com.study.Say,META-INF/services/com.study.Say,文件名为接口的全限定类名。在配置文件中加入两个实现类的全限定类名。

com.study.impl.ZhanSanSay
com.study.impl.LiSiSay
写一个测试类SPITest.java
public class SPITest {
  public static void main(String[] args) {
    ServiceLoader<Say> loaders = ServiceLoader.load(Say.class);
    Iterator<Say> it = loaders.iterator();
    while (it.hasNext()) {
      Say say= it.next();
      say.say();
    }
  }
}

运行结果:

zhansan say
lisi say

ServiceLoader的load方法将在META-INF/services/com.study.Say中配置的子类都进行了加载。

反射+注解

先添加依赖:

<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.11</version>
</dependency>

在加一个注解扫描工具类:

public class AnnotationUtil {
​
    public static <T> Set<Class<? extends T>> findClassList(Class<? extends T> clazz, Class<? extends Annotation> annotationClass) {
        String packageName = clazz.getPackage().getName();
        Reflections reflections = new Reflections(packageName);
        return reflections.getTypesAnnotatedWith(annotationClass)
                .stream()
                .filter(clazz::isAssignableFrom)
                .map(c -> (Class<? extends T>) c)
                .collect(Collectors.toSet());
    }
}

具体实现:

//java-api
public interface Say {
    void say();
}
public @interface SayAnnotation {
}
public class SayFactory {
​
    public static Say getSay() throws RuntimeException {
        Set<Class<? extends Say>> classList = AnnotationUtil.findClassList(Say.class, SayAnnotation.class);
        return classList.stream()
                .findFirst()
                .map(clazz->{
                    Say say = null;
                    try {
                        say = clazz.newInstance();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    return say;
                })
                .<RuntimeException>orElseThrow(()->{
                    throw new RuntimeException("Not find Say !");
                });
    }
    
}
​
//java-api-impl
//这里需要注意:ZhanSanSay的包名一定要和Say的包名一样,不然注解扫描不到
@SayAnnotation
public class ZhanSanSay implements Say {
    @Override
    public void say() {
        System.out.println("zhansan say");
    }
}
​
//test
Say say = SayFactory.getSay();
say.say();

反射+文件(spring.factory)

添加依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.3.21</version>
</dependency>

具体实现:

//java-api
public interface Say {
    void say();
}
public class SayFactory {
    public static Say getSay(){
        return SpringFactoriesLoader.loadFactoryNames(Say.class, null)
                .stream()
                .map(className->{
                    Say factoryInstance;
                    try {
                        Class<?> aClass = Class.forName(className);
                        factoryInstance = (Say) aClass.newInstance();
                    } catch (ClassNotFoundException|InstantiationException|IllegalAccessException e) {
                        e.printStackTrace();
                        throw new RuntimeException("Not find Say !");
                    }
                    return factoryInstance;
                })
                .findFirst()
                .<RuntimeException>orElseThrow(()->{
                    throw new RuntimeException("Not find Say !");
                });
    }
}
​
//java-api-impl
public class ZhanSanSay implements Say {
    @Override
    public void say() {
        System.out.println("zhansan say");
    }
}
//java-api-impl项目中添加文件classpath:META-INF/spring.factories,其内容为(根据实际情况添加全类名):
com.study.api.Say=\
com.study.api.impl.ZhanSanSay
    
//test
Say say = SayFactory.getSay();
say.say();