在开始阐述DAO与Service层如何实现解耦之前,我先提一个站在学习者角度的问题,为什么在MVC的三层开发架构中会非常推崇接口编程,那么根据已有的解释,接口编程的好处是:可以帮助层与层之间的解耦,让每个部分独立出来,互不影响,更加的利于团队开发合作和提高复用性与扩展性。那么,在数据访问层(DAO)和业务层(SERVICE)之间的解耦是如何做到的呢?下面请先看一张框架简图:
不难发现的是,UserService是通过接口UserDao来间接操作UserDaoImpl访问Domain对象User中的数据的。有些编程经验的童鞋都应该明白,要想拿到具体的实现,那么这种操作(先不管红色部分的UserDaoFactory工厂类),在访问User时就必定要在UserService中有这样的语句:UserDao userDao = new UserDaoImpl(); 那么,问题来了, UserDaoImpl实现类在这里出现并将导致在后期拓展时(比如UserDao的具体实现改换为Hibernate或者其它技术)就无法真正的做到解耦,因为你必须在你的源代码中指定具体的实现类。如果这里,你还没有明白,那么请看以下代码:
首先,我们来构造一个Domain域对象User,此User在此其实并没有多大意义只是为了展示Domain对象的结构:
package com.jasber.jdbc.test1;
import java.util.Date;
/**
* This is Bean that corresponding int Mysql database table tb_test
*
* @author Jasber-Yon
*
*/
public class User {
private Integer id;
private String name;
private Date birthday;
private Double salary;
public User() {
}
public User(Integer id, String name, Date birthday, Double salary) {
super();
this.id = id;
this.name = name;
this.birthday = birthday;
this.salary = salary;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
}
然后,定义一个数据访问接口UserDao:
package com.jasber.jdbc.test1;
/**
*
* @author Jasber-Yon
*
*/
public interface UserDao {
public boolean getUserInfoById(User user);
}
现在,就来实现这个UserDao数据访问接口,实现类UserDaoImpl(在这里就不做真正的实现了,只为说明问题):
package com.jasber.jdbc.test1;
public class UserDaoImpl implements UserDao {
@Override
public boolean getUserInfoById(User user) {
//.....代码省略
System.out.println("哈哈,我被调用了~");
return false;
}
}
那么,现在,我们可以写一个UserService来模拟一下调用了:
package com.jasber.jdbc.test1;
/**
*
* @author Jasber-Yon
*
*/
public class UserService {
public static void main(String[] args) {
User user = new User();
UserDao userDao = new UserDaoImpl();
userDao.getUserInfoById(user);
}
}
现在,就会发现,UserDaoImpl 在UserService中被“点名”了,这样干,显然是不合理的,没有做到真的解耦,我们的目的是,要让
UserService仅仅只需要通过数据访问接口UserDao就做到需要的操作。那么,应该如何来做呢?
一、提供一个工厂类,让他去对具体的实现类进行实例化,并返回UserDao的实现类的实例的引用。
二、这个数据访问接口的实现类在哪儿呢?通过配置文件(XML/Properties,我偷个懒就使用properties文件了)来配置,让工 厂类去读取(或者专用的操作类去读取)。
那么,请看这个工厂类UserDaoFactory的实现:
package com.jasber.jdbc.test1;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class UserDaoFactory {
private static UserDao userDao = null;// 注意此句必须放在实例化工厂类的语句之前,否者会在运行时被置为null
private static UserDaoFactory userDaoFactory = new UserDaoFactory();
private UserDaoFactory() {
Properties properties = new Properties();
InputStream inputStream = UserDaoFactory.class.getClassLoader()
.getResourceAsStream("daoConfig.properties");
try {
properties.load(inputStream);
String userDaoImpl = properties.getProperty("userDaoImpl");
userDao = (UserDao) Class.forName(userDaoImpl).newInstance();
} catch (Throwable e) {
throw new ExceptionInInitializerError(e);// 此问题非常严重
}finally{
try {
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static UserDaoFactory getInstance() {
return userDaoFactory;
}
public UserDao getUserDao() {
return userDao;
}
}
现在再来看看,我们是怎样在UserService中来操作数据访问对象(DAO)的:
package com.jasber.jdbc.test1;
/**
*
* @author Jasber-Yon
*
*/
public class UserService {
public static void main(String[] args) {
/*User user = new User();
UserDao userDao = new UserDaoImpl();
userDao.getUserInfoById(user);*/
User user = new User();
UserDao userDao = UserDaoFactory.getInstance().getUserDao();
userDao.getUserInfoById(user);
}
}
与注释部分对比,不难发现,已经没有了UserDaoImpl的踪影。完全是在操作接口了。是不是就已经解耦了呢?
想必,到这儿,已经明白是怎么回事了。下面在说一说,我在程序中使用到的 daoConfig.properties 文件(我是偷懒了啊,当然这样也是为了简化代码保证阐述问题的单纯性,就没有使用XML来说事),因为我们使用到了类加载器,该配置文件必须要放置在ClassPath路径下(当然位置是随意的,ClassPath路径就是指运行程序时,会被JVM读取加载的目录,也就是bin目录下,Java工程里,使用eclipse的就直接放在src目录下即可,编译时会被自动带过去的),不然就会无法找到该文件而出现空指针异常。
以上就是本篇博客。~博主的肚子好饿~