简体   繁体   English

Java 如何初始化已经加载 Class

[英]Java How Initialize Already Loaded Class

I am trying to develop a dynamic Factory Class, where Factory Class does not know the factories, I will put the code so that the question is clearer.我正在尝试开发一个动态工厂 Class,其中工厂 Class 不知道工厂,我将放置代码以便问题更清楚。

App.java: App.java:

package br.com.factory;

public class App {
    public static void main(String[] args) {
        Product product = ProductFactory.createProduct("Xiaomi", "MI XX");
        if (product != null) {
            System.out.println(product.getName());
        }
    }
}

Product.java:产品.java:

package br.com.factory;

public interface Product {
   public String getName();   
}

ProductFactory.java: ProductFactory.java:

package br.com.factory;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

public class ProductFactory {
   
   private static HashMap<String, Map<String, Supplier<Product>>> registries = new HashMap<>();

   private ProductFactory(){}

   static {
      ClassLoaderInitializer.initialize(ProductSupplier.class);
   }

   public static Product createProduct(String manufacturer, String model) {
      Map<String, Supplier<Product>> manufacturers = registries.getOrDefault(manufacturer, null);
      if (manufacturers != null){
         Supplier<Product> suppliers = manufacturers.getOrDefault(model, null);
         if (suppliers != null) {
            return suppliers.get();
         }
      }
      return null;
   }

   public static void registerFactory(String manufacturer, String model, Supplier<Product> supplier) {
      registries
         .computeIfAbsent(manufacturer, p -> new HashMap<>())
         .putIfAbsent(model, supplier);
   }

}

ProductSupplier.java:产品供应商.java:

package br.com.factory;

import java.util.function.Supplier;

public interface ProductSupplier extends Supplier<Product> {

}

XiaomiFactory.java: XiaomiFactory.java:

package br.com.factory.xiaomi;

import br.com.factory.Product;
import br.com.factory.ProductFactory;
import br.com.factory.ProductSupplier;

public class XiaomiFactory implements ProductSupplier {

   static {
      ProductFactory.registerFactory("Xiaomi", "MI XX", XiamoMiXX::new);
   }

   private XiaomiFactory() {
   }

   @Override
   public Product get() {
      return new XiamoMiXX();
   }

}

class XiamoMiXX implements Product {
   @Override
   public String getName() {
      return "Xiaomi Mi XX";
   }
}

ClassLoaderInitializer.java: ClassLoaderInitializer.java:

package br.com.factory;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;

public class ClassLoaderInitializer {

   private ClassLoaderInitializer() {
   }

   public static void initialize(Class<?> parentClass) {
      try {
         Enumeration<URL> resources = ClassLoaderInitializer.class.getClassLoader().getResources("");
         while (resources.hasMoreElements()) {
            URL nextElement = resources.nextElement();           

            try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { nextElement });) {               
               URL[] urLs = urlClassLoader.getURLs();

               for (URL u : urLs) {
                  try {
                     File file = new File(u.toURI());
                     initializeClass(file, file, urlClassLoader, parentClass);
                  } catch (URISyntaxException e) {
                     e.printStackTrace();
                  }
               }
            }
         }
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   private static void initializeClass(File root, File file, ClassLoader loader, Class<?> parentClass) {
      if (file.isDirectory()) {
         File[] listFiles = file.listFiles();
         for (File f : listFiles) {
            initializeClass(root, f, loader, parentClass);
         }
      } else {
         if (file.getName().toUpperCase().endsWith(".class".toUpperCase())) {
            try {
               String fileName = file.toString();
               String className = fileName.substring(root.toString().length() + 1,
                     fileName.toUpperCase().lastIndexOf(".class".toUpperCase())).replace(File.separator, ".");

               Class<?> clazz = Class.forName(className, false, loader);

               if (clazz.isAssignableFrom(parentClass)) {
                  Class.forName(className, true, loader);
               }

            } catch (ClassNotFoundException e) {
               e.printStackTrace();
            }
         }
      }
   }
}

The problem occurs in the initializeClass method,问题出现在initializeClass方法中,

more precisely in:更准确地说是:

Class <?> Clazz = Class.forName (className, false, loader);

if (clazz.isAssignableFrom (parentClass)) {
   Class.forName (className, true, loader);
}

the idea of the ClassLoaderInitializer class is to initialize the classes that inherit from a given class "Class <?> parentClass" ClassLoaderInitializer class 的想法是初始化从给定 class "Class <?> parentClass" 继承的类

but when I call the method但是当我调用该方法时

Class.forName (className, true, loader); 

for the second time, passing true in the second parameter, the class is not initialized.第二次,在第二个参数中传递true ,class 未初始化。

if I call:如果我打电话:

Class.forName (className, true, loader); 

directly, the initializeClass method will initialize all classes, what I would not like to happen.直接,initializeClass 方法将初始化所有类,这是我不希望发生的。

is there any outside of me explicitly initializing (forcing) the class specifies?在我之外有没有明确初始化(强制)class 指定的?

Use ServiceLoader使用ServiceLoader

ServiceLoader allows you to register services using metadata in the META-INF/services/ folder. ServiceLoader 允许您使用META-INF/services/文件夹中的元数据注册服务。 It allows you to use a Java API to register all your services at once.它允许您使用 Java API 一次注册所有服务。 It's better than trying to do it yourself, especially since it's standardized and doesn't require "magic" to get registered.这比尝试自己做要好,特别是因为它是标准化的并且不需要“魔法”来注册。 Magic which might be broken in Java 9 and later with the introduction of modules.随着模块的引入,Java 9 及更高版本中可能会破坏的魔法。

This is how you should use it:这是你应该如何使用它:

ProductSupplier.java

public interface ProductSupplier extends Supplier<Product> {
  // Add these methods
  String getManufacturer();
  String getModel();
}

ProductFactory.java

public class ProductFactory {
  private static final Map<List<String>, ProductSupplier> SUPPLIERS;
  static {
    var suppliers = new HashMap<List<String>, ProductSupplier>();
    for (var supplier : ServiceLoader.load(ProductSupplier.class)) { // Yes, it's as easy as this.
      var key = List.of(supplier.getManufacturer(), supplier.getModel());
      suppliers.put(key, supplier);
    }
    SUPPLIERS = Map.copyOf(suppliers);
  }

  public static Product createProduct(String manufacturer, String model) {
      var key = List.of(manufacturer, model);
      var supplier = suppliers.getOrDefault(key, () -> null);
      return supplier.get();
   }
}

XiaomiFactory.java

public class XiaomiFactory implements ProductSupplier {
  @Override public String getManufacturer() { return "Xiaomi"; }
  @Override public String getModel() { return "Mi XX"; }
  @Override public Product get() { return new XiaomiMiXX(); }
}

In META-INF/services/com.br.factory.ProductSupplier :META-INF/services/com.br.factory.ProductSupplier

com.br.factory.xiaomi.XiaomiFactory
com.br.factory.samsung.SamsungFactory # Need to create
com.br.factory.apple.AppleFactory     # Need to create

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM