Java如何初始化已加载Class

Java How Initialize Already Loaded Class

我正在尝试开发一个动态工厂Class,其中工厂Class不知道工厂,我会把代码放在一起,这样问题就更清楚了。

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:

package br.com.factory;

public interface Product {
   public String getName();   
}

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:

package br.com.factory;

import java.util.function.Supplier;

public interface ProductSupplier extends Supplier<Product> {

}

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:

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();
            }
         }
      }
   }
}

问题出现在initializeClass方法中,

更准确地说:

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

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

ClassLoaderInitializer class 的思想是初始化继承自给定 class 的 classes "Class parentClass"

但是当我调用方法时

Class.forName (className, true, loader); 

第二次,第二个参数传true,class没有初始化

如果我打电话:

Class.forName (className, true, loader); 

直接地,initializeClass 方法将初始化所有 classes,这是我不希望发生的事情。

我之外是否有任何明确初始化(强制)class 指定?

使用ServiceLoader

ServiceLoader 允许您使用 META-INF/services/ 文件夹中的元数据注册服务。它允许您使用 Java API 一次注册所有服务。这比尝试自己做要好,特别是因为它是标准化的并且不需要“魔法”来注册。在 Java 9 及更高版本中可能会随着模块的引入而被打破。

您应该这样使用它:

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(); }
}

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