简体   繁体   English

自动将混凝土类装配到抽象装饰器中

[英]Autowiring Concrete Class into Abstract Decorator

Greetings ladies and gents, 女士们和女士们,您好!

I'm implementing a system where we are trying to keep things highly flexible. 我正在实施一个系统,试图使事情保持高度灵活性。 We have an abstract superclass called EmailTaskService which has two classes inheriting from it. 我们有一个名为EmailTaskService的抽象超类,它有两个继承自它的类。 One is EmailTaskServiceImpl (the concrete implementation class), and the other is another abstract class called ScheduledEmailTaskService which has a subclass called ScheduledEmailTaskServiceImpl . 一个是EmailTaskServiceImpl (具体的实现类),另一个是另一个名为ScheduledEmailTaskService抽象类,它具有一个名为ScheduledEmailTaskServiceImpl的子类。

To keep things flexible, we are using both a vertical and horizontal decorator pattern on the ScheduledEmailTaskService . 为了保持灵活性,我们在ScheduledEmailTaskService上同时使用了垂直和水平装饰器模式。 This means that it inherits from EmailTaskService as well as composing in the concrete service itself. 这意味着它继承自EmailTaskService以及组成具体的服务本身。 But my problem comes from trying to autowire the concrete EmailTaskServiceImpl , all I get is a No qualifying bean of type EmailTaskServiceImpl exception thrown. 但是我的问题来自于尝试自动连接具体的EmailTaskServiceImpl ,我得到的只是抛出了No qualifying bean of type EmailTaskServiceImplNo qualifying bean of type EmailTaskServiceImpl Obviously I can't just autowire EmailTaskService because its an abstract class, not an interface. 显然,我不能仅仅自动连接EmailTaskService因为它是一个抽象类,而不是一个接口。

I'll post the code below to give you an idea of what is going on. 我将在下面的代码中发布,以使您了解发生了什么。
EmailTaskService EmailTask​​Service

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

    @Autowired
    protected EmailTaskDAO emailTaskDao;

    public abstract void deleteEmailTask(I emailTaskId);

} }

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

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

ScheduledEmailTaskService ScheduledEmailTask​​Service

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

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

    more concretey methods go here

In case you are wondering GenericService<E, I> is an interface that specifies CRUD methods for all my services to implement which is why EmailTaskService implements it. 如果您想知道GenericService<E, I>是一个接口,为我要实现的所有服务指定CRUD方法,这就是EmailTaskService实施它的原因。

So my question is how do I get some sort of reference to EmailTaskService or EmailTaskServiceImpl successfully available in ScheduledEmailTaskService either through Autowiring or through another sensible method. 所以我的问题是,如何通过自动装配或通过其他明智的方法来获取对ScheduledEmailTaskService成功可用的EmailTaskServiceEmailTaskServiceImpl某种引用。 If this just isn't possible, Why? 如果这不可能,为什么?

EDIT 1 (Context Configuration): Ask and ye shall receive: 编辑1(上下文配置):Ask and ye将收到:

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

EDIT 2: Actual Proper Stacktrace: 编辑2:实际正确的Stacktrace:

[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

And before you ask, yes my JUnit is configured to use the Spring Application Context correctly. 在您询问之前,是的,我的JUnit已配置为正确使用Spring Application Context。

Given that you have @Transactional everywhere, I'm going to assume you have some proxying going on with JDK proxies. 鉴于到处都有@Transactional ,我将假设您对JDK代理进行了一些代理。

As it turns out, JKD proxies can only get your target bean's interfaces, not their class hierarchy. 事实证明,JKD代理只能获取目标bean的接口,而不能获取它们的类层次结构。 So the proxy created for your EmailTaskServiceImpl is not of type EmailTaskServiceImpl and can therefore not fill in for an injection target of that type. 因此,对于你创建的代理EmailTaskServiceImpl不是类型的EmailTaskServiceImpl ,因此不能对这种类型的注入目标的练习I. Instead, it is of type EmailTaskService , but your field is 而是类型为EmailTaskService ,但您的字段是

@Autowired
protected EmailTaskServiceImpl emailTaskService;

so that won't do. 这样就不会了。

There are two solutions: 有两种解决方案:

The first is to change your configuration to use CGLIB proxies. 首先是更改您的配置以使用CGLIB代理。 You'll need to change your transactional configuration to 您需要将事务配置更改为

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

and add the CGLIB libraries to your classpath. 并将CGLIB库添加到您的类路径中。

The second is to change your field to 第二是将您的字段更改为

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

and your class declaration to 和你的班级声明

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

Spring will use the name instead of the type when determining which bean to inject. 在确定要注入哪个bean时,Spring将使用名称而不是类型。 This is necessary because you have many EmailTaskService candidate beans. 这是必需的,因为您有许多EmailTaskService候选bean。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM