开发者

Generic Java service with programmatic bean creation does not inject EntityManager

I have a (hopefully) standard set-up with a bunch of @Controller, @Service, @Entity and @Repository annotated classes, for example:

@Service
public class ChannelManager {
    @Autowired
    private ChannelDao channelDao;

    public List<Channel> allChannels() {
        return channelDao.findAll();
    }
}

where ChannelDao is:

public interface ChannelDao extends PersistentDao<Channel> {}

ChannelImpl is:

@Repository
public class ChannelImpl extends PersistentImpl<Channel> implements ChannelDao {}

Channel is:

@Entity开发者_如何学JAVA
@Table(name = "channel")
public class Channel extends Persistent {
    @Id
    @Column(name = "id", nullable = false)
    private Long id;
    // set/get
}

PersistentDao is:

public interface PersistentDao<T extends Persistent> {
    public List<T> findAll();
}

and finally PersistentImpl is:

public class PersistentImpl<T extends Persistent> implements PersistentDao<T> {
    @PersistenceContext
    protected EntityManager entityManager;

    private Class<T> clazz; // gets assigned correctly

    public List<T> findAll() {
        return entityManager.createQuery("from " + clazz.getSimpleName()).getResultList();
    }
}

All this works as expected. I am using <context:annotation-config/> and <context:component-scan/> as well as the <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" ...> with a persistence.xml. Basically it all works.

But now I want to generalize the ChannelManager since I have many other entities and want to avoid duplication. For example I also need a JobManager with a List<Job> allJobs() { return jobDao.findall(); } method.

So I created this:

@Service
public class GeneralManager<T extends PersistentDao, E extends Persistent>  {
    @Autowired
    private AutowireCapableBeanFactory factory;
    public void setFactory(AutowireCapableBeanFactory factory) {
        this.factory = factory;
    }

    private Class<? extends T> impl;
    private T dao;

    public GeneralManager(Class<? extends T> impl) throws Exception {
        this.impl = impl;
    }

    @PostConstruct
    public void postCtor() {
        dao = factory.createBean(impl); // seems to be created and is not null
    }

    public List<E> all() {
        return dao.findAll();
    }
}

And I updated the context.xml

<bean id="jobImpl" class="a.b.c.GeneralManager">
    <property name="factory" ref="autowireFactory"/>
    <constructor-arg value="a.b.c.JobImpl"/>
</bean>

<bean id="autowireFactory" class="org.springframework.beans.factory.support.DefaultListableBeanFactory"/>

<context:annotation-config/>
  <context:component-scan base-package="a.b.c">
    <context:exclude-filter type="regex" expression=".*GeneralManager"/>
</context:component-scan>

And finally added a declaration in the controller:

@Autowired
@Qualifier(value="jobImpl")
private GeneralManager<JobDao, Job> jobManager;

Just imagine the Jobxxx classes to be similar to the Channelxxx ones.

However, when it comes to execute the findAll() method in the JobImpl specialization, the entityManager is null, so something is not set-up correctly.

So my question really is "can this be done"? Although if there is an alternate and nicer way to do this please enlighten me!

Edit: Might be of note that I'm using Spring 3.0.5, Java 1.6.0_21-b07 and Hibernate 3.6.2.Final


So after thinking a bit more about it, I've gone in a slightly different direction by explicitly defining the beans which was something I was trying to avoid. But it is working for me. I still don't know if what I was trying to do was just not possible because of type erasure.

My code now looks like:

public class GeneralManager<T extends PersistentDao, E extends Persistent>  {
    private T dao;
    public void setDao(T dao) {
        this.dao = dao;
    }
    ...
}

with the controller using:

@Autowired
private GeneralManager<ChannelDao, Channel> channelManager;

@Autowired
private GeneralManager<JobDao, Job> jobManager;

and context.xml now has:

<bean id="jobManager" class="com.a.b.service.EntityManager" p:dao-ref="jobImpl"/>
<bean id="jobImpl" class="com.a.b.dao.JobImpl"/>
<bean id="channelManager" class="com.a.b.service.EntityManager" p:dao-ref="channelImpl"/>
<bean id="channelImpl" class="com.a.b.dao.ChannelImpl"/>

Hope this helps someone!


why not simply use spring-data project? it does exactly what you need and more.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜