Чтобы загрузить класс динамически из JAR-файла, который реализует определенный интерфейс, но вы не знаете заранее, какой класс, который будет быть и сама баночка файл не определяет класса по умолчанию «плагин», вы можете перебирать скаченную банку и получить список классов, содержащихся в банке, как это:
/**
* Scans a JAR file for .class-files and returns a {@link List} containing
* the full name of found classes (in the following form:
* packageName.className)
*
* @param file
* JAR-file which should be searched for .class-files
* @return Returns all found class-files with their full-name as a List of
* Strings
* @throws IOException If during processing of the Jar-file an error occurred
* @throws IllegalArgumentException If either the provided file is null, does
* not exist or is no Jar file
*/
public List<String> scanJarFileForClasses(File file) throws IOException, IllegalArgumentException
{
if (file == null || !file.exists())
throw new IllegalArgumentException("Invalid jar-file to scan provided");
if (file.getName().endsWith(".jar"))
{
List<String> foundClasses = new ArrayList<String>();
try (JarFile jarFile = new JarFile(file))
{
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements())
{
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class"))
{
String name = entry.getName();
name = name.substring(0,name.lastIndexOf(".class"));
if (name.indexOf("/")!= -1)
name = name.replaceAll("/", ".");
if (name.indexOf("\\")!= -1)
name = name.replaceAll("\\", ".");
foundClasses.add(name);
}
}
}
return foundClasses;
}
throw new IllegalArgumentException("No jar-file provided");
}
когда известны классы, которые включены в файле банка, вам необходимо загрузить каждый класс и проверить, если они реализуют нужный интерфейс, как это:
/**
* <p>
* Looks inside a jar file and looks for implementing classes of the provided interface.
* </p>
*
* @param file
* The Jar-File containing the classes to scan for implementation of the given interface
* @param iface
* The interface classes have to implement
* @param loader
* The class loader the implementing classes got loaded with
* @return A {@link List} of implementing classes for the provided interface
* inside jar files of the <em>ClassFinder</em>s class path
*
* @throws Exception If during processing of the Jar-file an error occurred
*/
public List<Class<?>> findImplementingClassesInJarFile(File file, Class<?> iface, ClassLoader loader) throws Exception
{
List<Class<?>> implementingClasses = new ArrayList<Class<?>>();
// scan the jar file for all included classes
for (String classFile : scanJarFileForClasses(file))
{
Class<?> clazz;
try
{
// now try to load the class
if (loader == null)
clazz = Class.forName(classFile);
else
clazz = Class.forName(classFile, true, loader);
// and check if the class implements the provided interface
if (iface.isAssignableFrom(clazz) && !clazz.equals(iface))
implementingClasses.add(clazz);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
return implementingClasses;
}
, как вы можете собрать все реализации определенного интерфейса вы может просто инициализировать новый экземпляр через
public void executeImplementationsOfAInJarFile(File downloadedJarFile)
{
If (downloadedJarFile == null || !downloadedJarFile.exists())
throw new IllegalArgumentException("Invalid jar file provided");
URL downloadURL = downloadedJarFile.toURI().toURL();
URL[] downloadURLs = new URL[] { downloadURL };
URLClassLoader loader = URLClassLoader.newInstance(downloadURLs, getClass().getClassLoader());
try
{
List<Class<?>> implementingClasses = findImplementingClassesInJarFile(downloadedJarFile, A.class, loader);
for (Class<?> clazz : implementingClasses)
{
// assume there is a public default constructor available
A instance = clazz.newInstance();
// ... do whatever you like here
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
Обратите внимание, что в этом примере предполагается, что A является интерфейсом. Если в Jar-файле не может быть реализован класс реализации, файл jar будет загружен загрузчиком классов, но реализация объекта не произойдет.
Обратите внимание, что всегда рекомендуется обеспечить родительский загрузчик классов - особенно с помощью URLClassLoader. Кроме того, может случиться, что некоторые классы, которые не содержатся в Jar-файле, могут отсутствовать, и поэтому вы получите ClassNotFoundException
при попытке получить к ним доступ. Это связано с механизмом делегирования, используемым загрузчиками классов, которые сначала спрашивают своего родителя, знают ли они определение класса для требуемого класса. Если это так, класс будет загружен родителем; если нет, класс будет загружен созданным URLClassLoader.
Помните, что возможна загрузка одного и того же класса несколькими разными классами ClassLoaders (одноранговые загрузчики классов).Но хотя имя и байты класса могут быть одинаковыми, классы несовместимы с использованием разных экземпляров класса loader, поэтому попытка передать экземпляр, загруженный classloder A, в тип, загруженный загрузчиком классов B, завершится с ошибкой.
@Edit: изменен код, чтобы исключить возвращаемые значения null, вместо этого выбрасываются более или менее соответствующие исключения. @ Edit2: поскольку я не могу принять предложения по рассмотрению кода, я отредактировал обзор непосредственно в сообщении
Но клиент не знает имя конкретного имени класса, который он хочет загрузить, поэтому, где я могу получить "NameOfClassThatContainsTheCode" из. Клиентское приложение просто знает, что хочет загрузить загруженный класс, и этот класс реализует интерфейс A – siebenschlaefer