From bd6b07f6b96fcf7b10edac34c1d0af4f6bf1ac76 Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 2 Mar 2020 18:08:58 +0800 Subject: [PATCH] support spi --- CHANGELOG.md | 1 + .../hutool/core/util/ServiceLoaderUtil.java | 20 ++++++ .../template/engine/TemplateFactory.java | 50 +++----------- .../tokenizer/engine/TokenizerFactory.java | 58 ++-------------- .../cn.hutool.extra.template.TemplateEngine | 6 ++ .../cn.hutool.extra.tokenizer.TokenizerEngine | 9 +++ .../main/java/cn/hutool/log/LogFactory.java | 67 +++++++++---------- 7 files changed, 81 insertions(+), 130 deletions(-) create mode 100644 hutool-extra/src/main/resources/META-INF/services/cn.hutool.extra.template.TemplateEngine create mode 100644 hutool-extra/src/main/resources/META-INF/services/cn.hutool.extra.tokenizer.TokenizerEngine diff --git a/CHANGELOG.md b/CHANGELOG.md index fc17f04f2..f7c3e191f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * 【core 】 增加ServiceLoaderUtil * 【core 】 增加EnumUtil.getEnumAt方法 * 【core 】 增强EnumConvert判断能力(issue#I17082@Gitee) +* 【log 】 使用SPI机制代替硬编码 ### Bug修复 diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ServiceLoaderUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ServiceLoaderUtil.java index fdc57da56..166717c8e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ServiceLoaderUtil.java +++ b/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; /** @@ -18,6 +19,25 @@ import java.util.ServiceLoader; */ public class ServiceLoaderUtil { + /** + * 加载第一个可用服务,如果用户定义了多个接口实现类,只获取第一个不报错的服务。 + * + * @param 接口类型 + * @param clazz 服务接口 + * @return 第一个服务接口实现对象,无实现返回{@code null} + */ + public static T loadFirstAvailable(Class clazz) { + final Iterator iterator = load(clazz).iterator(); + if(iterator.hasNext()){ + try { + return iterator.next(); + } catch (ServiceConfigurationError e) { + // ignore + } + } + return null; + } + /** * 加载第一个服务,如果用户定义了多个接口实现类,只获取第一个。 * diff --git a/hutool-extra/src/main/java/cn/hutool/extra/template/engine/TemplateFactory.java b/hutool-extra/src/main/java/cn/hutool/extra/template/engine/TemplateFactory.java index c60259021..2659af8a0 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/template/engine/TemplateFactory.java +++ b/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,自动创建对应的模板引擎对象
* 推荐创建的引擎单例使用,此方法每次调用会返回新的引擎 - * + * * @param config 模板配置,包括编码、模板文件path等信息 * @return {@link Engine} */ @@ -37,41 +30,16 @@ public class TemplateFactory { /** * 根据用户引入的模板引擎jar,自动创建对应的模板引擎对象
* 推荐创建的引擎单例使用,此方法每次调用会返回新的引擎 - * + * * @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 !"); } } diff --git a/hutool-extra/src/main/java/cn/hutool/extra/tokenizer/engine/TokenizerFactory.java b/hutool-extra/src/main/java/cn/hutool/extra/tokenizer/engine/TokenizerFactory.java index 9e0ef1a6b..b8c800f12 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/tokenizer/engine/TokenizerFactory.java +++ b/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 !"); } } diff --git a/hutool-extra/src/main/resources/META-INF/services/cn.hutool.extra.template.TemplateEngine b/hutool-extra/src/main/resources/META-INF/services/cn.hutool.extra.template.TemplateEngine new file mode 100644 index 000000000..03499e5dd --- /dev/null +++ b/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 \ No newline at end of file diff --git a/hutool-extra/src/main/resources/META-INF/services/cn.hutool.extra.tokenizer.TokenizerEngine b/hutool-extra/src/main/resources/META-INF/services/cn.hutool.extra.tokenizer.TokenizerEngine new file mode 100644 index 000000000..505ff5d8b --- /dev/null +++ b/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 \ No newline at end of file diff --git a/hutool-log/src/main/java/cn/hutool/log/LogFactory.java b/hutool-log/src/main/java/cn/hutool/log/LogFactory.java index 1980b4bf5..80648d667 100644 --- a/hutool-log/src/main/java/cn/hutool/log/LogFactory.java +++ b/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 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 { * 检查日志实现是否存在
* 此方法仅用于检查所提供的日志相关类是否存在,当传入的日志类类不存在时抛出ClassNotFoundException
* 此方法的作用是在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 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 { * 决定日志实现 *

* 依次按照顺序检查日志库的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 { * 决定日志实现 *

* 依次按照顺序检查日志库的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 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