【问题标题】:Autowiring Concrete Class into Abstract Decorator将具体类自动装配到抽象装饰器中
【发布时间】:2014-04-06 23:50:11
【问题描述】:

女士们,先生们,您好,

我正在实施一个系统,我们试图让事情保持高度灵活。我们有一个名为EmailTaskService 的抽象超类,它有两个继承自它的类。一个是EmailTaskServiceImpl(具体实现类),另一个是另一个抽象类ScheduledEmailTaskService,它有一个子类ScheduledEmailTaskServiceImpl

为了保持灵活性,我们在ScheduledEmailTaskService 上同时使用垂直和水平装饰器模式。这意味着它继承自 EmailTaskService 以及组合在具体服务本身中。但是我的问题来自于尝试自动装配具体的EmailTaskServiceImpl,我得到的只是一个No qualifying bean of type EmailTaskServiceImpl 抛出的异常。显然我不能只自动装配EmailTaskService,因为它是一个抽象类,而不是一个接口。

我将在下面发布代码,让您了解发生了什么。
EmailTask​​Service

public abstract class EmailTaskService<E, I> implements GenericService<E, I>{

    @Autowired
    protected EmailTaskDAO emailTaskDao;

    public abstract void deleteEmailTask(I emailTaskId);

}

EmailTask​​ServiceImpl

@Service("emailTaskService")
public class EmailTaskServiceImpl extends EmailTaskService<EmailTask, Long>{

    @Override
    @Transactional
    public long getTotalObjects() {
        return super.emailTaskDao.count();
    }
    etc etc other methods here doing concretey things.

ScheduledEmailTask​​Service

public abstract class ScheduledEmailTaskService<E, I> extends EmailTaskService<E, I> {

    @Autowired
    protected ScheduledEmailTaskDAO scheduledEmailTaskDao;
    @Autowired
    @Qualifier("emailTaskService")
    protected EmailTaskService<EmailTask, Long> emailTaskService;

    @Override
    public abstract void deleteEmailTask(I emailTaskId) throws InvalidInvocationException;

    public abstract void deleteScheduledEmailTask(I scheduledEmailTaskId);
}

ScheduledEmailTask​​Service

@Service
public class ScheduledEmailTaskServiceImpl extends ScheduledEmailTaskService<ScheduledEmailTask, Long> {

    @Override
    @Transactional
    public long getTotalObjects() {
        return super.scheduledEmailTaskDao.count();
    }

    more concretey methods go here

如果您想知道 GenericService&lt;E, I&gt; 是一个接口,它为我的所有服务指定 CRUD 方法来实现,这就是 EmailTaskService 实现它的原因。

所以我的问题是如何通过自动装配或其他合理方法在ScheduledEmailTaskService 中成功获得对EmailTaskServiceEmailTaskServiceImpl 的某种引用。如果这是不可能的,为什么?

编辑 1(上下文配置): 问,你会收到:

<beans Spring namespace config etc etc etc>
<context:component-scan base-package="au.com.mail" />

<mvc:annotation-driven />

<tx:annotation-driven />

<task:annotation-driven/>

<!-- Project Properties -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <value>classpath:project.properties</value>
    </property>
</bean> 
</beans>

编辑 2:实际正确的堆栈跟踪:

[junit] Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scheduledEmailTaskServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: protected au.com.mail.service.emailtask.EmailTaskService au.com.mail.service.emailtask.ScheduledEmailTaskService.emailTaskService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [au.com.mail.service.emailtask.EmailTaskService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=emailTaskService)}
[junit]     at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
[junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1146)
[junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
[junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
[junit]     at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:296)
[junit]     at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
[junit]     at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:293)
[junit]     at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
[junit]     at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:628)
[junit]     at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
[junit]     at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
[junit]     at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:120)
[junit]     at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
[junit]     at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)
[junit]     at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:248)
[junit]     at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContextInternal(CacheAwareContextLoaderDelegate.java:64)
[junit]     at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:91)
[junit]     ... 50 more
[junit] Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: protected au.com.mail.service.emailtask.EmailTaskService au.com.mail.service.emailtask.ScheduledEmailTaskService.emailTaskService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [au.com.mail.service.emailtask.EmailTaskService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=emailTaskService)}
[junit]     at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:517)
[junit]     at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
[junit]     at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:286)
[junit]     ... 66 more
[junit] Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [au.com.mail.service.emailtask.EmailTaskService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=emailTaskService)}
[junit]     at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:988)
[junit]     at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:858)
[junit]     at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:770)
[junit]     at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:489)
[junit]     ... 68 more

在你问之前,是的,我的 JUnit 已配置为正确使用 Spring 应用程序上下文。

【问题讨论】:

  • 您的方式很好。确保您的 EmailTaskServiceImpl 类正在被组件扫描。
  • 其实我们可以看到你的上下文配置吗?
  • 是的,两个 *ServiceImpl 类都在 ApplicationContext 中可用,但我仍然得到一个 No Bean Definition 异常

标签: java spring inheritance decorator autowired


【解决方案1】:

鉴于您到处都有@Transactional,我将假设您正在使用 JDK 代理进行一些代理。

事实证明,JKD 代理只能获取目标 bean 的接口,而不是它们的类层次结构。因此为您的EmailTaskServiceImpl 创建的代理不是EmailTaskServiceImpl 类型,因此无法填写该类型的注入目标。相反,它的类型是EmailTaskService,但您的字段是

@Autowired
protected EmailTaskServiceImpl emailTaskService;

这样就不行了。

有两种解决方案:

首先是更改您的配置以使用 CGLIB 代理。您需要将事务配置更改为

<tx:annotation-driven proxy-target-class="true" />

并将 CGLIB 库添加到您的类路径中。

第二个是将你的字段改为

@Autowired
@Qualifier("someName")
protected EmailTaskService emailTaskService;

和你的类声明

@Service("someName")
public class EmailTaskServiceImpl extends EmailTaskService<EmailTask, Long>{

Spring 在确定注入哪个 bean 时将使用名称而不是类型。这是必要的,因为您有许多 EmailTaskService 候选 bean。

【讨论】:

  • 好的,试过了,没有变化。我的问题不在于我有多个可用于自动装配的 bean。我没有得到任何可用于自动装配的 bean(EmailTaskService 类型)。
  • @JamesMassey 您的包裹似乎不适合您在component-scan 中的内容。 au.com.afgonline.mailau.com.mail.
  • gah,那是我试图避免使用的公司名称。我向你保证它的所有配置都正确。我只是忘了从堆栈跟踪中删除名称。哦,也是对您的帖子的回应,如果我可以避免它们,我非常希望避免使用 CGLIB 代理。我宁愿使用静态工厂而不是 CGLIB 代理。
  • @JamesMassey 我能想到的最后一件事是这些类没有添加到您的测试类路径中。
  • 抱歉,在我的测试配置中,我的应用程序上下文中有上下文配置,这些类肯定存在。我只是不知道为什么他们也没有参考。静态工厂会是要走的路吗?无论如何都感谢您尝试
猜你喜欢
  • 2017-05-13
  • 1970-01-01
  • 1970-01-01
  • 2019-05-19
  • 1970-01-01
  • 2023-01-31
  • 1970-01-01
  • 1970-01-01
  • 2015-02-07
相关资源
最近更新 更多