AnnotationConfigApplicationContext и родительский контекст - PullRequest
4 голосов
/ 06 декабря 2010

Я столкнулся с проблемой, пытаясь определить контекстную иерархию, используя AnnotationConfigApplicationContext.

Проблема заключается в определении контекста модуля внутри beanRefContext.xml и установке свойства parent с другим контекстом (на основе XML / Annotated).

Пример:

beanRefContext.xml в модуле A

<bean id="moduleA_ApplicationContext"  
      class="org.springframework.context.support.ClassPathXmlApplicationContext">
    <property name="configLocations">
        <list>
            <value>classpath:db-context.xml</value>
        </list>
    </property>
</bean>

дб-context.xml

<bean id="dataSource" 
      class="org.apache.commons.dbcp.BasicDataSource" 
      destroy-method="close"
      p:driverClassName="org.h2.Driver"
      p:url="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=MySQL;TRACE_LEVEL_SYSTEM_OUT=2"/>

<!-- Hibernate Session Factory -->
<bean name="sessionFactory" 
      class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="useTransactionAwareDataSource" value="true"/>
        <property name="packagesToScan">
            <list>
                <value>com.example.model</value>
            </list>
        </property>
        <property name="hibernateProperties">
        <!-- hibernate props -->
        </property>
</bean>

beanRefContext.xml в модуле B

<bean id="moduleB_ApplicationContext" 
      class="org.springframework.context.annotation.AnnotationConfigApplicationContext" >
    <property name="parent" ref="moduleA_ApplicationContext"/>
        <constructor-arg>
            <list>
                <value>com.example.dao</value>
            </list>
        </constructor-arg>
</bean>

FooHibernateDao

class FooHibernateDao implements FooDao {
    @Autowired
    @Qualifier("sessionFactory")
    private SessionFactory sessionsFactory;

    // CRUD methods
}

Приложению контекста модуля B не удается найти bean-компонент, определенный в контексте приложения модуля A.
Из кода AnnotationConfigApplicationContext видно, что процесс сканирования не использует родителя в качестве ссылки для разрешения bean-компонентов.

Есть ли что-то, что я делаю неправильно, или моя попытка создать иерархию невозможна при настройке аннотации?

Ответы [ 5 ]

6 голосов
/ 06 декабря 2010

Проблема связана с тем, что конструктор AnnotationConfigApplicationContext выполняет сканирование. Таким образом, родительский элемент не устанавливается на этом этапе, он устанавливается только после того, как сканирование выполнено, поскольку родительский объект установлен свойством - таким образом, причина, по которой он не находит ваш компонент.

По умолчанию bean-компонент AnnotationConfigApplicationContext не имеет конструктора, который принимает родительскую фабрику - не уверен, почему.

Вы можете либо использовать обычный контекст приложения на основе xml и настроить там сканирование аннотации, либо вы можете создать пользовательский компонент-заполнитель, который будет создавать контекст приложения аннотации. Это будет указывать родительскую ссылку, а затем делать сканирование.

Взгляните на источник ...

Фабрика будет выглядеть так:

public class AnnotationContextFactory implements FactoryBean<ApplicationContext> {

private String[] packages;
private ApplicationContext parent;

@Override
public ApplicationContext getObject() throws Exception {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.setParent(parent);
    context.scan(packages);
    context.refresh();
    return context;
}

@Override
public Class<ApplicationContext> getObjectType() {
    return ApplicationContext.class;
}

@Override
public boolean isSingleton() {
    return true;
}

public void setPackages(String... args) {
    this.packages = args;
}

public void setParent(ApplicationContext parent) {
    this.parent = parent;
    }
}

И ваше определение бина:

<bean id="moduleB_ApplicationContext" class="za.co.test2.AnnotationContextFactory">
    <property name="parent" ref="moduleA_ApplicationContext" />
    <property name="packages">
        <list>
            <value>za.co.test2</value>
        </list>
    </property>
</bean>
3 голосов
/ 18 мая 2016

Не используйте XML для дочернего контекста. Используйте ctx.setParent, затем ctx.register. Как это:

public class ParentForAnnotationContextExample {

    public static void main(String[] args) {
        ApplicationContext parentContext = new AnnotationConfigApplicationContext(ParentContext.class);

        AnnotationConfigApplicationContext childContext = new AnnotationConfigApplicationContext();
        childContext.setParent(parentContext);
        childContext.register(ChildContext.class); //don't add in the constructor, otherwise the @Inject won't work
        childContext.refresh();

        System.out.println(childContext.getBean(ParentBean.class));
        System.out.println(childContext.getBean(ChildBean.class));

        childContext.close();
    }

    @Configuration
    public static class ParentContext {
        @Bean ParentBean someParentBean() {
            return new ParentBean();
        }
    }

    @Configuration
    public static class ChildContext {
        @Bean ChildBean someChildBean() {
            return new ChildBean();
        }
    }

    public static class ParentBean {}

    public static class ChildBean {
        //this @Inject won't work if you use ChildContext.class in the child AnnotationConfigApplicationContext constructor
        @Inject private ParentBean injectedFromParentCtx;
    }
}
1 голос
/ 19 марта 2011

Я сталкиваюсь с той же проблемой,

Другой возможностью является расширение AnnotationConfigApplicationContext и добавление только необходимого конструктора или создание контекста программно, если вы создаете экземпляр AnnotationConfigApplicationContext из Java.

0 голосов
/ 27 июня 2019

Я также столкнулся с подобной проблемой, и после некоторых исследований я нашел следующий подход с использованием конструктора из AnnotationConfigApplicationContext, который позволяет установить иерархию между контекстами

DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(parentContext);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(lbf);
context.register(annotatedClass1.class, annotatedClass2.class);
context.refresh();
0 голосов
/ 18 ноября 2016

То, что я сделал, было следующим:

BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance("classpath:beanRefContext.xml");
BeanFactoryReference parentContextRef = locator.useBeanFactory("ear.context");
ApplicationContext parentContext = (ApplicationContext) parentContextRef.getFactory();
childContext.setParent(parentContext);

И угадайте, что, это сработало :)

PS: Если кто-нибудь знает, как заменить classpath: beanRefContext.xml на класс @Configuration, пожалуйста, сообщите нам всем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...