注解配置应用上下文和父上下文

4
我在使用AnnotationConfigApplicationContext定义上下文层次结构时遇到了问题。问题出现在beanRefContext.xml中定义模块上下文并将“parent”属性设置为另一个上下文(基于XML/注释)。例如:

在模块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:
<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"/>

在模块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:
class FooHibernateDao implements FooDao {
    @Autowired
    @Qualifier("sessionFactory")
    private SessionFactory sessionsFactory;
    // CRUD methods
}

模块B应用程序上下文无法找到在模块A应用程序上下文中定义的bean。从查看AnnotationConfigApplicationContext的代码来看,扫描过程似乎不使用父级作为引用来解析bean。是否有什么我做错了或者我的尝试使用注释配置创建层次结构是不可能的?

这应该可以正常工作。您能否举一个未找到bean定义的示例,并说明子上下文如何解决它? - skaffman
db-context.xml文件中配置了数据源和sessionFactory(简单的XML bean配置),但是在尝试在module_B应用程序上下文中自动装配它们时,它说找不到sessionsFactory以满足dao依赖项。 - Bivas
请编辑您的问题,向我们展示相关组件。您的描述很好,但是有关具体细节的某些内容阻止了它的工作。 - skaffman
@skaffman 我添加了组件,希望对你有帮助。 - Bivas
5个回答

7
问题源于AnnotationConfigApplicationContext的构造函数进行扫描。因此,在此阶段尚未设置父级,只有在扫描完成后才设置父级,因为父级是通过属性设置的,所以它找不到您的bean的原因就在这里。
默认的AnnotationConfigApplicationContext bean没有接受父工厂的构造函数,不确定为什么会这样。
您可以使用普通的基于xml的应用程序上下文,并在其中配置注释扫描,或者您可以创建一个自定义工厂bean来创建注释应用程序上下文。这将指定父引用,然后进行扫描。
看一下源代码...
工厂将像这样:
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定义:

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

这是AnnotationConfigApplicationContext中明显的缺陷。请参见https://jira.springsource.org/browse/SPR-7791。 - skaffman

3
不要在子上下文中使用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

我遇到了同样的问题,

另一个可能性是扩展AnnotationConfigApplicationContext并仅添加所需的构造函数或在从Java实例化AnnotationConfigApplicationContext时以编程方式构建上下文。


0
我所做的是以下内容:
BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance("classpath:beanRefContext.xml");
BeanFactoryReference parentContextRef = locator.useBeanFactory("ear.context");
ApplicationContext parentContext = (ApplicationContext) parentContextRef.getFactory();
childContext.setParent(parentContext);

猜猜看,它成功了 :)

附言:如果有人知道如何用@Configuration类替换classpath:beanRefContext.xml,请告诉我们所有人。


0

我也遇到了类似的问题,在一些研究后,我发现可以使用AnnotationConfigApplicationContext的构造函数来设置上下文之间的层次结构。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接