Browse Source

support spi

Looly 5 years ago
parent
commit
bd6b07f6b9

+ 1 - 0
CHANGELOG.md

@@ -15,6 +15,7 @@
 * 【core  】     增加ServiceLoaderUtil
 * 【core  】     增加EnumUtil.getEnumAt方法
 * 【core  】     增强EnumConvert判断能力(issue#I17082@Gitee)
+* 【log   】     使用SPI机制代替硬编码
 
 ### Bug修复
 

+ 20 - 0
hutool-core/src/main/java/cn/hutool/core/util/ServiceLoaderUtil.java

@@ -1,6 +1,7 @@
 package cn.hutool.core.util;
 
 import java.util.Iterator;
+import java.util.ServiceConfigurationError;
 import java.util.ServiceLoader;
 
 /**
@@ -19,6 +20,25 @@ import java.util.ServiceLoader;
 public class ServiceLoaderUtil {
 
 	/**
+	 * 加载第一个可用服务,如果用户定义了多个接口实现类,只获取第一个不报错的服务。
+	 *
+	 * @param <T>   接口类型
+	 * @param clazz 服务接口
+	 * @return 第一个服务接口实现对象,无实现返回{@code null}
+	 */
+	public static <T> T loadFirstAvailable(Class<T> clazz) {
+		final Iterator<T> iterator = load(clazz).iterator();
+		if(iterator.hasNext()){
+			try {
+				return iterator.next();
+			} catch (ServiceConfigurationError e) {
+				// ignore
+			}
+		}
+		return null;
+	}
+
+	/**
 	 * 加载第一个服务,如果用户定义了多个接口实现类,只获取第一个。
 	 *
 	 * @param <T>   接口类型

+ 9 - 41
hutool-extra/src/main/java/cn/hutool/extra/template/engine/TemplateFactory.java

@@ -1,30 +1,23 @@
 package cn.hutool.extra.template.engine;
 
-import com.jfinal.template.Engine;
-
+import cn.hutool.core.util.ServiceLoaderUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.template.TemplateConfig;
 import cn.hutool.extra.template.TemplateEngine;
 import cn.hutool.extra.template.TemplateException;
-import cn.hutool.extra.template.engine.beetl.BeetlEngine;
-import cn.hutool.extra.template.engine.enjoy.EnjoyEngine;
-import cn.hutool.extra.template.engine.freemarker.FreemarkerEngine;
-import cn.hutool.extra.template.engine.rythm.RythmEngine;
-import cn.hutool.extra.template.engine.thymeleaf.ThymeleafEngine;
-import cn.hutool.extra.template.engine.velocity.VelocityEngine;
 import cn.hutool.log.StaticLog;
+import com.jfinal.template.Engine;
 
 /**
  * 简单模板工厂,用于根据用户引入的模板引擎jar,自动创建对应的模板引擎对象
- * 
- * @author looly
  *
+ * @author looly
  */
 public class TemplateFactory {
 	/**
 	 * 根据用户引入的模板引擎jar,自动创建对应的模板引擎对象<br>
 	 * 推荐创建的引擎单例使用,此方法每次调用会返回新的引擎
-	 * 
+	 *
 	 * @param config 模板配置,包括编码、模板文件path等信息
 	 * @return {@link Engine}
 	 */
@@ -37,41 +30,16 @@ public class TemplateFactory {
 	/**
 	 * 根据用户引入的模板引擎jar,自动创建对应的模板引擎对象<br>
 	 * 推荐创建的引擎单例使用,此方法每次调用会返回新的引擎
-	 * 
+	 *
 	 * @param config 模板配置,包括编码、模板文件path等信息
 	 * @return {@link Engine}
 	 */
 	private static TemplateEngine doCreate(TemplateConfig config) {
-		try {
-			return new BeetlEngine(config);
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new FreemarkerEngine(config);
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new VelocityEngine(config);
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new RythmEngine(config);
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new EnjoyEngine(config);
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new ThymeleafEngine(config);
-		} catch (NoClassDefFoundError e) {
-			// ignore
+		final TemplateEngine engine = ServiceLoaderUtil.loadFirstAvailable(TemplateEngine.class);
+		if(null != engine){
+			return engine;
 		}
+
 		throw new TemplateException("No template found ! Please add one of template jar to your project !");
 	}
 }

+ 5 - 53
hutool-extra/src/main/java/cn/hutool/extra/tokenizer/engine/TokenizerFactory.java

@@ -1,17 +1,9 @@
 package cn.hutool.extra.tokenizer.engine;
 
+import cn.hutool.core.util.ServiceLoaderUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.tokenizer.TokenizerEngine;
 import cn.hutool.extra.tokenizer.TokenizerException;
-import cn.hutool.extra.tokenizer.engine.analysis.SmartcnEngine;
-import cn.hutool.extra.tokenizer.engine.ansj.AnsjEngine;
-import cn.hutool.extra.tokenizer.engine.hanlp.HanLPEngine;
-import cn.hutool.extra.tokenizer.engine.ikanalyzer.IKAnalyzerEngine;
-import cn.hutool.extra.tokenizer.engine.jcseg.JcsegEngine;
-import cn.hutool.extra.tokenizer.engine.jieba.JiebaEngine;
-import cn.hutool.extra.tokenizer.engine.mmseg.MmsegEngine;
-import cn.hutool.extra.tokenizer.engine.mynlp.MynlpEngine;
-import cn.hutool.extra.tokenizer.engine.word.WordEngine;
 import cn.hutool.log.StaticLog;
 
 /**
@@ -38,51 +30,11 @@ public class TokenizerFactory {
 	 * @return {@link TokenizerEngine}
 	 */
 	private static TokenizerEngine doCreate() {
-		try {
-			return new AnsjEngine();
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new HanLPEngine();
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new IKAnalyzerEngine();
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new JcsegEngine();
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new JiebaEngine();
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new MmsegEngine();
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new WordEngine();
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new SmartcnEngine();
-		} catch (NoClassDefFoundError e) {
-			// ignore
-		}
-		try {
-			return new MynlpEngine();
-		} catch (NoClassDefFoundError e) {
-			// ignore
+		final TokenizerEngine engine = ServiceLoaderUtil.loadFirstAvailable(TokenizerEngine.class);
+		if(null != engine){
+			return engine;
 		}
+
 		throw new TokenizerException("No tokenizer found ! Please add some tokenizer jar to your project !");
 	}
 }

+ 6 - 0
hutool-extra/src/main/resources/META-INF/services/cn.hutool.extra.template.TemplateEngine

@@ -0,0 +1,6 @@
+cn.hutool.extra.template.engine.beetl.BeetlEngine
+cn.hutool.extra.template.engine.freemarker.FreemarkerEngine
+cn.hutool.extra.template.engine.velocity.VelocityEngine
+cn.hutool.extra.template.engine.rythm.RythmEngine
+cn.hutool.extra.template.engine.enjoy.EnjoyEngine
+cn.hutool.extra.template.engine.thymeleaf.ThymeleafEngine

+ 9 - 0
hutool-extra/src/main/resources/META-INF/services/cn.hutool.extra.tokenizer.TokenizerEngine

@@ -0,0 +1,9 @@
+cn.hutool.extra.tokenizer.engine.ansj.AnsjEngine
+cn.hutool.extra.tokenizer.engine.hanlp.HanLPEngine
+cn.hutool.extra.tokenizer.engine.ikanalyzer.IKAnalyzerEngine
+cn.hutool.extra.tokenizer.engine.jcseg.JcsegEngine
+cn.hutool.extra.tokenizer.engine.jieba.JiebaEngine
+cn.hutool.extra.tokenizer.engine.mmseg.MmsegEngine
+cn.hutool.extra.tokenizer.engine.word.WordEngine
+cn.hutool.extra.tokenizer.engine.analysis.SmartcnEngine
+cn.hutool.extra.tokenizer.engine.mynlp.MynlpEngine

+ 31 - 36
hutool-log/src/main/java/cn/hutool/log/LogFactory.java

@@ -14,13 +14,12 @@ import cn.hutool.log.dialect.tinylog.TinyLogFactory;
 
 import java.net.URL;
 import java.util.Map;
-import java.util.ServiceConfigurationError;
-import java.util.ServiceLoader;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * 日志工厂类
- * 
+ *
+ * @author Looly
  * @see Slf4jLogFactory
  * @see Log4j2LogFactory
  * @see Log4jLogFactory
@@ -29,20 +28,21 @@ import java.util.concurrent.ConcurrentHashMap;
  * @see JbossLogFactory
  * @see ConsoleLogFactory
  * @see JdkLogFactory
- * 
- * @author Looly
- *
  */
 public abstract class LogFactory {
 
-	/** 日志框架名,用于打印当前所用日志框架 */
+	/**
+	 * 日志框架名,用于打印当前所用日志框架
+	 */
 	protected String name;
-	/** 日志对象缓存 */
+	/**
+	 * 日志对象缓存
+	 */
 	private Map<Object, Log> logCache;
 
 	/**
 	 * 构造
-	 * 
+	 *
 	 * @param name 日志框架名
 	 */
 	public LogFactory(String name) {
@@ -52,7 +52,7 @@ public abstract class LogFactory {
 
 	/**
 	 * 获取日志框架名,用于打印当前所用日志框架
-	 * 
+	 *
 	 * @return 日志框架名
 	 * @since 4.1.21
 	 */
@@ -62,7 +62,7 @@ public abstract class LogFactory {
 
 	/**
 	 * 获得日志对象
-	 * 
+	 *
 	 * @param name 日志对象名
 	 * @return 日志对象
 	 */
@@ -77,7 +77,7 @@ public abstract class LogFactory {
 
 	/**
 	 * 获得日志对象
-	 * 
+	 *
 	 * @param clazz 日志对应类
 	 * @return 日志对象
 	 */
@@ -92,7 +92,7 @@ public abstract class LogFactory {
 
 	/**
 	 * 创建日志对象
-	 * 
+	 *
 	 * @param name 日志对象名
 	 * @return 日志对象
 	 */
@@ -100,7 +100,7 @@ public abstract class LogFactory {
 
 	/**
 	 * 创建日志对象
-	 * 
+	 *
 	 * @param clazz 日志对应类
 	 * @return 日志对象
 	 */
@@ -110,7 +110,7 @@ public abstract class LogFactory {
 	 * 检查日志实现是否存在<br>
 	 * 此方法仅用于检查所提供的日志相关类是否存在,当传入的日志类类不存在时抛出ClassNotFoundException<br>
 	 * 此方法的作用是在detectLogFactory方法自动检测所用日志时,如果实现类不存在,调用此方法会自动抛出异常,从而切换到下一种日志的检测。
-	 * 
+	 *
 	 * @param logClassName 日志实现相关类
 	 */
 	protected void checkLogExist(Class<?> logClassName) {
@@ -118,6 +118,7 @@ public abstract class LogFactory {
 	}
 
 	// ------------------------------------------------------------------------- Static start
+
 	/**
 	 * @return 当前使用的日志工厂
 	 */
@@ -127,7 +128,9 @@ public abstract class LogFactory {
 
 	/**
 	 * 自定义日志实现
-	 * 
+	 *
+	 * @param logFactoryClass 日志工厂类
+	 * @return 自定义的日志工厂类
 	 * @see Slf4jLogFactory
 	 * @see Log4j2LogFactory
 	 * @see Log4jLogFactory
@@ -136,9 +139,6 @@ public abstract class LogFactory {
 	 * @see JbossLogFactory
 	 * @see ConsoleLogFactory
 	 * @see JdkLogFactory
-	 * 
-	 * @param logFactoryClass 日志工厂类
-	 * @return 自定义的日志工厂类
 	 */
 	public static LogFactory setCurrentLogFactory(Class<? extends LogFactory> logFactoryClass) {
 		return GlobalLogFactory.set(logFactoryClass);
@@ -146,7 +146,9 @@ public abstract class LogFactory {
 
 	/**
 	 * 自定义日志实现
-	 * 
+	 *
+	 * @param logFactory 日志工厂类对象
+	 * @return 自定义的日志工厂类
 	 * @see Slf4jLogFactory
 	 * @see Log4j2LogFactory
 	 * @see Log4jLogFactory
@@ -155,9 +157,6 @@ public abstract class LogFactory {
 	 * @see JbossLogFactory
 	 * @see ConsoleLogFactory
 	 * @see JdkLogFactory
-	 * 
-	 * @param logFactory 日志工厂类对象
-	 * @return 自定义的日志工厂类
 	 */
 	public static LogFactory setCurrentLogFactory(LogFactory logFactory) {
 		return GlobalLogFactory.set(logFactory);
@@ -165,7 +164,7 @@ public abstract class LogFactory {
 
 	/**
 	 * 获得日志对象
-	 * 
+	 *
 	 * @param name 日志对象名
 	 * @return 日志对象
 	 */
@@ -175,7 +174,7 @@ public abstract class LogFactory {
 
 	/**
 	 * 获得日志对象
-	 * 
+	 *
 	 * @param clazz 日志对应类
 	 * @return 日志对象
 	 */
@@ -194,7 +193,8 @@ public abstract class LogFactory {
 	 * 决定日志实现
 	 * <p>
 	 * 依次按照顺序检查日志库的jar是否被引入,如果未引入任何日志库,则检查ClassPath下的logging.properties,存在则使用JdkLogFactory,否则使用ConsoleLogFactory
-	 * 
+	 *
+	 * @return 日志实现类
 	 * @see Slf4jLogFactory
 	 * @see Log4j2LogFactory
 	 * @see Log4jLogFactory
@@ -203,7 +203,6 @@ public abstract class LogFactory {
 	 * @see JbossLogFactory
 	 * @see ConsoleLogFactory
 	 * @see JdkLogFactory
-	 * @return 日志实现类
 	 */
 	public static LogFactory create() {
 		final LogFactory factory = doCreate();
@@ -215,7 +214,8 @@ public abstract class LogFactory {
 	 * 决定日志实现
 	 * <p>
 	 * 依次按照顺序检查日志库的jar是否被引入,如果未引入任何日志库,则检查ClassPath下的logging.properties,存在则使用JdkLogFactory,否则使用ConsoleLogFactory
-	 * 
+	 *
+	 * @return 日志实现类
 	 * @see Slf4jLogFactory
 	 * @see Log4j2LogFactory
 	 * @see Log4jLogFactory
@@ -224,16 +224,11 @@ public abstract class LogFactory {
 	 * @see JbossLogFactory
 	 * @see ConsoleLogFactory
 	 * @see JdkLogFactory
-	 * @return 日志实现类
 	 */
 	private static LogFactory doCreate() {
-		final ServiceLoader<LogFactory> factories = ServiceLoaderUtil.load(LogFactory.class);
-		for (LogFactory factory : factories) {
-			try {
-				return factory;
-			} catch (ServiceConfigurationError e) {
-				// ignore
-			}
+		final LogFactory factory = ServiceLoaderUtil.loadFirstAvailable(LogFactory.class);
+		if(null != factory){
+			return factory;
 		}
 
 		// 未找到任何可支持的日志库时判断依据:当JDK Logging的配置文件位于classpath中,使用JDK Logging,否则使用Console