我们先新建一个空白的Maven工程。
然后我们将要编写的代码分为三部分:Service层、Dao层和UI。UI调用Service层,Service层再根据业务调用Dao层,Dao层再实际进行数据库的操控。
此时项目结构如下图所示:
下面将Java中的文件列出来。
dao包中包含IAccountDao接口以及包含IAccountDao接口的实现类的impl包
// IAccountDao.java
package com.yonmin.dao;
public interface IAccountDao {
// 模拟保存账户功能
void saveAccount();
}
// IAccountDao接口的实现类 AccountDaoImpl.java
package com.yonmin.dao.impl;
import com.yonmin.dao.IAccountDao;
public class AcccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("保存账户成功");
}
}
service包中包含IAccountService接口以及包含IAccountService接口的实现类的impl包
// IAccountService.java
package com.yonmin.service;
// 模拟保存账户信息服务
public interface IAccountService {
void saveAccount();
}
// IAccountService接口的实现类 AccountServiceImpl.java
package com.yonmin.service.impl;
import com.yonmin.dao.IAccountDao;
import com.yonmin.dao.impl.AcccountDaoImpl;
import com.yonmin.service.IAccountService;
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = (IAccountDao)new AcccountDaoImpl();
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
我们在ui包下新建Client.java用来发出保存账户的服务请求
// Client.java
package com.yonmin.ui;
import com.yonmin.service.IAccountService;
import com.yonmin.service.impl.AccountServiceImpl;
public class Client {
public static void main(String[] args) {
IAccountService accountService = (IAccountService) new AccountServiceImpl();
accountService.saveAccount();
}
}
在AccountServiceImpl.java
中有这么一行代码,使用了new
关键字创建了对象。
private IAccountDao accountDao = (IAccountDao)new AcccountDaoImpl();
在Client.java
中也有使用new
关键字创建对象地一行代码
IAccountService accountService = (IAccountService) new AccountServiceImpl();
当我们使用new关键字时,就加强了程序之间的耦合,IoC所要做的就是尽可能地降低程序之间的耦合,即尽可能地消除new关键字。
那我们应该怎么做呢?前辈们给出的答案是使用工厂模式和配置文件。
我们在resources目录下新建一个名为bean.properties
的配置文件,
新建一个和dao包同级的factory包,包下新建一个名为BeanFactory的用于创建Bean对象的工厂类。此时项目结构如下
Bean就是可以重用的组件,JavaBean就是用Java语言编写的可重用组件。
bean.properties中的内容就是键值对,读取配置文件后可以通过等号前面的键获取等号后面的值
# bean.properties
accountService=com.yonmin.service.impl.AccountServiceImpl
accountDao=com.yonmin.dao.impl.AccountDaoImpl
factory包中放置的就是BeanFactory,即创建我们的service和dao对象的工厂
// BeanFactory.java
package com.yonmin.factory;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class BeanFactory {
// 定义一个properties对象
private static Properties props;
// 定义一个Map,用于存放我们要创建的对象,我们将其称之为容器
private static Map<String,Object> beans;
// 使用静态代码块对Properties对象赋值
static {
try {
// 实例化对象
props = new Properties();
// 获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
// 实例化容器
beans = new HashMap<String,Object>();
// 取出配置文件中所有的key
Enumeration<Object> keys = props.keys();
// 遍历枚举
while (keys.hasMoreElements()){
// 取出每个key
String key = keys.nextElement().toString();
// 根据key获取value
String beanPath = props.getProperty(key);
// 反射创建对象
Object value = BeanFactory.getBean(beanPath);
// 把bean放到容器中
beans.put(key,value);
}
} catch (Exception e){
e.printStackTrace();
}
}
/**
* 根据bean的名称获取对象
* @param beanName
* @return
*/
public static Object getBean(String beanName) {
return beans.get(beanName);
}
}
上述代码将配置文件bean.properties
中的键和值所对应的对象存入到了名为beans的Map中,我们可以通过getBean
静态方法传入beanName获取Map中相应的bean对象。此时的Map就相当于Spring中的IoC容器
此时IAccountService接口不用变化,IAccountService接口的实现类AccountServiceImpl中要去掉new关键字。
通过BeanFactory类的静态方法getBean
,传入accountDao参数即可获取AccountDaoImpl的bean对象
// AccountServiceImpl.java
package com.yonmin.service.impl;
import com.yonmin.dao.IAccountDao;
import com.yonmin.factory.BeanFactory;
import com.yonmin.service.IAccountService;
public class AccountServiceImpl implements IAccountService {
// private IAccountDao accountDao = new AccountDaoImpl();
private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
通过BeanFactory类的静态方法getBean
,传入accountService参数即可获取AccountServiceImpl的bean对象
// Client.java
package com.yonmin.ui;
import com.yonmin.factory.BeanFactory;
import com.yonmin.service.IAccountService;
public class Client {
public static void main(String[] args) {
// AccountServiceImpl accountService = new AccountServiceImpl();
IAccountService accountService = (IAccountService) BeanFactory.getBean("accountService");
accountService.saveAccount();
}
}
此时我们将原来代码中的new关键字,但是可能你会说了,BeanFactory类中实例化对象操作new Properties()
又多了个new关键字,这就是之前说的尽可能消除new关键字,而不是让代码中不出现一个new关键字。
说了这么多,也到了解释IoC为何物的时候了:当我们不使用IoC时创建对象,我们的应用直接和资源联系,而且二者的联系消除不掉;而当我们使用IoC创建对象时,过程就发生了变化,我们的应用和资源断开了联系,而是找工厂要资源,由工厂与资源联系,并获得需要的对象,转到应用中,这样就消除了应用和资源的联系,这种思想就是IoC。因为实体类将其自主创建对象的权力交给了工厂创建,因此也叫做控制反转,带来的好处就是减少代码中的依赖关系,也即削减程序的耦合。