使用实例工厂方法实例化

我时不时地发现自己摸索了一些旧代码,找到了“我在哪里做过类似工厂的事情”的示例。

上周再次发生这种情况时,我决定只查找所有示例,并创建一个示例项目和有关该示例的博客文章。

所以在这篇文章中,我:

  • 从简单的“原始” Java SE工厂示例开始
  • 然后使用Java SE SPI
  • Java SE上的CDI
  • Java EE上的 CDI
  • Java EE上的EJB
  • Java SE上的动态SPI
  • 最后是Java EE上的SPI

这个例子

这个示例应用程序是一个非常简单的“ Hello World”,您可以输入名称,并且有多种表达问候的方法。

Greeting Service获取Greeting Factory的实例。 然后,它可以按名称要求工厂提供Greeting (接口),然后工厂将返回正确的实现。

有3种具体的实现方式:

  • English将打招呼“ Good day name”
  • Afrikaans将打招呼“ Goeie dag name” 。 (请参阅https://www.youtube.com/watch?v=CtxB4sbV0pA )
  • Bugs Bunny会打招呼“ Eeee, 什么名字 ?” (请参阅https://www.youtube.com/watch?v=UeVtZjGII-I )

Github中提供了此博客的所有源代码 :

git clone https://github.com/phillip-kruger/factories-example

问候界面:

public interface Greeting {public String getName();public String sayHello(String to);}

香草

这个基本的Java SE应用程序有一个主要方法,可让您传递名称和想要的问候方式。

工厂是获得正确实现的基本if-statement

public Greeting getGreeting(String name){if(name.equalsIgnoreCase("BugsBunny")){return new BugsBunny();}else if(name.equalsIgnoreCase("Afrikaans")){return new Afrikaans();}else {return new English();}}

一个具体的实现示例, 英语 :

public class English implements Greeting {@Overridepublic String sayHello(String to) {return "Good day " + to + ".";}@Overridepublic String getName() {return "English";}}

运行示例:

vanilla文件夹中:

mvn clean install

这将构建项目并运行该应用程序。 日志将输出:

SEVERE: Good day Phillip.Goeie dag Phillip.Eeee, what's up Phillip ?

您也可以在Maven之外运行此命令:

java -jar target/vanilla-1.0.0-SNAPSHOT.jar World BugsBunnySEVERE: Eeee, what's up World ?

另见

  • https://alvinalexander.com/java/java-factory-pattern-example

服务提供商接口(SPI)

上面的示例意味着我可以非常轻松地添加另一个实现,并在询问时更新if statement以返回该实现。

但是,我们要改进的是if-statement 。 我们希望可以在不修改现有代码的情况下添加新的实现。 我要做的就是添加新的实现。

SPI是Java SE的一部分,是允许您构建可插入扩展的API。

将应用程序分解为模块。

我们要做的第一件事是将应用程序分解为模块 :

  • API –这将包含Greeting接口(我们的合同)
  • 引擎–其中将包含服务和工厂(以及默认的英语实现)
  • 其他实现–所有其他实现都变成了自己的模块(因此,一种用于南非荷兰语,另一种用于Bugs Bunny等)

这已经意味着我可以通过创建一个新模块来添加新的实现,而不必触摸代码,只需更新依赖项即可:

<dependencies><dependency><groupId>${project.groupId}</groupId><artifactId>spi-api</artifactId><version>${project.version}</version></dependency><dependency><groupId>${project.groupId}</groupId><artifactId>spi-impl-afrikaans</artifactId><version>${project.version}</version></dependency><dependency><groupId>${project.groupId}</groupId><artifactId>spi-impl-bugsbunny</artifactId><version>${project.version}</version></dependency></dependencies>

映射文件

具体的实现需要通过在/src/main/resources/META-INF/services/添加一个名为com.github.phillipkruger.factory.api.Greeting的文件来将其Greeting类注册为实现(接口的完全限定名称) )

文件的内容是实现的名称,例如Bugs Bunny :

com.github.phillipkruger.factory.impl.BugsBunny

该工厂

工厂现在需要获取所有Greetings实例:

ServiceLoader<Greeting> loader = ServiceLoader.load(Greeting.class);Iterator<Greeting> greetingIterator = loader.iterator();while (greetingIterator.hasNext()) {Greeting greeting = greetingIterator.next();loadedGreetings.put(greeting.getName(), greeting);}

现在我们摆脱了工厂中的if-statement

运行示例:

spi文件夹中:

mvn clean install

这将构建项目并运行该应用程序。 日志将输出:

SEVERE: Good day Phillip.Goeie dag Phillip.Eeee, what's up Phillip ?

您也可以在Maven之外运行此命令:

java -jar spi-engine/target/spi-engine-1.0.0-SNAPSHOT-jar-with-dependencies.jar Johny AfrikaansSEVERE: Goeie dag Johny.

另见

  • https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html

上下文和依赖注入(CDI)

最新版本的CDI允许您在Java SE中使用CDI。 为了创建工厂,我们将创建自己的注释作为称为GreetingProvider的API的一部分:

@Qualifier@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface GreetingProvider {String value();}

value是实现的名称。

以上的文字实现 :

@AllArgsConstructorpublic class GreetingProviderLiteral extends AnnotationLiteral<GreetingProvider> implements GreetingProvider {private final String name;@Overridepublic String value() {return this.name;}}

这将允许我们使用@GreetingProvider注释任何具体的实现(现在是RequestScoped CDI Bean),例如英语:

@GreetingProvider("English")@RequestScopedpublic class English implements Greeting {@Overridepublic String sayHello(String to) {return "Good day " + to + ".";}@Overridepublic String getName() {return "English";}}

工厂更改以查找所有@GreetingProvider类:

public class GreetingFactory {@Inject @Anyprivate Instance<Greeting> greetings;public Greeting getGreeting(String name) {Instance<Greeting> instance = greetings.select(new GreetingProviderLiteral(name));if(!instance.isUnsatisfied()){Greeting provider = instance.get();return provider;}else{return new English();}}}

因此,现在我们不再需要SPI映射文件,工厂中没有if-statement ,但是我们仍然必须更新依赖项以包含所需的所有实现。

运行示例:

cdi文件夹中:

mvn clean install

这将构建项目并运行该应用程序。 日志将输出:

SEVERE: Good day Phillip.Goeie dag Phillip.Eeee, what's up Phillip ?

您也可以在Maven之外运行此命令:

java -jar cdi-engine/target/cdi-engine-1.0.0-SNAPSHOT-jar-with-dependencies.jar Charmaine BugsBunnySEVERE: Eeee, what's up Charmaine ?

另见

  • http://www.mastertheboss.com/jboss-frameworks/cdi/building-a-cdi-2-standalone-java-application
  • http://www.adam-bien.com/roller/abien/entry/injecting_classes_in_java_se

Java EE上的CDI

我们还可以使用CDI在Application Server上创建解决方案。 现在,我们将入口点设为REST服务(而不是主要方法),因此我们需要创建并添加ApplicationConfig来启用JAX-RS:

@ApplicationPath("/api")public class ApplicationConfig extends Application {}

GreetingService现在成为REST资源,允许您执行GET ,将名称作为PathParam传递,以及可选的方式作为QueryParam进行问候:

@Path("/")@Produces(MediaType.APPLICATION_JSON)public class GreetingService {@Injectprivate GreetingFactory factory;@GET@Path("{to}")public String sayHello(@PathParam("to") String to, @QueryParam("way") List<String> way){//....}}

工厂和带注释的RequestScoped CDI Bean实现与Java SE示例上的CDI完全相同。

运行示例:

该示例可以在3个不同的应用程序服务器上运行(为诚实起见)

  • 野蜂群
  • 开放自由
  • Payara Micro

(您不必下载,安装或配置任何东西,Maven构建会做到这一点)

javaee-cdi文件夹中:

mvn clean install -P wildfly

要么

mvn clean install -P liberty

要么

mvn clean install -P payara

在所有3种情况下,maven都会:

  • 在部署了应用程序的情况下启动应用程序服务器
  • 击中2个REST网址:
    • http:// localhost:8080 / javaee-cdi-engine / api (此列表所有实现)
  • 关闭应用程序服务器(Payara除外)

因此,在日志中,您将看到(类似):

===============================================
["BugsBunny","Afrikaans","English"]
==============================================================================================
["Eeee, what's up Phillip ?","Goeie dag Phillip.","Good day Phillip."]
===============================================

如果运行Payara,则服务器不会关闭,因此您也可以手动测试工厂:

wget -qO- http://localhost:8080/javaee-cdi-engine/api/Donald?way=BugsBunny["Eeee, what's up Donald ?"]

Java EE上的EJB

只是为了完成示例,这是在Java EE上使用EJB的方法(因此没有CDI,因此也没有自定义注释)

我们仅使用JNDI查找命名的EJB。

GreetingService与Java EE CDI示例相同,因此我们仍然有一个REST入口点。 现在,具体的实现变为EJB,例如英语:

@Stateless@EJB(beanInterface = Greeting.class, beanName = "English", name = "English")public class English implements Greeting {@Overridepublic String sayHello(String to) {return "Good day " + to + ".";}@Overridepublic String getName() {return "English";}}

现在, 工厂基于Bean名称进行JNDI查找:

@Log@Statelesspublic class GreetingFactory {@EJB(lookup = "java:module/English")private Greeting english; // defaultpublic Greeting getGreeting(String name) {Greeting g = lookup("java:module/" + name);if(g==null)return english;return g;}public List<Greeting> getAll(){List<Greeting> greetings = new ArrayList<>();try {InitialContext context = new InitialContext();NamingEnumeration<Binding> list = (NamingEnumeration<Binding>)context.listBindings("java:global/javaee-ejb-engine");    while (list.hasMore()) {Binding next = list.next();if(next.getName().endsWith(Greeting.class.getName())){Greeting g = lookup("java:global/javaee-ejb-engine/" + next.getName());if(g!=null && !greetings.contains(g))greetings.add(g);}}} catch (NamingException e) {throw new RuntimeException(e);} return greetings;}private Greeting lookup(String jndi){try {InitialContext context = new InitialContext();Object o = context.lookup(jndi);return (Greeting)o;} catch (NamingException e) {log.log(Level.SEVERE, "Could not lookup [{0}]", jndi);return null;}   }}

运行示例:

与Java EE CDI示例类似,它在Wildfly Swarm , Open Liberty和Payara Micro上运行

javaee-ejb文件夹中:

mvn clean install -P wildfly

(或-P自由或-P payara)

在所有3种情况下,maven都会:

  • 在部署了应用程序的情况下启动应用程序服务器
  • 击中2个REST网址:
    • http:// localhost:8080 / javaee-ejb-engine / api (此列表列出了所有实现)
  • 关闭应用程序服务器(Payara除外)

因此,在日志中,您将看到(类似):

===============================================
["BugsBunny","Afrikaans","English"]
==============================================================================================
["Eeee, what's up Phillip ?","Goeie dag Phillip.","Good day Phillip."]
===============================================

如果运行Payara,则服务器不会关闭,因此您也可以手动测试工厂:

wget -qO- http://localhost:8080/javaee-ejb-engine/api/Barney?way=Afrikaans["Goeie dag Barney."]

动态SPI

到目前为止,添加新实现时,我们唯一要做的就是创建包含Greeting实现的模块,并更新pom.xml以包含新的依赖项。

接下来,让我们看看如何动态加载新的实现(因此无需更新依赖项)。

实现完全类似于Java SE SPI示例,包括映射文件,但是现在我们可以删除模块作为pom.xml依赖项:

<dependencies><dependency><groupId>${project.groupId}</groupId><artifactId>dynamic-spi-api</artifactId><version>${project.version}</version></dependency><!-- This will be loaded dynamically<dependency><groupId>${project.groupId}</groupId><artifactId>dynamic-spi-impl-afrikaans</artifactId><version>${project.version}</version></dependency><dependency><groupId>${project.groupId}</groupId><artifactId>dynamic-spi-impl-bugsbunny</artifactId><version>${project.version}</version></dependency>--></dependencies>

工厂看起来像这样:

public class GreetingFactory {private final Map<String,Greeting> loadedGreetings = new HashMap<>();public GreetingFactory(){URLClassLoader classloader = getURLClassLoader();ServiceLoader<Greeting> loader = ServiceLoader.load(Greeting.class, classloader);Iterator<Greeting> greetingIterator = loader.iterator();while (greetingIterator.hasNext()) {Greeting greeting = greetingIterator.next();loadedGreetings.put(greeting.getName(), greeting);}}public Greeting getGreeting(String name){if(loadedGreetings.containsKey(name)){return loadedGreetings.get(name);}else {return new English();}}private URLClassLoader getURLClassLoader(){File[] pluginFiles = getPluginFiles();ArrayList<URL> urls = new ArrayList<>();for(File plugin:pluginFiles){try{URL pluginURL = plugin.toURI().toURL();urls.add(pluginURL);}catch(MalformedURLException m){log.log(Level.SEVERE, "Could not load [{0}], ignoring", plugin.getName());}}return new URLClassLoader(urls.toArray(new URL[]{}),GreetingFactory.class.getClassLoader());}private File[] getPluginFiles(){File loc = new File("plugins");File[] pluginFiles = loc.listFiles((File file) -> file.getPath().toLowerCase().endsWith(".jar"));return pluginFiles;}}

基本上,工厂仍将使用SPI的ServiceLoader加载可用的问候语,但我们传入自定义的ClassLoader。我们在plugins文件夹中查找任何jar文件,并使用URLClassloader加载。

这意味着我现在可以只创建一个新的实现模块并将文件放在plugins文件夹中。 美观且可插拔。

运行示例:

dynamic-spi文件夹中:

mvn clean install

这将构建项目并运行该应用程序。 日志将输出:

SEVERE: Good day Phillip.Goeie dag Phillip.Eeee, what's up Phillip ?

您也可以在Maven之外运行此命令:

java -jar dynamic-spi-engine/target/dynamic-spi-engine-1.0.0-SNAPSHOT-jar-with-dependencies.jar Madonna BugsBunnySEVERE: Eeee, what's up Madonna ?

Java EE上的动态SPI

现在,这是否是一个好主意,将进行另外的讨论,只是为了表明它是可能的,我们现在将使用动态SPI在应用程序服务器上加载实现。 这意味着我可以向正在运行的服务器添加新的实现。 因此,我不仅可以在不接触代码或依赖项的情况下添加新的实现,而且还可以启用该新实现而无需重新启动应用程序。

这些实现完全类似于Java SE SPI示例, pom.xml不包含任何实现模块,并且我们现在有了一个新类,可以将模块加载到plugins文件夹中:

这是一个ApplicationScoped CDI Bean,可在启动时加载模块。 也可以使用REST重新加载模块:

@Path("/pluginloader")@ApplicationScoped@Logpublic class PluginLoader {@Produces @Named("Greetings")private final Map<String,Greeting> loadedGreetings = new HashMap<>();public void init(@Observes @Initialized(ApplicationScoped.class) ServletContext context) {loadPlugins();}@GET@Path("/reload")public Response loadPlugins(){ClassLoader classloader = getClassLoader();ServiceLoader<Greeting> loader = ServiceLoader.load(Greeting.class, classloader);Iterator<Greeting> greetingIterator = loader.iterator();while (greetingIterator.hasNext()) {Greeting greeting = greetingIterator.next();log.log(Level.SEVERE, "Adding provider [{0}]", greeting.getName());if(!loadedGreetings.containsKey(greeting.getName())){loadedGreetings.put(greeting.getName(), greeting);}}return Response.ok("ok").build();}private ClassLoader getClassLoader(){File[] pluginFiles = getPluginFiles();if(pluginFiles!=null){        ArrayList<URL> urls = new ArrayList<>();for(File plugin:pluginFiles){try{URL pluginURL = plugin.toURI().toURL();urls.add(pluginURL);}catch(MalformedURLException m){log.log(Level.SEVERE, "Could not load [{0}], ignoring", plugin.getName());}}return new URLClassLoader(urls.toArray(new URL[]{}),this.getClass().getClassLoader());}return this.getClass().getClassLoader();}private File[] getPluginFiles(){File loc = getPluginDirectory();if(loc==null)return null;File[] pluginFiles = loc.listFiles((File file) -> file.getPath().toLowerCase().endsWith(".jar"));return pluginFiles;}private File getPluginDirectory(){File plugins = new File("plugins");if(plugins.exists())return plugins;  return null;}}

所有已加载的GreetingsMap<String,Greeting>中都可用,可以将其注入工厂 :

@RequestScoped@Logpublic class GreetingFactory {@Inject @Named("Greetings")private Map<String,Greeting> greetings;// ...}

运行示例:

与Java EE CDI和EJB示例类似,它在Wildfly Swarm , Open Liberty和Payara Micro上运行

javaee-spi文件夹中:

mvn clean install -P wildfly

(或-P自由或-P payara)

在所有3种情况下,maven都会:

  • 在部署了应用程序的情况下启动应用程序服务器
  • 击中2个REST网址:
    • http:// localhost:8080 / javaee-spi-engine / api (此列表列出了所有实现)
  • 关闭应用程序服务器(Payara除外)

因此,在日志中,您将看到(类似):

===============================================
["BugsBunny","Afrikaans","English"]
==============================================================================================
["Eeee, what's up Phillip ?","Goeie dag Phillip.","Good day Phillip."]
===============================================

如果运行Payara,则服务器不会关闭,因此您也可以手动测试工厂:

wget -qO- http://localhost:8080/javaee-spi-engine/api/Frans?way=Afrikaans["Goeie dag Frans."]

当前,在plugins文件夹中,您将看到2个已知的实现(南非荷兰语和Bugs Bunny):

ls javaee-spi-engine/plugins/javaee-spi-impl-afrikaans-1.0.0-SNAPSHOT.jar  javaee-spi-impl-bugsbunny-1.0.0-SNAPSHOT.jar

当我们构建这些实现时,将其复制到那里。

现在,让服务器保持运行状态,并添加一种新的问候方式,称为AliG。 (请参阅https://www.youtube.com/watch?v=b00lc92lExw )

cd javaee-spi-impl-aligmvn clean install -P plugin

这会将Ali G实现复制到plugins文件夹。

现在,让我们再次问候Frans:

wget -qO- http://localhost:8080/javaee-spi-engine/api/Frans?way=AliG["Booyakasha Frans !"]

因此,我们可以向正在运行的服务器添加新的具体实现。

结束

就是这样(现在)。 欢迎任何评论和您自己的示例!

翻译自: https://www.javacodegeeks.com/2017/12/some-factory-examples.html

使用实例工厂方法实例化

使用实例工厂方法实例化_一些工厂实例相关推荐

  1. 抽象工厂和工厂方法示例_抽象工厂设计模式示例

    抽象工厂和工厂方法示例 本文是我们名为" Java设计模式 "的学院课程的一部分. 在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们. 您将了解模式如此 ...

  2. 抽象工厂和工厂方法示例_工厂方法设计模式示例

    抽象工厂和工厂方法示例 本文是我们名为" Java设计模式 "的学院课程的一部分. 在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们. 您将了解模式如此 ...

  3. 实验5:配置通过静态工厂方法创建的bean、实例工厂方法创建的bean、(FactoryBean测试)★

    实验5:配置通过静态工厂方法创建的bean.实例工厂方法创建的bean.(FactoryBean测试)★ bean的创建默认就是框架利用反射new出来的bean实例 工厂模式:工厂帮我们创建对象:有一 ...

  4. 工厂方法模式_工厂方法模式

    工厂方法模式是简单工厂模式的升级版,简单工厂模式不符合设计模式的原则(即:单一职责,开闭原则) 优点: 职责明确,扩展方便 缺点:需要创建多个工厂 实现步骤: 1.将工厂通用方法抽取接口 (例如:IF ...

  5. 简单工厂和工厂方法模式(附简单实例)

    工厂模式: 工厂模式是常用的一类创建型设计模式,通常工厂模式是指工厂方法模式.其是使用频率最高的工厂模式. 上图几种工厂模式由简到难: 简单工厂模式定义:定义一个工厂类,其可以根据参数的不同返回不同类 ...

  6. 设计模式之工厂类模式总结对比、简单工厂模式、工厂方法模式、抽象工厂模式、带反射的工厂模式、例子代码分析、最详细

    1. 题目 假设某公司同时用SqlServer.MySql数据库,即会切换两数据库(不同数据库的sql语句有些许差异),同时,两数据库里均有对Users.Departments表的操作(sql代码不一 ...

  7. 021 设计模式之工厂方法模式,抽象工厂模式的区别

    简单工厂模式,工厂方法模式和抽象工厂模式的异同 简单工厂模式,工厂方法模式和抽象工厂模式都是属于创建型设计模式,这三种创建型模式都不需要知道具体类.我们掌握一种思想,就是在创建一个对象时,需要把容易发 ...

  8. 设计模式---3(工厂方法模式的概念,工厂方法模式的实现,工厂方法模式和简单工厂模式比较)

    工厂方法模式 概念 工厂方法模式同样属于类的创建型模式又被称为多态工厂模式 . 工厂方法模式的意义 定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中. 核心工厂类不再负责产品的创建,这样核 ...

  9. 简单工厂模式、工厂方法模式、抽象工厂模式

    2019-11-12 参考大话数据结构 假设我们要解决一个问题:编程实现一个计算器 用面向过程的思想,很容易想到,首先输入两个操作数,再判断运算符进行运算,输出结果即可,但是这样的代码很难维护,我们需 ...

最新文章

  1. pfSense 2.4.3 发布,包含重要的安全修复补丁
  2. timestamp的两个属性:CURRENT_TIMESTAMP 和ON UPDATE CURRENT_TIMESTAMP
  3. OpenCV中图像显示窗口函数namedWindow()详解
  4. python dump函数_python 处理 json 四个函数dumps、loads、dump、load的区别
  5. 创建一个dynamics 365 CRM online plugin (四) - PreValidation
  6. android一些小技巧
  7. [7.14更新日志]CRP编译缓存和海外构建让编译健步如飞!
  8. 用实例的方式去理解storm的并发度
  9. JVM各个组成部分和其基本功能
  10. MySQL日志设置双1_mysql日志配置文件示例
  11. ES6中Number中的扩展
  12. 从数据黑盒到数据白盒,阿里云基础产品首席架构师黄瑞瑞分享背后的故事
  13. 初学云计算:华为vs阿里vs红帽,如何选择?
  14. 二极管三极管电路原理和理解
  15. 花生壳域名SSH登录不上的问题
  16. 疾病抗争从未停止,糖尿病数据可视化分析
  17. 如何查看数据库中表的前5行,3-8行,随机3行记录?
  18. JavaBean之Builder模式
  19. Java鸡兔同笼问题
  20. python列表的基本操作-python列表的用法

热门文章

  1. P3170-[CQOI2015]标识设计【插头dp】
  2. AT3957-[AGC023F]01 on Tree【贪心,堆】
  3. P7045-[MCOI-03]金牌【构造,交互题】
  4. jzoj4252-QYQ的图【dfs】
  5. jzoj100042-保留道路【最小生成树,图论】
  6. 2018/7/10-纪中某C组题【jzoj3792,jzoj3793,jzoj3794】
  7. 各种有用的东西留言板
  8. 初一模拟赛总结(2019.6.1)
  9. 清明梦超能力者黄YY[树链剖分+扫描线,线段树合并]
  10. JavaFX官方教程(六)之带有JavaFX CSS的花式表单