我是靠谱客的博主 顺心店员,最近开发中收集的这篇文章主要介绍java spi 扩展_SPI扩展机制在框架中的使用,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

public class ExtensionLoader{private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);private ConcurrentMap singletonInstances = null;//存放所有扩展点类的集合,key是扩展名,每一个扩展文件中可以定义多个扩展类

private ConcurrentMap> extensionClasses = null;//加载该路径下所有扩展类

private static final String SPI_LOCATION = "META-INF/services/";privateClassLoader classLoader;private Classtype;private volatile boolean init = false;//存储扩展点对象和其对应的扩展加载器

private static final Map, ExtensionLoader>> extensionLoaders = new ConcurrentHashMap<>();//通过构造方法设置类加载器,使用Thread.currentThread().getContextClassLoader(),如果没有setContextClassLoader()则为系统类加载器AppClassLoader

private ExtensionLoader(Classtype) {this(type, Thread.currentThread().getContextClassLoader());

}private ExtensionLoader(Classtype, ClassLoader classLoader) {this.type =type;this.classLoader =classLoader;

}

@SuppressWarnings("unchecked")public static ExtensionLoader getExtensionLoader(Classtype) {//根据扩展点类型获取对应的扩展加载器

ExtensionLoader loader = (ExtensionLoader) extensionLoaders.get(type);if (loader == null) {//如果没有该扩展加载器,则进行初始化

loader =initExtensionLoader(type);

}returnloader;

}//初始化方法是静态的且加了锁,相当于在类对象上加锁,可以保证同时只有一个扩展器的初始化操作

@SuppressWarnings("unchecked")private static synchronized ExtensionLoader initExtensionLoader(Classtype) {

ExtensionLoader loader = (ExtensionLoader) extensionLoaders.get(type);if (loader == null) {

loader= new ExtensionLoader<>(type);

extensionLoaders.putIfAbsent(type, loader);

loader= (ExtensionLoader) extensionLoaders.get(type);

}returnloader;

}

@SuppressWarnings("unchecked")public ListgetExtensions() {

checkAndInit();

List extensions = new ArrayList<>(extensionClasses.size());for (Map.Entry>entry : extensionClasses.entrySet()) {

extensions.add(getExtension(entry.getKey()));

}

extensions.sort(new ExtensionOrderComparator());returnextensions;

}//根据扩展名获取扩展点对象

publicT getExtension(String name) {

checkAndInit();if (name == null) {return null;

}try{//注意,@Spi是使用在扩展点接口上的,@SpiMeta是使用在实现类上的

Spi spi = type.getAnnotation(Spi.class);//单例类型

if (spi.scope() ==Scope.SINGLETON) {returngetSingletonInstance(name);

}else{//原型类型

Class clz =extensionClasses.get(name);if (clz == null) {return null;

}returnclz.newInstance();

}

}catch(Exception e) {new RuntimeException(type.getName() + ":Error when getExtension " +name, e);

}return null;

}

@SuppressWarnings("unchecked")private T getSingletonInstance(String name) throwsInstantiationException, IllegalAccessException {

T obj=singletonInstances.get(name);if (obj != null) {returnobj;

}

Class clz =extensionClasses.get(name);if (clz == null) {return null;

}//加锁对象为集合对象,确保只有一个线程能创建扩展点对象

synchronized(singletonInstances) {

obj=singletonInstances.get(name);if (obj != null) {returnobj;

}

obj=clz.newInstance();

singletonInstances.put(name, obj);

}returnobj;

}private voidcheckAndInit() {//init被volatile修饰,确保只有一个线程进行初始化

if (!init) {

loadExtensionClasses();

}

}//这里在方法级别加锁,锁对象是扩展点对应的扩展类加载器对象

private synchronized voidloadExtensionClasses() {if(init) {return;

}//将META-INF/services/目录下的扩展点加载进集合中

extensionClasses =loadExtensionClasses(SPI_LOCATION);

singletonInstances= new ConcurrentHashMap<>();

init= true;

}private ConcurrentMap>loadExtensionClasses(String prefix) {//根据前缀和类的全限定名来读取文件,所以这里注意扩展点的文件名称必须是类的全限定名

String fullName = prefix +type.getName();

List classNames = new ArrayList();try{

Enumerationurls;if (classLoader == null) {

urls=ClassLoader.getSystemResources(fullName);

}else{

urls=classLoader.getResources(fullName);

}if (urls == null || !urls.hasMoreElements()) {return new ConcurrentHashMap<>();

}while(urls.hasMoreElements()) {

URL url=urls.nextElement();//解析类

parseUrl(type, url, classNames);

}

}catch(Exception e) {throw new RuntimeException("ExtensionLoader loadExtensionClasses error, prefix: " + prefix + " type: " +type.getClass(), e);

}//将类加载进内存,并放入集合中

returnloadClass(classNames);

}

@SuppressWarnings("unchecked")private ConcurrentMap> loadClass(ListclassNames) {

ConcurrentMap> map = new ConcurrentHashMap>();for(String className : classNames) {try{

Classclz;if (classLoader == null) {//classLoader为空,使用加载当前类的类加载器进行加载

clz = (Class) Class.forName(className);

}else{

clz= (Class) Class.forName(className, true, classLoader);

}

checkExtensionType(clz);

String spiName=getSpiName(clz);if(map.containsKey(spiName)) {new RuntimeException(clz.getName() + ":Error spiName already exist " +spiName);

}else{

map.put(spiName, clz);

}

}catch(Exception e) {

logger.error(type.getName()+ ":" + "Error load spi class", e);

}

}returnmap;

}private void checkExtensionType(Classclz) {

checkClassPublic(clz);

checkConstructorPublic(clz);

checkClassInherit(clz);

}private void checkClassPublic(Classclz) {if (!Modifier.isPublic(clz.getModifiers())) {new RuntimeException(clz.getName() + ":Error is not a public class");

}

}private void checkClassInherit(Classclz) {if (!type.isAssignableFrom(clz)) {new RuntimeException(clz.getName() + ":Error is not instanceof " +type.getName());

}

}private void checkConstructorPublic(Classclz) {

Constructor>[] constructors =clz.getConstructors();if (constructors == null || constructors.length == 0) {new RuntimeException(clz.getName() + ":Error has no public no-args constructor");

}for (Constructor>constructor : constructors) {if (Modifier.isPublic(constructor.getModifiers()) && constructor.getParameterTypes().length == 0) {return;

}

}new RuntimeException(clz.getName() + ":Error has no public no-args constructor");

}public String getSpiName(Class>clz) {

SpiMeta spiMeta= clz.getAnnotation(SpiMeta.class);//如果SpiMeta中没有定义name属性,则使用类型,如@SpiMeta(name = "coreSamplePrinter")

return (spiMeta != null && !"".equals(spiMeta.name())) ?spiMeta.name() : clz.getSimpleName();

}private void parseUrl(Class type, URL url, List classNames) throwsServiceConfigurationError {

InputStream inputStream= null;

BufferedReader reader= null;try{

inputStream=url.openStream();

reader= new BufferedReader(newInputStreamReader(inputStream, StandardCharsets.UTF_8));

String line;int indexNumber = 0;while ((line = reader.readLine()) != null) {

indexNumber++;

parseLine(type, url, line, indexNumber, classNames);

}

}catch(Exception x) {

logger.error(type.getName()+ ":" + "Error reading spi configuration file", x);

}finally{try{if (reader != null) {

reader.close();

}if (inputStream != null) {

inputStream.close();

}

}catch(IOException y) {

logger.error(type.getName()+ ":" + "Error closing spi configuration file", y);

}

}

}private void parseLine(Class type, URL url, String line, int lineNumber, List names) throwsIOException,

ServiceConfigurationError {int ci = line.indexOf('#'); //可以使用#在扩展文件后写一些说明

if (ci >= 0) {

line= line.substring(0, ci);

}

line=line.trim();if (line.length() <= 0) {return;

}if ((line.indexOf(' ') >= 0) || (line.indexOf('t') >= 0)) {throw new RuntimeException(type.getName() + ": " + "Illegal spi configuration-file syntax");

}int cp = line.codePointAt(0);if (!Character.isJavaIdentifierStart(cp)) {throw new RuntimeException(type.getName() + ": " + url + ": " + line + ": " + "Illegal spi provider-class name: " +line);

}for (int i = Character.charCount(cp); i < line.length(); i +=Character.charCount(cp)) {

cp=line.codePointAt(i);if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) {throw new RuntimeException(type.getName() + ": " + url + ": " + line + ": " + "Illegal spi provider-class name: " +line);

}

}if (!names.contains(line)) {

names.add(line);

}

}

}

最后

以上就是顺心店员为你收集整理的java spi 扩展_SPI扩展机制在框架中的使用的全部内容,希望文章能够帮你解决java spi 扩展_SPI扩展机制在框架中的使用所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部