使用实例工厂方法实例化_一些工厂实例
使用實例工廠方法實例化
我時不時地發現自己摸索了一些舊代碼,找到了“我在哪里做過類似工廠的事情”的示例。
上周再次發生這種情況時,我決定只查找所有示例,并創建一個示例項目和有關該示例的博客文章。
所以在這篇文章中,我:
- 從簡單的“原始” 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;}}所有已加載的Greetings在Map<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
使用實例工廠方法實例化
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的使用实例工厂方法实例化_一些工厂实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 华为推出Mate 60 RS非凡大师手机
- 下一篇: 命名对象实体对象_我的对象命名