Es wird versucht, Abhängigkeiten zur Laufzeit mithilfe von Apache Aether dynamisch herunterzuladen und zu laden, aber SpJava

Java-Forum
Guest
 Es wird versucht, Abhängigkeiten zur Laufzeit mithilfe von Apache Aether dynamisch herunterzuladen und zu laden, aber Sp

Post by Guest »

Ich arbeite an der Optimierung einer Spring Boot-Anwendung, indem ich Abhängigkeiten zur Laufzeit herunterlade, anstatt sie in die endgültige JAR-Datei zu packen. Obwohl ich den Download-Mechanismus erfolgreich implementiert habe, erkennt Spring Boot diese dynamisch geladenen Abhängigkeiten nicht, obwohl sie über den Java ClassLoader zugänglich sind.
Ziel:
Reduzieren Sie die endgültige JAR-Größe durch:
  • Abhängigkeiten aus dem kompilierten JAR ausschließen
  • Erforderliche Abhängigkeiten herunterladen unter Laufzeit
  • Dynamisches Laden mit URLClassLoader
Mein Hintergrund
Ich habe online nach möglichen Lösungen gesucht, aber keine gefunden. Ich habe zuvor bei der Entwicklung von Minecraft-Plugins mit Libby etwas Ähnliches mit Core Java gehandhabt, bin mir aber nicht sicher, ob das mit Spring Boot machbar ist. Obwohl ich KI-Tools verwendet habe, um einige Abhängigkeitsprobleme zu lösen, stecke ich jetzt an diesem Punkt fest.
Meine aktuelle Implementierung
DependancyLoader.java

Code: Select all

package com.hapangama.sunlicense.boot;

import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.collection.CollectResult;
import org.eclipse.aether.collection.DependencyCollectionException;
import org.eclipse.aether.collection.DependencySelector;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.resolution.DependencyResult;
import org.eclipse.aether.util.filter.DependencyFilterUtils;
import org.eclipse.aether.util.graph.selector.ScopeDependencySelector;
import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
import org.springframework.stereotype.Service;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger;
import java.util.stream.Collectors;

@Service
public class DependencyLoader {
private static final Logger LOGGER = Logger.getLogger(DependencyLoader.class.getName());
private static final String DEPENDENCIES_DIR = "BOOT-INF/lib";

public static void initializeDependencies() throws Exception {
// Create libs directory if it doesn't exist
File libsDir = new File(DEPENDENCIES_DIR);
if (!libsDir.exists()) {
libsDir.mkdirs();
}

// Initialize Maven components
RepositorySystem system = Booter.newRepositorySystem();
DefaultRepositorySystemSession session = Booter.newRepositorySystemSession(system);
session.setLocalRepositoryManager(
system.newLocalRepositoryManager(
session,
new LocalRepository(libsDir.getAbsolutePath())
)
);

// Define repositories
List repositories = Arrays.asList(
new RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2/")
.setPolicy(new RepositoryPolicy(true, RepositoryPolicy.UPDATE_POLICY_DAILY, RepositoryPolicy.CHECKSUM_POLICY_WARN))
.build(),
new RemoteRepository.Builder("spring-releases", "default", "https://repo.spring.io/release")
.setPolicy(new RepositoryPolicy(true, RepositoryPolicy.UPDATE_POLICY_DAILY, RepositoryPolicy.CHECKSUM_POLICY_WARN))
.build(),
new RemoteRepository.Builder("jcenter", "default", "https://jcenter.bintray.com")
.setPolicy(new RepositoryPolicy(true, RepositoryPolicy.UPDATE_POLICY_DAILY, RepositoryPolicy.CHECKSUM_POLICY_WARN))
.build(),
new RemoteRepository.Builder("vaadin-addons", "default", "https://maven.vaadin.com/vaadin-addons")
.setPolicy(new RepositoryPolicy(true, RepositoryPolicy.UPDATE_POLICY_DAILY, RepositoryPolicy.CHECKSUM_POLICY_WARN))
.build()
);

// Define dependencies
List  dependencies = Arrays.asList(
// Spring Boot dependencies
new Dependency(new DefaultArtifact("org.springframework.boot:spring-boot-starter-data-jpa:2.5.4"), "runtime"),
new Dependency(new DefaultArtifact("org.springframework.boot:spring-boot-starter-security:2.5.4"), "runtime"),

// Vaadin and related dependencies
new Dependency(new DefaultArtifact("com.vaadin:vaadin-spring-boot-starter:24.0.0"), "runtime"),
new Dependency(new DefaultArtifact("in.virit:viritin:2.10.1"), "runtime"),
new Dependency(new DefaultArtifact("com.github.appreciated:apexcharts:24.0.1"), "runtime"),
new Dependency(new DefaultArtifact("org.parttio:starpass-theme:1.0.4"), "runtime"),
new Dependency(new DefaultArtifact("org.vaadin.crudui:crudui:7.1.2"), "runtime"),

// Database
new Dependency(new DefaultArtifact("com.h2database:h2:2.1.214"), "runtime"),

// Utility libraries
new Dependency(new DefaultArtifact("org.modelmapper:modelmapper:3.2.0"), "runtime"),

// Discord integration
new Dependency(new DefaultArtifact("net.dv8tion:JDA:5.2.1"), "runtime")
);

// Create collection request
CollectRequest collectRequest = new CollectRequest();
collectRequest.setRepositories(repositories);
dependencies.forEach(collectRequest::addDependency);

// Resolve dependencies
DependencyResult result = resolveDependencies(system, session, collectRequest);

// Get resolved artifact files
List artifactFiles = getArtifactFiles(result);

// Create and set up the custom ClassLoader
URL[] urls = artifactFiles.stream()
.map(file -> {
try {
return file.toURI().toURL();
} catch (MalformedURLException e) {
LOGGER.warning("Failed to convert file to URL: " + file);
return null;
}
})
.filter(Objects::nonNull)
.toArray(URL[]::new);

URLClassLoader classLoader = new URLClassLoader(urls, DependencyLoader.class.getClassLoader());
Thread.currentThread().setContextClassLoader(classLoader);

// Verify critical dependencies
verifyDependencies(classLoader);
}

private static DependencyResult resolveDependencies(RepositorySystem system,
RepositorySystemSession session,
CollectRequest collectRequest) throws Exception {
CollectResult collectResult = system.collectDependencies(session, collectRequest);
DependencyRequest dependencyRequest = new DependencyRequest(collectResult.getRoot(),
DependencyFilterUtils.classpathFilter("compile", "runtime"));

return system.resolveDependencies(session, dependencyRequest);
}

private static List getArtifactFiles(DependencyResult result) {
PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
result.getRoot().accept(nlg);

return nlg.getArtifacts(false).stream()
.map(Artifact::getFile)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}

private static void verifyDependencies(ClassLoader classLoader) {
try {
// Verify JDA
Class.forName("net.dv8tion.jda.api.hooks.ListenerAdapter", true, classLoader);
LOGGER.info("JDA dependency loaded successfully");

// Verify Spring Boot
Class.forName("org.springframework.boot.SpringApplication", true, classLoader);
LOGGER.info("Spring Boot dependency loaded successfully");

// Add other critical dependency verifications as needed

} catch (ClassNotFoundException e) {
LOGGER.severe("Failed to verify critical dependency: "  + e.getMessage());
throw new RuntimeException("Critical dependency verification failed", e);
}
}
}
Booter.java

Code: Select all

package com.hapangama.sunlicense.boot;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.eclipse.aether.util.listener.ChainedRepositoryListener;
import org.eclipse.aether.util.listener.ChainedTransferListener;

import java.io.File;

// Booter.java
public class Booter {
public static RepositorySystem newRepositorySystem() {
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
locator.addService(TransporterFactory.class, FileTransporterFactory.class);
locator.addService(TransporterFactory.class, HttpTransporterFactory.class);

return locator.getService(RepositorySystem.class);
}

public static DefaultRepositorySystemSession newRepositorySystemSession(RepositorySystem system) {
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();

LocalRepository localRepo = new LocalRepository("target/local-repo");
session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));

session.setTransferListener(new ChainedTransferListener());
session.setRepositoryListener(new ChainedRepositoryListener());

return session;
}
}
Hauptklasse

Code: Select all

package com.hapangama.sunlicense;

import com.hapangama.sunlicense.boot.DependencyLoader;
import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.theme.Theme;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.DefaultResourceLoader;

import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.Properties;

@Theme(value = "sun")
@SpringBootApplication(exclude = ErrorMvcAutoConfiguration.class)
public class SunLicenseApplication implements AppShellConfigurator {

public static void main(String[] args) {
try {
// Initialize dependencies before starting Spring
DependencyLoader.initializeDependencies();

// Get the context class loader that has our dependencies
ClassLoader customClassLoader = Thread.currentThread().getContextClassLoader();

// Create Spring application
SpringApplication app = new SpringApplication(SunLicenseApplication.class);

Properties properties = new Properties();
properties.put("spring.main.allow-bean-definition-overriding", "true");
properties.put("spring.main.allow-circular-references", "true");

app.setDefaultProperties(properties);

// Important: Set both resource loader and context class loader
app.setResourceLoader(new DefaultResourceLoader(customClassLoader));
Thread.currentThread().setContextClassLoader(customClassLoader);

ConfigurableApplicationContext context = app.run(args);

if (context != null && context.isActive()) {
System.out.println("Application started successfully");
System.out.println("Active profiles: "  + Arrays.toString(context.getEnvironment().getActiveProfiles()));
}

} catch (Exception e) {
System.out.println("Failed to start application");
e.printStackTrace();
System.exit(1);
}
}
}

Protokoll der Startkonsole
Java konnte JDA laden (eine der Bibliotheken erfolgreich)

Code: Select all

INFO: JDA dependency loaded successfully
aber es wirft die Klasse aus, die später aus irgendeinem Grund nicht gefunden wurde.

Code: Select all

Caused by: java.lang.ClassNotFoundException: net.dv8tion.jda.api.hooks.ListenerAdapter
https://paste.hapangama.com/egewawovif.properties
Pom.xml

Code: Select all

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0

org.springframework.boot
spring-boot-starter-parent
3.3.5
 

com.hapangama
SunLicense
0.0.1-SNAPSHOT
SunLicense
SunLicense














17
24.5.4




Vaadin Directory
https://maven.vaadin.com/vaadin-addons

false





 
org.springframework.boot
spring-boot-starter-data-jpa
provided

 
org.springframework.boot
spring-boot-starter-security
provided

  
com.vaadin
vaadin-spring-boot-starter
provided


com.h2database
h2
provided


org.projectlombok
lombok
true

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post