@ComponentScan не обнаруживает bean-компоненты в образе времени выполнения Java, связанном с jlink

Я использую JavaFX на Java 11 для создания настольного приложения. Приложение упаковано в пользовательский образ времени выполнения со всеми его модулями и их зависимостями, используя jlink, Для внедрения зависимости приложение использует Spring Framework.

Я знаю, что модули Spring в настоящее время не включают module-info.classвместо этого они поставляются как "автоматические модули". Поскольку автоматические модули не могут быть связаны с jlinkЯ добавил вручную module-info.class к каждому JAR-файлу зависимостей Spring, используя плагин Moditect Maven. Вот соответствующие части pom.xml:

    ...
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${dependency.spring.version}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.moditect</groupId>
                <artifactId>moditect-maven-plugin</artifactId>
                <version>1.0.0.Beta2</version>
                <executions>
                    <execution>
                        <id>add-module-infos</id>
                        <phase>package</phase>
                        <goals>
                            <goal>add-module-info</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/dependencies</outputDirectory>
                            <modules>
                                <module>
                                    <artifact>
                                        <groupId>org.springframework</groupId>
                                        <artifactId>spring-context</artifactId>
                                    </artifact>
                                    <moduleInfoSource>
                                        module spring.context {
                                        opens org.springframework.context.support to spring.core;
                                        requires java.naming;
                                        exports org.springframework.validation.annotation;
                                        exports org.springframework.ui.context.support;
                                        exports org.springframework.validation;
                                        exports org.springframework.jndi;
                                        exports org.springframework.context.annotation;
                                        exports org.springframework.context.event;
                                        exports org.springframework.context.support;
                                        exports org.springframework.context;
                                        exports org.springframework.format.support;
                                        exports org.springframework.format;
                                        exports org.springframework.jmx.export.naming;
                                        exports org.springframework.stereotype;
                                        exports org.springframework.ui.context;
                                        opens org.springframework.context.annotation to spring.core, spring.beans, com.my.app;
                                        opens org.springframework.instrument.classloading to spring.data.jpa;
                                        requires java.desktop;
                                        requires spring.aop;
                                        requires spring.beans;
                                        requires spring.core;
                                        requires spring.expression;
                                        requires spring.jcl;
                                        //requires java.annotation;
                                        }
                                    </moduleInfoSource>
                                </module>
                                <module>
                                    <artifact>
                                        <groupId>org.springframework</groupId>
                                        <artifactId>spring-aop</artifactId>
                                    </artifact>
                                    <moduleInfoSource>
                                        module spring.aop {
                                        exports org.springframework.aop.config;
                                        exports org.springframework.aop.framework;
                                        exports org.springframework.aop.framework.autoproxy;
                                        exports org.springframework.aop.support;
                                        exports org.springframework.aop.aspectj.annotation;
                                        opens org.aopalliance.intercept to spring.tx;
                                        requires spring.beans;
                                        requires spring.core;
                                        requires spring.jcl;
                                        //requires aspectjrt;
                                        exports org.springframework.aop.scope;
                                        }
                                    </moduleInfoSource>
                                </module>
                                <module>
                                    <artifact>
                                        <groupId>org.springframework</groupId>
                                        <artifactId>spring-beans</artifactId>
                                    </artifact>
                                    <moduleInfoSource>
                                        module spring.beans {
                                        exports org.springframework.beans.factory.wiring;
                                        exports org.springframework.beans;
                                        exports org.springframework.beans.factory;
                                        exports org.springframework.beans.factory.annotation;
                                        exports org.springframework.beans.factory.config;
                                        exports org.springframework.beans.factory.support;
                                        exports org.springframework.beans.factory.parsing;
                                        exports org.springframework.beans.factory.xml;
                                        exports org.springframework.beans.propertyeditors;
                                        exports org.springframework.beans.support;
                                        requires java.management.rmi;
                                        requires java.desktop;
                                        requires spring.core;
                                        requires spring.jcl;
                                        }
                                    </moduleInfoSource>
                                </module>
                                <module>
                                    <artifact>
                                        <groupId>org.springframework</groupId>
                                        <artifactId>spring-core</artifactId>
                                    </artifact>
                                    <moduleInfoSource>
                                        module spring.core {
                                        exports org.springframework.cglib.reflect;
                                        exports org.springframework.util.comparator;
                                        exports org.springframework.asm;
                                        exports org.springframework.cglib.core;
                                        exports org.springframework.cglib.proxy;
                                        exports org.springframework.cglib.transform;
                                        exports org.springframework.core;
                                        exports org.springframework.core.annotation;
                                        exports org.springframework.core.convert;
                                        exports org.springframework.core.convert.converter;
                                        exports org.springframework.core.convert.support;
                                        exports org.springframework.core.env;
                                        exports org.springframework.core.io;
                                        exports org.springframework.core.io.support;
                                        exports org.springframework.core.type;
                                        exports org.springframework.core.type.filter;
                                        exports org.springframework.core.type.classreading;
                                        exports org.springframework.objenesis;
                                        exports org.springframework.util;
                                        exports org.springframework.util.xml;
                                        //requires aspectjrt;
                                        //requires javassist;
                                        requires jdk.jconsole;
                                        requires java.desktop;
                                        requires java.management;
                                        requires java.xml;
                                        requires spring.jcl;
                                        }
                                    </moduleInfoSource>
                                </module>
                                <module>
                                    <artifact>
                                        <groupId>org.springframework</groupId>
                                        <artifactId>spring-expression</artifactId>
                                    </artifact>
                                    <moduleInfoSource>
                                        module spring.expression {
                                        exports org.springframework.expression;
                                        exports org.springframework.expression.spel;
                                        exports org.springframework.expression.spel.standard;
                                        exports org.springframework.expression.spel.support;
                                        requires spring.core;
                                        }
                                    </moduleInfoSource>
                                </module>
                                <module>
                                    <artifact>
                                        <groupId>org.springframework</groupId>
                                        <artifactId>spring-jcl</artifactId>
                                    </artifact>
                                    <moduleInfoSource>
                                        module spring.jcl {
                                        requires java.logging;
                                        exports org.apache.commons.logging;
                                        }
                                    </moduleInfoSource>
                                </module>
                            </modules>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    ...

С вышеупомянутыми определениями модуля, jlink успешно без проблем, и мое приложение объединено в пользовательский образ времени выполнения, который содержит все его зависимости.

Однако, если я запускаю приложение, используя JRE, возникают проблемы:

java -m com.my.app/com.my.app.Main

вызывает IllegalAccessException:

Caused by: java.lang.IllegalAccessException: module spring.core does not read module com.my.app
    at java.base/java.lang.invoke.MethodHandles.privateLookupIn(MethodHandles.java:197)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at spring.core@5.1.8.RELEASE/org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:500)
    ... 25 more

Согласно этой теме, ошибка может быть исправлена ​​с помощью --add-reads:

java --add-reads spring.core=com.my.app -m com.my.app/com.my.app.Main

Вместо этого, другой вид IllegalAccessException брошен:

Caused by: java.lang.IllegalAccessError: class com.my.app.AppConfig$$EnhancerBySpringCGLIB$$e9ee1f7f (in module com.my.app) cannot access class org.springframework.cglib.core.ReflectUtils (in module spring.core) because module com.my.app does not read module spring.core
    at com.my.app/com.my.app.AppConfig$$EnhancerBySpringCGLIB$$e9ee1f7f.CGLIB$STATICHOOK1(<generated>)
    at com.my.app/com.my.app.AppConfig$$EnhancerBySpringCGLIB$$e9ee1f7f.<clinit>(<generated>)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:398)
    at spring.core@5.1.8.RELEASE/org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:563)
    at spring.core@5.1.8.RELEASE/org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363)
    at spring.core@5.1.8.RELEASE/org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:582)
    at spring.core@5.1.8.RELEASE/org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:110)
    at spring.core@5.1.8.RELEASE/org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:108)
    at spring.core@5.1.8.RELEASE/org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at spring.core@5.1.8.RELEASE/org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
    ... 18 more

Как следует из сообщения об ошибке, я добавил еще один --add-reads параметр:

java --add-reads spring.core=com.my.app --add-reads com.my.app=spring.core -m com.my.app/com.my.app.Main

Теперь приложение запускается и читает его AppConfig.class, который аннотируется @ComponentScan:

@Configuration
@ComponentScan
class AppConfig {
}

Однако Spring не обнаруживает ни одного класса проекта, который аннотирован стереотипом (@Component, @Service), а на выходе Main.java указывает:

public class Main extends Application {

    private ConfigurableApplicationContext applicationContext;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void init() {
        applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    }

    @Override
    public void start(Stage mainWindow) throws IOException {
        System.out.println("Printing beans: " + applicationContext.getBeanDefinitionNames().length);
        for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
    }

    @Override
    public void stop() {
        applicationContext.stop();
    }
}
Printing beans: 5
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
appConfig

Почему @ComponentScan пропустить все мои аннотированные классы? AppConfig.java находится в корневом пакете моего приложения, и все классы находятся в подпакетах, поэтому Spring должен иметь возможность обнаруживать их в зависимости от заданной конфигурации.

Для полноты вот module-info.java моего приложения:

module com.my.app {
    requires java.logging;
    requires transitive javafx.graphics;
    requires transitive javafx.controls;
    requires transitive javafx.fxml;
    requires spring.context;
    requires spring.beans;
    requires java.sql;

    exports com.my.app;
    exports com.my.app.controllers;
    exports com.my.app.controllers.modals;
    exports com.my.app.models;
    exports com.my.app.exceptions;
    exports com.my.app.services;
    exports com.my.app.services.impl;

    opens com.my.app to spring.aop, spring.beans, spring.context, spring.core, spring.expression, spring.jcl;
    opens com.my.app.controllers to spring.aop, spring.beans, spring.context, spring.core, spring.expression, spring.jcl;
    opens com.my.app.controllers.modals to spring.aop, spring.beans, spring.context, spring.core, spring.expression, spring.jcl;
    opens com.my.app.services to spring.aop, spring.beans, spring.context, spring.core, spring.expression, spring.jcl;
    opens com.my.app.services.impl to spring.aop, spring.beans, spring.context, spring.core, spring.expression, spring.jcl;
}

0 ответов

Другие вопросы по тегам