java AnnotationConfigApplicationContext 和父上下文

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4364221/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 06:02:57  来源:igfitidea点击:

AnnotationConfigApplicationContext and parent context

javaspringannotations

提问by Bivas

I'm facing an issue trying to define a context hierarchy using AnnotationConfigApplicationContext.

我在尝试使用AnnotationConfigApplicationContext.

The problem is when defining a module context inside beanRefContext.xmland setting the 'parent' property with another context (XML/Annotated based).

问题是在内部定义模块上下文beanRefContext.xml并使用另一个上下文(基于 XML/注释)设置“父”属性时。

Example:

例子:

beanRefContext.xml in module A

模块 A 中的 beanRefContext.xml

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

db-context.xml

db-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 in module B

模块 B 中的 beanRefContext.xml

<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

FooHibernateDao

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

    // CRUD methods
}

Module B application context fails to find bean defined in module A application context.
From looking at the code of AnnotationConfigApplicationContextit seems that the scanning process doesn't use the parent as a reference to resolve beans.

模块 B 应用程序上下文无法找到模块 A 应用程序上下文中定义的 bean。
从代码来看AnnotationConfigApplicationContext,扫描过程似乎没有使用父级作为解析bean的参考。

Is there something I'm doing wrong or my attempt to create a hierarchy is impossible with annotation configuration?

是不是我做错了什么,或者我无法使用注释配置来创建层次结构?

回答by Michael Wiles

The problem stems from the fact that the constructor of the AnnotationConfigApplicationContext does the scan. Thus the parent is not set at this stage, it is only set after the scan is done as the parent is set by a property - thus the reason why it does not find your bean.

问题源于 AnnotationConfigApplicationContext 的构造函数执行扫描的事实。因此,在此阶段未设置父级,它仅在扫描完成后设置,因为父级由属性设置 - 这就是它找不到您的 bean 的原因。

The default AnnotationConfigApplicationContext bean does not have a constructor that takes a parent factory - not sure why.

默认的 AnnotationConfigApplicationContext bean 没有采用父工厂的构造函数 - 不知道为什么。

You can either use the normal xml based application context and configure your annotation scanning in there or you can create a custom fatory bean that will do create the annotation application context. This would specify the parent reference and then do the scan.

您可以使用普通的基于 xml 的应用程序上下文并在其中配置您的注释扫描,或者您可以创建一个自定义的 fatory bean 来创建注释应用程序上下文。这将指定父引用,然后进行扫描。

Take a look at the source...

看一下源码...

The factory would look like this:

工厂看起来像这样:

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;
    }
}

And your bean definition:

还有你的 bean 定义:

<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>

回答by Paul Hilliar

Don't use XML for the child context. Use ctx.setParent then ctx.register. Like this:

不要将 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;
    }
}

回答by Rafael

I run into the same problem,

我遇到了同样的问题,

Another possibility is to extend AnnotationConfigApplicationContext and add just the required constructor or build the context programmatically, if you are instantiating the AnnotationConfigApplicationContext from java.

如果您从 java 实例化 AnnotationConfigApplicationContext,另一种可能性是扩展 AnnotationConfigApplicationContext 并仅添加所需的构造函数或以编程方式构建上下文。

回答by Anand Rockzz

What I did was the following:

我所做的是以下内容:

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

And guess what, it worked:)

猜猜看,它奏效了:)

PS:If anyone knows how to replace the classpath:beanRefContext.xml with an @Configuration class, please let us all know.

PS:如果有人知道如何用@Configuration 类替换 classpath:beanRefContext.xml,请让我们都知道。

回答by xavipen

I also run into a similar problem and after some research I found the following approach using a constructor from AnnotationConfigApplicationContext that allows to set the a hierarchy between contexts

我也遇到了类似的问题,经过一些研究,我发现以下方法使用 AnnotationConfigApplicationContext 中的构造函数,允许设置上下文之间的层次结构

DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(parentContext);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(lbf);
context.register(annotatedClass1.class, annotatedClass2.class);
context.refresh();