6

我们在现有项目中有许多 DAO(目前没有接口,但可以改变)。我们不是为每个 DAO 类连接一个 Spring 管理的 bean 并将它们注入服务层,而是有一个类似于这样的 DAO“工厂”:

public class DAOFactory {
private static DAOFactory daoFac;

static{
    daoFac = new DAOFactory();
}

private DAOFactory(){}

public static DAOFactory getInstance(){
    return daoFac;
}

public MyDAO1 getMyDAO1(){
    return new MyDAO1();
}

    public MyDAO2 getMyDAO2(){
    return new MyDAO2();
}
    ...

(注意 MyDAO1 和 MyDAO2 是具体的类)

这使我们可以轻松地在服务层中添加/调用 DAO 方法,而无需 1.) 将 DAO 接口作为属性添加到服务类 2.) 通过配置将 DAO 实现连接到服务方法中。(而且我们有时在一个服务类中使用多个 DAO)。

DAOFactory.getInstance().getMyDAO1().doSomething();

到目前为止,这种策略对我们很有效(我们不需要切换实现),但我想知道如果我们能够开始新的,是否有更好的方法?我将 DAO 视为 bean 的自动装配,但我仍然需要在每个服务类中创建属性来表示正在使用的那些 DAO。在一个大型项目中,无论如何我都不愿意开始自动装配 bean——我们需要为所有开发人员提供可见性。

感觉就像我在 a.) 与实现紧密耦合,但代码/配置开销较少和 b.) 与接口松散耦合,但需要大量代码/配置开销之间来回切换。

我有没有更好的方法?中间有什么?欢迎发表意见。

4

5 回答 5

5

我将把所有的 DAO 作为 Spring 管理的组件,并将它们注入到服务中以实现松散耦合。为什么你认为自动装配 bean 在大项目中不好。?

只需使用 @Component 注释每个 DAO 类并替换MyDao mydao = factory.getmyDao()

@Autowired MyDao myDao;

我没有看到太多的编码/配置开销。

于 2012-12-06T18:33:07.620 回答
3

到目前为止,我对我的项目采取了几种不同的方法,但还没有真正确定什么是“最好的”。可能没有“最佳”,但可能有“最适合您的需求”。

首先,我选择了一个基础服务类。

public abstract BaseService {
    @Autowired FooDAO fooDao;
    @Autowired BarDAO barDao;
    . . .
    . . .
    protected getFooDAO() {
        return this.fooDao;
    }
}

然后在我的服务类中,我可以简单地写

Foo foo = getFooDAO().uniqueById(id);

这很有效,它使我的服务从 dao 实例变量的所有自动装配和访问器类中保持整洁。问题是,我现在在我的每个服务中都有这个基类的副本,坦率地说,嗯,没什么大不了的。但它也有点产生代码味道,因为它没有使用组合而不是继承,从本质上讲,DI 的一个原因是鼓励组合。

一位同事推荐了一个像你这样的工厂,并称之为 ServiceProvider。我们将其自动连接到我们的服务中。

@Component
public class ServiceProvider {
    @Autowired FooDAO fooDao;
    public FooDAO getFooDAO() {
        return this.fooDao;
    }
    . . .
    // yadda yadda
}

然后我们有你所拥有的东西:

Foo foo = getServiceProvider().getFooDAO().uniqueById(id);

这非常丑陋,而且确实不适合清晰。因此,我们只使用了提供者的实例,并将其命名为类似 sp 的简短而甜美的名称。然后我们得到

Foo foo = this.sp.getFooDAO().uniqueById(id);

再一次,它有效。这可能是一个更好的设计。而且我们只在一个地方自动连接 DAO,而不是到每个服务中,尽管这两种方式都不是什么大问题。但这让我感觉更好,即使我感觉更好不是项目要求(但你不认为它应该是吗?)

我一直在想我们会把两者结合起来。我们将更改 BaseService 以自动装配 ServiceProvider,然后包装丑陋的调用。

public abstract BaseService {
    @Autowired ServiceProvider serviceProvider;

    protected getFooDAO() {
        return this.serviceProvider.getFooDAO();
    }

    protected getBarDAO() {
        return this.serviceProvider.getBarDAO();
    }
}

在我的服务中提供更好的速记,不需要我将每个 DAO 自动连接到每个服务中,在我看来,这会变得很笨重,但也没有每个服务中所有这些引用的副本,你知道,是一个完全荒谬的担忧。

我留下的问题是在调试器中单步执行代码。进入和退出每个 getWhateverDAO() 调用是乏味的,通过 getServiceProvider() 添加一个可能的步骤也无济于事。

但这就是我处理这个问题的地方。坦率地说,我认为我花了很多时间思考这个问题,因为这是避免我们的应用程序带来的所有真正难题的好方法。

于 2012-12-06T19:51:53.567 回答
1

好问题。

我觉得你开始使用是很可惜的DAOFactory。Spring 是一个超级灵活的工厂,所以我真的不明白你为什么需要另一个。Spring 中的自动装配有很多优点,并且不需要接口,因此您可以轻松切换到使用 Spring 访问 DAO。恕我直言,它不会减少但会提高其他开发人员的可见性。

此外,如果您正在考虑重构 DAO 层,请查看 Google 代码中的 GenericDAO:http ://code.google.com/p/hibernate-generic-dao/

我对这个图书馆有很好的体验。它可以节省您的时间。你实际上并不需要很多 DAO。你只需要一个 DAO。您显然可以从 google 代码包装通用 DAO 并添加您的应用程序特定的术语和功能。但不要在那里添加实体特定的代码。实体特定代码应该在服务层。如果您使用 Hibernate 标准 API,则不再有脆弱的 HQL,也不会与 hibernate 耦合。这个库同时支持 Hibernate 和 JPA,它的 API 非常简单和强大。

于 2012-12-06T18:37:46.993 回答
0

如果您在 1 项服务中使用太多 DAO,您应该考虑将 1 项服务拆分为更低级别(细粒度)的服务

于 2012-12-06T18:35:53.940 回答
0

如果您不想注释 DAO 类或有配置注释(例如@Value污染它们),我看到两个选项:

@Configuration1.用DAO创建@Bean一个

@Configuration
public class DaoConfiguration {

    @Value("${db.name}")
    private String dbName;

    @Value("${foo.table}")
    private String fooTable;

    @Value("${bar.table}")
    private String barTable;

    @Bean
    private FooDao fooDao() {
        return new FooDao(dbName, fooTable);
    }

    @Bean
    private BarDao barDao() {
        return new BarDao(dbName, barTable);
    }
}

@Autowired然后为您需要的 DAO创建一个字段:

@Autowired
private FooDao fooDao;

2.创建DAO工厂@Component

如果您需要在 DAO 被销毁时进行一些清理,这很有用。

@Component
public class DaoFactory {

    @Value("${db.name}")
    private String dbName;

    @Value("${foo.table}")
    private String fooTable;

    @Value("${bar.table}")
    private String barTable;

    private FooDao fooDao;
    private BarDao barDao;

    @PostConstruct
    public void init() {
        fooDao = new FooDao(dbName, fooTable);
        barDao = new BarDao(dbName, barTable);
    }

    @PreDestroy
    public void destroy() {
        try {
            fooDao.close();
        } catch (Exception e) {
            log.error("Failed to clean up FooDao", e);
        }
        try {
            barDao.close();
        } catch (Exception e) {
            log.error("Failed to clean up BarDao", e);
        }
    }

    public FooDao fooDao() {
        return fooDao;
    }

    public BarDao barDao() {
        return barDao;
    }
}

@Autowired然后在需要 DAO 的类中为工厂创建一个字段:

@Autowired
private DaoFactory daoFactory;

并将其用作:

daoFactory.barDao().findAll();
于 2017-07-17T10:59:48.867 回答