简体   繁体   English

在HashMap中存储注入的Spring Bean

[英]Storing injected Spring Beans in a HashMap

This is a question about the use of Spring for business objects and how they are stored after being injected into classes that need them. 这是一个关于将Spring用于业务对象的问题,以及将它们注入到需要它们的类中后如何存储它们的问题。

In a typical Spring setup, you might have a business object as follows: 在典型的Spring设置中,您可能具有如下业务对象:

package app.service.impl;

public class MaintainStudent implements app.service.intf.MaintainStudent
{
    protected app.dao.intf.CourseDAO courseDAO;
    protected app.dao.intf.StudentDAO studentDAO;
    protected app.dao.intf.CourseAssignDAO courseAssignDAO;

    //injection methods here
    public void setCourseDAO(app.dao.intf.CourseDAO courseDAO)
    {
        this.courseDAO = courseDAO;
    }

    public void setStudentDAO(app.dao.intf.StudentDAO studentDAO)
    {
        this.studentDAO = studentDAO;
    }

    public void setCourseAssignDAO(app.dao.intf.CourseAssignDAO courseAssignDAO)
    {
        this.courseAssignDAO = courseAssignDAO;
    }
    //end injection methods

    //example business method
    @Override
    @Transactional(rollbackFor={Exception.class})
    public void assignCourse(StudentKey studenKey, CourseKey courseKey)
    {
        courseAssignDAO.assignCourse(studentKey, courseKey);
    }

    //rest of the class here
}

The bean XML entries for the above might look like this: 上面的bean XML条目可能看起来像这样:

<bean id="baseDAO" class="app.dao.base.BaseDAO" abstract="true">
    <property name="dataSource" ref="dataSource" />
</bean>

<bean id="courseDAO" class="app.dao.impl.CourseDAO"
    parent="baseDAO">
</bean>

<bean id="studentDAO" class="app.dao.impl.StudentDAO"
    parent="baseDAO">
</bean>    

<bean id="courseAssignDAO" class="app.dao.impl.CourseAssignDAO"
    parent="baseDAO">
</bean>

<bean id="maintainStudent" class="app.service.impl.MaintainStudent">
    <property name="courseDAO" ref="courseDAO" />
    <property name="studentDAO" ref="courseAssignDAO" />
    <property name="courseAssignDAO" ref="studentDAO" />
</bean>

My question is, is it ok to store the injected beans in a HashMap instead of in individual interface variables? 我的问题是,将注入的bean存储在HashMap而不是在单个接口变量中可以吗? I ask this question because I am working on a porting project for an applicaion I support and I am trying to replace the application's EJB / JDBC transaction architecture with Spring. 我问这个问题是因为我正在为我支持的应用程序进行移植项目,并且试图用Spring替换应用程序的EJB / JDBC事务体系结构。 I am using Struts2 and Spring as the new architecture for the application. 我正在使用Struts2和Spring作为该应用程序的新体系结构。 I am using Spring in this project primarily for its Database Transaction capabilities. 我在这个项目中使用Spring主要是因为它具有数据库事务功能。 The existing application uses factory classes to instantiate everything, and I have come up with a way of converting the architecture to Dependency Injection using the factory classes by storing the beans in a HashMap . 现有的应用程序使用工厂类实例化所有内容,并且我想出了一种通过将bean存储在HashMap来使用工厂类将体系结构转换为依赖注入的方法。

Below is an example of the same business object using my HashMap method of storing the beans: 下面是使用我的HashMap方法存储bean的同一业务对象的示例:

package app.service.impl;

public class MaintainStudent extends app.base.BaseBusinessObj
    implements app.service.intf.MaintainStudent
{ 
    //injection methods here
    public void setCourseDAO(app.dao.intf.CourseDAO courseDAO)
    {
        this.springBeans.add(courseDAO.getClass().getName(), courseDAO);
    }

    public void setStudentDAO(app.dao.intf.StudentDAO studentDAO)
    {
        this.springBeans.add(studentDAO.getClass().getName(), studentDAO);
    }

    public void setCourseAssignDAO(app.dao.intf.CourseAssignDAO courseAssignDAO)
    {
        this.springBeans.add(courseAssignDAO.getClass().getName(), courseAssignDAO);
    }
    //end injection methods

    //example business method
    @Override
    @Transactional(rollbackFor={Exception.class})
    public void assignCourse(StudentKey studenKey, CourseKey courseKey)
    {
        app.dao.intf.CourseAssignDAO courseAssignDAO =
            app.dao.fact.CourseAssignDAOFactory.requestBean(this);

        courseAssignDAO.assignCourse(studentKey, courseKey);
    }

    //rest of the class here
}

BaseBusinessObj class: BaseBusinessObj类:

package app.base;    

public class BaseBusinessObj implements org.springframework.beans.factory.DisposableBean
{
    private Map<String, Object> springBeans = new HashMap<String, Object>();

    public void addBean(String className, Object bean)
    {
        this.springBeans.put(className, bean);
    }

    public Object getBean(String className)
    {
        return this.springBeans.get(className);
    }

    @Override
    public void destroy() throws Exception
    {
        this.springBeans.clear();
    }
}

Modified bean XML entries: 修改后的bean XML条目:

<bean id="baseBusinessObj" class="app.base.BaseBusinessObj" abstract="true">
</bean>

<bean id="baseDAO" class="app.dao.base.BaseDAO" abstract="true"
    parent="baseBusinessObj">
    <property name="dataSource" ref="dataSource" />
</bean>

<bean id="courseDAO" class="app.dao.fact.CourseDAOFactory"
    parent="baseDAO">
</bean>

<bean id="studentDAO" class="app.dao.fact.StudentDAOFactory"
    parent="baseDAO">
</bean>

<bean id="courseAssignDAO" class="app.dao.fact.CourseAssignDAOFactory"
    parent="baseDAO">
</bean>

<bean id="maintainStudent" class="app.service.fact.MaintainStudentFactory"
    parent="baseBusinessObj">
    <property name="courseDAO" ref="courseDAO" />
    <property name="studentDAO" ref="courseAssignDAO" />
    <property name="courseAssignDAO" ref="studentDAO" />
</bean>

Sample converted factory class that now serves up an injected bean instead of creating a new instance: 样本转换后的工厂类,现在提供注入的Bean而不是创建新实例:

package app.dao.fact;

public class CourseAssignDAOFactory extends app.dao.impl.CourseAssignDAO
{
    protected CourseAssignDAOFactory()
    {
        super();
    }

    //method formerly called "newInstance()"
    public static app.dao.intf.CourseAssignDAO requestBean(app.base.BaseBusinessObj requester)
    {
        app.dao.intf.CourseAssignDAO result;
        Object requestedBean = requester.getBean(CourseAssignDAOFactory.class.getName());
        result = (app.dao.intf.CourseAssignDAO)requestedBean;

        return result;
    }
}

What my architecture above does is provide the capability of writing code in a traditional non-DI style that allows the developer to explicitly "request" needed objects. 我上面的体系结构所做的工作是提供以传统的非DI样式编写代码的功能,该功能使开发人员可以明确地“请求”所需的对象。 This requesting is a similar activity to creating (instantiating) objects yourself. 该请求与您自己创建(实例化)对象的活动类似。 In my architecture, 'Requesting a Bean' has replaced 'Creating a New Instance' so that the Factory Class statements can be left in the code while making the code work with Dependency Injection architecture. 在我的体系结构中,“请求Bean”已替换为“创建新实例”,以便在使代码与Dependency Injection体系结构一起工作时,可以在代码中保留Factory Class语句。 I'm going to create a code generator that will automatically generate the required injector methods into every class that needs them. 我将创建一个代码生成器,它将自动在每个需要它们的类中生成所需的注入器方法。

I have tested the above architecture and it works. 我已经测试了上述架构,并且可以正常工作。 Transactions commit and rollback when expected. 事务在预期时提交并回滚。

I'm just wondering if this architecture I have created will still allow Spring to behave as designed. 我只是想知道我创建的这种体系结构是否仍将允许Spring像设计的那样运行。

With this architecture, will all of my beans still be able to be destroyed by Spring after a transaction is complete? 有了这种架构,事务完成后,我的所有bean仍能被Spring销毁吗? Or will there be hanging objects in memory because of the HashMap storage idea? 还是因为HashMap存储想法而在内存中挂起了对象?

Let me know what you think. 让我知道你的想法。 Thanks for taking the time to read this. 感谢您抽时间阅读。

You can replace the factories with Provider<Type> and have those injected instead: 您可以使用Provider<Type>替换工厂,并注入工厂:

public class CourseAssignDaoUser {

    @Autowired
    private Provider<CourseAssignDao> factory;

    void useDao() {
        CourseAssignDao dao = factory.get();
        // ...
    }
}

Or even inject the values directly: 甚至直接注入值:

public class CourseAssignDaoUser {

    @Autowired
    private CourseAssignDao dao;

    void useDao() {
        // ...
    }
}

If this is not possible for some reason you can use the Spring context instead of using the map: 如果由于某种原因无法实现,则可以使用Spring上下文而不是使用map:

public class CourseAssignDAOFactory {

    public static CourseAssignDAO requestBean(ApplicationContext context) {
        return context.getBean(CourseAssignDao.class);
    }
}

But using @Autowired with or without a Provider is definitely the best solution. 但是,使用@Autowired或不使用Provider绝对是最佳解决方案。

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

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