Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/355.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我可以使用代码控制ApplicationContext在Spring Boot中做出的依赖项解析决策吗?_Java_Spring_Spring Boot_Dependency Injection - Fatal编程技术网

Java 我可以使用代码控制ApplicationContext在Spring Boot中做出的依赖项解析决策吗?

Java 我可以使用代码控制ApplicationContext在Spring Boot中做出的依赖项解析决策吗?,java,spring,spring-boot,dependency-injection,Java,Spring,Spring Boot,Dependency Injection,我在SpringBoot中使用自动连接将接口的实现注入到标记为组件的类中。 有时我需要使用某些接口的特定实现来运行应用程序(和或测试)。 我知道这可以通过注释组合(@Qualifier、@Primary等)来实现,但这些注释并不适合我的需要。 我希望能够(可选)编写在ApplicationContext确定将创建哪些接口实现之前运行的代码,并在该代码中覆盖一个或多个决策 我曾尝试使用如下代码: context.registerBean(MyService.class, () -> new

我在SpringBoot中使用自动连接将接口的实现注入到标记为组件的类中。 有时我需要使用某些接口的特定实现来运行应用程序(和或测试)。 我知道这可以通过注释组合(
@Qualifier
@Primary
等)来实现,但这些注释并不适合我的需要。 我希望能够(可选)编写在
ApplicationContext
确定将创建哪些接口实现之前运行的代码,并在该代码中覆盖一个或多个决策

我曾尝试使用如下代码:

context.registerBean(MyService.class, () -> new MyService());
如下所述:

但是,我在代码中找不到足够早地插入它的位置,这样它就会影响应用程序中的所有自动连接字段。 特别是,这是测试中的一个问题(标记为
@SpringBootTest

我希望能够使用类似于C语言的代码:

在一个测试中,我可能会使用以下代码,然后运行测试:

container.Register<IDataLayer, MockDataLayer>();
container.Register<IPersistenceLayer, FilePersistenceLayer>();
container.Register<IDataLayer, SQLDataLayer>();
container.Register<IPersistenceLayer, MockPersistenceLayer>();
container.Register();
container.Register();
在另一个测试中,我可能会使用以下代码,然后运行测试:

container.Register<IDataLayer, MockDataLayer>();
container.Register<IPersistenceLayer, FilePersistenceLayer>();
container.Register<IDataLayer, SQLDataLayer>();
container.Register<IPersistenceLayer, MockPersistenceLayer>();
container.Register();
container.Register();
在生产中,我可能会运行这个

container.Register<IDataLayer, SQLDataLayer>();
container.Register<IPersistenceLayer, FilePersistenceLayer>();
container.Register();
container.Register();
或者仅仅依靠文件配置


是否有可能创建对
ApplicationContext
所做选择的这种级别的控制,或者我必须依赖注释和xml配置文件的脆弱选择,以使我的每个测试完全按照我的需要运行?

据我所知,您正在寻找在特定需求的特定实现上运行的东西。 请关注本课程:

org.springframework.beans.factory.config.ServiceLocatoryFactoryBean

您可以配置它,定义实现,并根据需求获取bean

<beans:bean id="dataStrategyFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
        <beans:property name="serviceLocatorInterface" value="com.abc.DataStrategyFactory" />
    </beans:bean>
    <beans:alias name="FileImpl" alias="FILE" />
    <beans:alias name="DBImpl" alias="DB" />
    <beans:alias name="WSImpl" alias="WS" />
    <beans:alias name="NativeImpl" alias="DEFAULT" />


提供接口实现(这里是DataStrategyFactory)并根据需要在运行时获取对象。

函数bean是Spring 5的一个新特性,它更适合将函数注册为bean提供者。如果您只需要基于代码的配置,则不需要进行配置,但可以使用标准的基于Spring注释的配置

<beans:bean id="dataStrategyFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
        <beans:property name="serviceLocatorInterface" value="com.abc.DataStrategyFactory" />
    </beans:bean>
    <beans:alias name="FileImpl" alias="FILE" />
    <beans:alias name="DBImpl" alias="DB" />
    <beans:alias name="WSImpl" alias="WS" />
    <beans:alias name="NativeImpl" alias="DEFAULT" />
常规的、标准的SpringJavaConfig 简单示例,配置类:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ApplicationConfiguration {

    @Bean
    public DemoManager helloWorld()
    {
        return new DemoManagerImpl();
    }
}
主要类别:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
public static void main(String[] args) {
   ApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);

   HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
   helloWorld.setMessage("Hello World!");
   helloWorld.getMessage();
}
这将使用组件扫描来查找配置类,然后调用其方法来获取bean。您可以提供所需的配置类作为参数,您提到的SpringBootTest也支持这一点

因此,在测试时,您可以使用自己的测试配置来定制加载哪些bean并提供额外的bean。如果配置类是嵌套类,则无需指定它,甚至:

@SpringBootTest
@RunWith(SpringRunner.class)
public class SpringBootDemoApplicationTests
{  
    @Test
    public void testSomething() {
       // ...
    }

    @TestConfiguration
    static class MyTestConfiguration {

        //tests specific beans
        @Bean
        DataSource createDataSource(){
            //
        }
    }
}
使用
@TestConfiguration
将添加到您的配置中-如果您不想添加配置,而是完全替换配置,请使用
@springbootest(classes=YourCustomConfiguration.class)

备选方案:使用SpringJavaConfig手动创建应用程序上下文 如果您不想使用javaconfig或组件扫描,而是想“自己”注册配置类,您可以这样做,例如在main类中使用这种main方法:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
public static void main(String[] args) {
   ApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);

   HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
   helloWorld.setMessage("Hello World!");
   helloWorld.getMessage();
}
它不是普遍使用的,但也没有错

备选方案2:手动应用程序上下文、手动bean注册 如果确实希望避免使用configuration类,也可以这样做:

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SomeClass {
  public static void main(String args[]) {

    // first, we create empty context ourselves
    ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();

    // then we get its bean factory to be able to register stuff
    ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();

    // register our bean
    YourBean beanToRegister = new YourBean();
    beanFactory.registerSingleton(beanToRegister.getClass().getCanonicalName(), beanToRegister);

    ctx.refresh(); // context refresh actually updates the status

    // here we can test a bean was actually created and working
    YourBean helloWorld = ctx.getBean(YourBean.class);
    helloWorld.setAuthor("Hello World!");
    System.out.println(helloWorld.getAuthor());
  }
}
@SpringBootTest(properties={"spring.main.allow-bean-definition-overriding=true"})
public class MyConditionalTest {

    @Test
    public void testMyStuff() { 
    // do your test here
    }

    @TestConfiguration
    public OverrideSpringBean {

       @Bean
       public IDataLayer dataLayer() {
           return new MockPersistenceLayer();
       }
    }

}

与另一种选择一样,这不是Spring的常见方法,但也没有错。

如果我没有弄错,您的测试只需要条件bean,我建议您声明“生产”
@Bean
s在您的主类上,然后对于您的测试,您可以使用属性
spring.main.allow Bean definition overriding=true
,使用
@TestConfiguration
覆盖您需要的Bean

大概是这样的:

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SomeClass {
  public static void main(String args[]) {

    // first, we create empty context ourselves
    ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();

    // then we get its bean factory to be able to register stuff
    ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();

    // register our bean
    YourBean beanToRegister = new YourBean();
    beanFactory.registerSingleton(beanToRegister.getClass().getCanonicalName(), beanToRegister);

    ctx.refresh(); // context refresh actually updates the status

    // here we can test a bean was actually created and working
    YourBean helloWorld = ctx.getBean(YourBean.class);
    helloWorld.setAuthor("Hello World!");
    System.out.println(helloWorld.getAuthor());
  }
}
@SpringBootTest(properties={"spring.main.allow-bean-definition-overriding=true"})
public class MyConditionalTest {

    @Test
    public void testMyStuff() { 
    // do your test here
    }

    @TestConfiguration
    public OverrideSpringBean {

       @Bean
       public IDataLayer dataLayer() {
           return new MockPersistenceLayer();
       }
    }

}

为此使用基于代码的配置文件是否有问题?(xml配置通常不再是首选)我宁愿不必使用配置文件。我想用代码控制它。这给了我更好的控制,并且在开发环境中更易于维护。我不熟悉“基于代码”和“配置文件”的组合。你能提供这种解决方案的链接让我看一下吗?基于java代码的配置是代码,是执行Spring配置的标准方式。通常,配置文件本身由Spring扫描,但如果您也希望手动扫描,请给出一个示例。如果我理解,您需要动态注释来选择实现类。@eis感谢您提供此指针。如果这是可行的,它看起来确实像我所需要的,但是我看到,虽然它是为我的一些自动连接字段调用的,但它没有为其他字段调用。例如,我的一个迄今为止注释为Primary的实现似乎仍然被创建。我有一个标准的spring启动应用程序,我的大多数类在main/java中,一些测试和测试类在test/java中。我应该如何使用此配置模式来确保它将控制所有分辨率选择?为了实现这一点,删除所有主要注释就足够了吗?我认为“常规的、标准的SpringJavaConfig”对我来说是一个很好的解决方案,但我有一个问题。当我使用此方法时,是否应该从类中删除组件注释,以便通过配置类的解析是明确的?@DavidSackstein是的,对于使用javaconfig配置配置的bean。你可以混合使用这些方法,但不要在同一个类中使用,我会