如何以编程方式创建Spring上下文?

时间:2021-02-12 15:01:36

Does anyone know if there any way that I can programmatically create a bean context?

有谁知道我是否可以通过编程方式创建bean上下文?

I want to be able to do something like:

我希望能够做到这样的事情:

ConfigurableApplicationContext c = new ConfigurableApplicationContext();
BeanDefinition bd = new BeanDefinition();
bd.setId("id");
bd.setClassName("classname");
bd.setProperty("propertyName", propertyValue");
...etc...

or better still be able to inject a ready made bean into the application context:

或者更好的是仍然能够将现成的bean注入应用程序上下文:

c.addBean("beanId", beanObject);

Or if I'm using annotations:

或者,如果我正在使用注释:

c.setAnnotationAware(true);
c.setAnnotationScanBasePackage("packagename");

or

c.addAnnotatedSpringClass("classnamethatisannotated");

The rationale for this is that I want to be able override bean definitions for the purpose of testing - In my test I create this new application context, configured with code in the test (not in xml) and then make this test application context have as a parent the SUT application context.

这样做的理由是我希望能够覆盖bean定义以进行测试 - 在我的测试中,我创建了这个新的应用程序上下文,在测试中配置了代码(不是在xml中),然后使这个测试应用程序上下文具有as父级SUT应用程序上下文。

I haven't found any code in the spring libraries that can do this. Has anyone built something like this? Would it be possible to build something like this? I know the former approach is doable, I'm not 100% sure the latter approaches will work without conditions.

我没有在spring库中找到任何可以执行此操作的代码。有没有人建造这样的东西?是否有可能建立这样的东西?我知道前一种方法是可行的,我不能100%确定后一种方法可以无条件地工作。

5 个解决方案

#1


Try either:


JavaConfig code sample

@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}

BeanBuilder code sample

def bb = new grails.spring.BeanBuilder()

bb.beans { 
  dataSource(BasicDataSource) { 
    driverClassName = "org.hsqldb.jdbcDriver" 
    url = "jdbc:hsqldb:mem:grailsDB" 
    username = "sa" 
    password = "" 
  } 

  sessionFactory(ConfigurableLocalSessionFactoryBean) { 
    dataSource = dataSource
    hibernateProperties = [ "hibernate.hbm2ddl.auto":"create-drop", "hibernate.show_sql":true ] 
  }   
}

AtUnit code sample

Unit test

@RunWith(AtUnit.class)
@Container(Container.Option.SPRING)
@MockFramework(MockFramework.Option.EASYMOCK)
public class ExampleSpringEasyMockTest {

    @Bean @Unit UserManagerImpl manager;
    @Bean("fred") User fred;
    @Bean("userDao") @Mock UserDao dao;
    @Bean("log") @Stub Logger log;

    @Test
    public void testGetUser() {
        expect(dao.load(1)).andReturn(fred);
        replay(dao);
        assertSame(fred, manager.getUser(1));
        verify(dao);
    }


}

Context file ( private for the test )

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

  <bean id="userManager" class="atunit.example.subjects.UserManagerImpl">
      <constructor-arg ref="log"/>
      <property name="userDao" ref="userDao"/>
  </bean>

  <bean id="fred" class="atunit.example.subjects.User">
      <property name="id" value="500"/>
      <property name="username" value="fred"/>
  </bean>

</beans>

#2


Why don't you just use two different contexts? one for production, one for tests... you're going about this the hard way.

你为什么不只使用两种不同的语境?一个用于生产,一个用于测试...你正在努力解决这个问题。

#3


In Spring you can override bean definition as easily as making them appear again lower in the file. We use this a lot for the very purpose you described; to have a different bean definition for unit tests than for production.

在Spring中,您可以轻松覆盖bean定义,使其在文件中再次显示为低位。为了你描述的目的,我们经常使用它;单元测试的bean定义与生产不同。

This is the pattern we use for our test-context.xml

这是我们用于test-context.xml的模式

<import resource="classpath:production-context.xml">

<bean id="overriddenBean" class="com.MyClass">
   ....
</bean>

This means that the bean with id = overriddenBean will be wired into the classes in your production contewxts were it is referenced. Allowing you to swap the beans you need for testing in place of those that you need for production code.

这意味着具有id = overriddenBean的bean将连接到生成的contewxts中的类,如果它被引用。允许您交换测试所需的bean来代替生产代码所需的bean。

Hope this helps

希望这可以帮助

#4


Just add a bean factory post processor that can manipulate/add any bean definition

只需添加一个可以操作/添加任何bean定义的bean工厂后处理器

    public class ABeanFactoryPostProcessor implements
        BeanFactoryPostProcessor {

    public void postProcessBeanFactory(
            ConfigurableListableBeanFactory beanFactory) throws BeansException {

            if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinition beanDefinition=...

                ((BeanDefinitionRegistry)beanFactory).registerBeanDefinition(name, beanDefinition);
            }
    }

}

#5


There is brand new way to do this - Spring Boot

有一种全新的方式 - Spring Boot

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application implements CommandLineRunner {

    private static final Logger LOG = LoggerFactory.getLogger(Application.class);

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

    @Override
    public void run(String... args) throws Exception {
        LOG.info("Hello world");
    }
}

#1


Try either:


JavaConfig code sample

@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}

BeanBuilder code sample

def bb = new grails.spring.BeanBuilder()

bb.beans { 
  dataSource(BasicDataSource) { 
    driverClassName = "org.hsqldb.jdbcDriver" 
    url = "jdbc:hsqldb:mem:grailsDB" 
    username = "sa" 
    password = "" 
  } 

  sessionFactory(ConfigurableLocalSessionFactoryBean) { 
    dataSource = dataSource
    hibernateProperties = [ "hibernate.hbm2ddl.auto":"create-drop", "hibernate.show_sql":true ] 
  }   
}

AtUnit code sample

Unit test

@RunWith(AtUnit.class)
@Container(Container.Option.SPRING)
@MockFramework(MockFramework.Option.EASYMOCK)
public class ExampleSpringEasyMockTest {

    @Bean @Unit UserManagerImpl manager;
    @Bean("fred") User fred;
    @Bean("userDao") @Mock UserDao dao;
    @Bean("log") @Stub Logger log;

    @Test
    public void testGetUser() {
        expect(dao.load(1)).andReturn(fred);
        replay(dao);
        assertSame(fred, manager.getUser(1));
        verify(dao);
    }


}

Context file ( private for the test )

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

  <bean id="userManager" class="atunit.example.subjects.UserManagerImpl">
      <constructor-arg ref="log"/>
      <property name="userDao" ref="userDao"/>
  </bean>

  <bean id="fred" class="atunit.example.subjects.User">
      <property name="id" value="500"/>
      <property name="username" value="fred"/>
  </bean>

</beans>

#2


Why don't you just use two different contexts? one for production, one for tests... you're going about this the hard way.

你为什么不只使用两种不同的语境?一个用于生产,一个用于测试...你正在努力解决这个问题。

#3


In Spring you can override bean definition as easily as making them appear again lower in the file. We use this a lot for the very purpose you described; to have a different bean definition for unit tests than for production.

在Spring中,您可以轻松覆盖bean定义,使其在文件中再次显示为低位。为了你描述的目的,我们经常使用它;单元测试的bean定义与生产不同。

This is the pattern we use for our test-context.xml

这是我们用于test-context.xml的模式

<import resource="classpath:production-context.xml">

<bean id="overriddenBean" class="com.MyClass">
   ....
</bean>

This means that the bean with id = overriddenBean will be wired into the classes in your production contewxts were it is referenced. Allowing you to swap the beans you need for testing in place of those that you need for production code.

这意味着具有id = overriddenBean的bean将连接到生成的contewxts中的类,如果它被引用。允许您交换测试所需的bean来代替生产代码所需的bean。

Hope this helps

希望这可以帮助

#4


Just add a bean factory post processor that can manipulate/add any bean definition

只需添加一个可以操作/添加任何bean定义的bean工厂后处理器

    public class ABeanFactoryPostProcessor implements
        BeanFactoryPostProcessor {

    public void postProcessBeanFactory(
            ConfigurableListableBeanFactory beanFactory) throws BeansException {

            if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinition beanDefinition=...

                ((BeanDefinitionRegistry)beanFactory).registerBeanDefinition(name, beanDefinition);
            }
    }

}

#5


There is brand new way to do this - Spring Boot

有一种全新的方式 - Spring Boot

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application implements CommandLineRunner {

    private static final Logger LOG = LoggerFactory.getLogger(Application.class);

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

    @Override
    public void run(String... args) throws Exception {
        LOG.info("Hello world");
    }
}