首页 > 解决方案 > 为什么我无法使用 Spring Boot 在我的 Spring 上下文中获取我的 Bean 定义?

问题描述

问这个我感觉很愚蠢,但我不明白我的代码哪里错了。

上下文是:

问题是:

现在细节:

我的 SpringBootApplication :

@SpringBootApplication
public class HbbTVApplication {
    public static void main(String[] args) {
        SpringApplication.run(HbbTVApplication.class, args);
    }
}

我的@Configuration 类:

@Configuration
@Profile(value = { "dev", "int", "pre", "pro" })
public class StandaloneFrontalConfig extends WebMvcConfigurerAdapter {

@Value("${kafka.bootstrap-servers}")
private String bootstrapServers;

@Bean
public Map<String, Object> producerConfigs() {
    Map<String, Object> props = new HashMap<>();
    props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
    props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
    props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
    return props;
}

@Bean
public ProducerFactory<String, String> producerFactory() {
    return new DefaultKafkaProducerFactory<>(producerConfigs());
}

@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
    return new KafkaTemplate<>(producerFactory());
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**").addResourceLocations("classpath:/standalone/");
}

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurerAdapter() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*");
        }
    };
}

@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
@Bean
public Security securityManager() {
    return new Security();
}

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public KngAflow getTechnicalCookie() {
    return new KngAflow();
}

@Bean
public EmbeddedServletContainerCustomizer customizer() {
    return new EmbeddedServletContainerCustomizer() {

        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            if (container instanceof JettyEmbeddedServletContainerFactory) {
                customizeJetty((JettyEmbeddedServletContainerFactory) container);
            }
        }

        private void customizeJetty(JettyEmbeddedServletContainerFactory jetty) {
            jetty.addServerCustomizers(new JettyServerCustomizer() {

                @Override
                public void customize(Server server) {
                    for (Connector connector : server.getConnectors()) {
                        if (connector instanceof ServerConnector) {
                            HttpConnectionFactory connectionFactory = ((ServerConnector) connector)
                                    .getConnectionFactory(HttpConnectionFactory.class);
                            connectionFactory.getHttpConfiguration().setCookieCompliance(CookieCompliance.RFC2965);
                        }
                    }
                }
            });
        }
    };
}
}

我的@服务:

@Service
public class CookieService implements services.CookieService, InitializingBean {

    /**
     * Serializable
     */
    private static final long serialVersionUID = -1997257884335775587L;

    @Autowired
    ApplicationContext app;

    @Override
    public Cookie createTechnicalCookie() {
        return new Cookie(app.getBean(KngAflow.class), null);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (app != null) {
            for (String bean : app.getBeanDefinitionNames()) {
                System.out.println("Bean: " + bean);
            }
        }
    }
}

和“未定义”的bean:

@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties({ "security", "maxAge", "domain", "updated" })
public class KngAflow implements Serializable, InitializingBean {

    @JsonProperty(value = "did")
    private String did;
    @JsonProperty(value = "checksum")
    private String checksum;
    @Autowired
    private Security security;
    private Integer maxAge;
    private String domain;
    private boolean updated = false;

    public KngAflow() {
        domain = ".mydomain.com";
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        did = UUID.randomUUID().toString();
        maxAge = 365 * 24 * 60 * 60;
        checksum = security.encrypt(did + security.md5(did));
    }
}

注意:课程不完整,我的项目中还有更多课程。我只把我看到的作为相关信息。如果需要其他东西,请问我。顺便说一句,所有的端点都被定义为一个唯一的@Controller 类,并且除了那些需要getTechCookie @Bean 的端点之外,所有的端点都在工作。

所以,我的问题发生在运行时执行中。当我启动我的 Spring Boot 应用程序时,Jetty 会启动并监听配置的端口。不过,如果您查看 CookieService @Service,我会列出自动装配上下文中定义的所有 bean 名称,而我的 getTechnicalCookie(又名 KngAflow)@Bean 丢失了。我不明白为什么。

当然,当我调用 @controller 来执行 @Service 代码时,会在执行 app.getBean(KngAflow.class) 行时抛出 NoSuchBeanDefinitionException。

我尝试使用 bean 名称而不是 bean 类型,没有变化。出于测试目的(因为从逻辑的角度来看它没有意义),我将我的 bean getTechCookie @Bean 定义为单例范围的 bean,并且 ApplicationContext 中仍然缺少该名称。

最后但并非最不重要的是:Eclipse 一切正常!

我的意思是,我所有的开发人员都是使用 Eclipse IDE 完成的。我的 Spring Boot 应用程序是使用 Maven 构建的,并且在 Eclipse 中执行它可以正常工作(并且我的 getTechCookie Bean 已定义并列出)。

当我使用 Maven Spring Boot 插件打包我的应用程序并使用 java -jar 执行它时,我的 getTechCookie (KngAflow.class) bean 丢失了。然而,这个类存在于 jar 中。

启动 Spring Boot 应用程序的 Spring 参数是 Spring 默认值(端口 8080,无 SSL,...),并且 active.profiles 始终介于 dev、int、pre 或 pro 之间(在我的 @Configuration 类中定义的那些)

我究竟做错了什么?

谢谢!

如果有帮助,我添加我的 POM 定义:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>my-app</artifactId>
    <packaging>jar</packaging>

    <parent>
        <groupId>com.mydomain.bigdata</groupId>
        <artifactId>mybigapp</artifactId>
        <version>1.1-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>${basedir}/src/main/resources</directory>
                <filtering>true</filtering>
                <includes>
                    <include>**/*</include>
                    <include>application.yml</include>
                </includes>
            </resource>
        </resources>

        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

编辑:我将我的@Service 类更改为“强制”spring 以接受我的类作为原型 bean,它可以工作。它非常难看,但它有效。但是,如果有人可以帮助我找出问题所在,我不喜欢这种解决方法:

@Override
    public void afterPropertiesSet() throws Exception {
        if (!context.containsBeanDefinition(KngAflow.class.getName()))
            context.registerBeanDefinition(KngAflow.class.getName(),
                    BeanDefinitionBuilder.genericBeanDefinition(KngAflow.class).setScope("prototype").getBeanDefinition());
    }

标签: javaspringspring-boot

解决方案


我做了一个以下简单的应用程序来重现问题。

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        run(Application.class, args);
    }

}
@Configuration
@Profile("dev")
public class BeanConfiguration {

    @Bean
    @Scope(scopeName = SCOPE_PROTOTYPE)
    public PrototypeBean prototypeBean() {
        return new PrototypeBean();
    }

}
public class PrototypeBean {}
@Service
@Slf4j
public class SingletonBean implements InitializingBean {

    @Autowired
    private ApplicationContext context;

    public PrototypeBean getPrototypeBean() {
        return context.getBean(PrototypeBean.class);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        for (String name : context.getBeanDefinitionNames()) {
            Class<?> c = context.getBean(name).getClass();
            log.debug("===> Name: {}, Type = {}", name, c.getTypeName());
        }
    }

}
@RestController
@RequestMapping("/bean")
public class BeanRestController {

    @Autowired
    private SingletonBean singletonBean;

    @GetMapping("/name")
    public String getName() {
        return singletonBean.getPrototypeBean().getClass().getName();
    }

}

当我使用-Dspring.profiles.active=dev设置执行应用程序时

然后我在日志中看到没有问题,并且 REST 端点正确返回响应:

===> Name: prototypeBean, Type = PrototypeBean

但是如果我在没有配置文件设置的情况下执行应用程序

然后我在日志中看到错误并且 REST 端点引发异常:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'PrototypeBean' available

推荐阅读